/*
 *===========================================================================
 *                    ipnet_config_add_inet_addr
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC int
ipnet_config_add_inet_addr(Ip_fd fd, char *ifname, char *option)
{
    char *inet_addr;
    char *prefix_len;
    char *argv[] = {
        "ifconfig",
        "-silent",
        IP_NULL,
        "inet",
        "add",
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL
    };
    int argc = 5;
    char inet_addr_str[IP_INET_ADDRSTRLEN];
    char inet_prefix_len_str[8];

    argv[2] = ifname;

    inet_addr = ipcom_strtok_r(option, " \t/", &option);
    if (inet_addr == IP_NULL)
    {
        IPCOM_LOG0(ERR, "inet address is missing or format is invalid");
        return -IP_ERRNO_EINVAL;
    }

    if (ipcom_strcmp(inet_addr, "dhcp") == 0)
    {
        struct Ip_ifreq  ifreq;

    use_dhcp:
        ipcom_strcpy(ifreq.ifr_name, ifname);
        ifreq.ifr_ifru.ifru_opt = 1;

		if (ipcom_socketioctl(fd, IP_SIOCXSDHCPRUNNING, &ifreq) < 0)
        {
            IPCOM_LOG1(ERR, "Failed to enable DHCP on %s", ifname);
            return ipcom_errno;
        }
        return 0;
    }

#ifdef IPNET_USE_RARP
    if (ipcom_strcmp(inet_addr, "rarp") == 0)
    {
        struct Ip_ethreq  ethreq;

        ipcom_strcpy(ethreq.ethr_name, ifname);
        ethreq.ethru.rarp = -1;

		if (ipcom_socketioctl(fd, IP_SIOCXETHSRARP, &ethreq) < 0)
        {
            IPCOM_LOG1(ERR, "Failed to enable RARPP on %s", ifname);
            return ipcom_errno;
        }
        return 0;
    }
#endif /* IPNET_USE_RARP */

    if (ipcom_strcmp(inet_addr, "driver") == 0)
    {
        /* Get the IPv4 address to use from the driver */
        struct Ip_ethreq ethreq;

        ipcom_strcpy(ethreq.ethr_name, ifname);
        if (ipcom_socketioctl(fd, IP_SIOCXETHGINET, &ethreq) < 0)
        {
            IPCOM_LOG1(ERR, "Failed to read the IPv4 address from the driver for %s", ifname);
            return ipcom_errno;
        }
        if (ethreq.ethru.inet.addr.s_addr == 0xffffffff)
            goto use_dhcp;
        inet_addr = inet_addr_str;
        (void) ipcom_inet_ntop(IP_AF_INET, &ethreq.ethru.inet.addr,
            inet_addr_str, sizeof(inet_addr_str));

        prefix_len = inet_prefix_len_str;
        ipcom_snprintf(inet_prefix_len_str, sizeof(inet_prefix_len_str), "%d",
            ipcom_mask_to_prefixlen(&ethreq.ethru.inet.mask, 32));
    }
    else
    {
        prefix_len = ipcom_strtok_r(option, " \t/", &option);
        if (prefix_len == IP_NULL)
        {
            IPCOM_LOG0(ERR, "prefix len is missing or format is invalid");
            return -IP_ERRNO_EINVAL;
        }
    }

    argv[argc++] = inet_addr;
    argv[argc++] = "prefixlen";
    argv[argc++] = prefix_len;

    return ipnet_config_cmd_ifconfig(argc, argv);
}
/*
 *===========================================================================
 *                    ipsecctrl_selectorTable_print
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
static void
ipsecctrl_selectorTable_print(Argvars *vars)
{
    Ipipsec_ctrl_selector   sel;
#ifdef IPCOM_USE_INET6
    char str[IP_INET6_ADDRSTRLEN];
#else
    char str[16];
#endif

    ipcom_printf(IP_LF"***** selectorTable ::"IP_LF);

    sel.Index = 0;  /* get first selector. */

    /* Get and print the Selectors in MIB format. */
    for (;;)
    {
        /* Get first/next Selector */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_SELECTOR, &sel) < 0)
        {
            ipcom_printf("ipsecctrl: ipcom_socketioctl(SELECTOR) failed (%s)"IP_LF,
                         ipcom_strerror(ipcom_errno));
            return;
        }

        if (sel.Index == 0)
            break;

        /* Print selector index */
        ipcom_printf(IP_LF"Selector Entry #%ld:"IP_LF
                     "selectorIndex = %ld"IP_LF,
                     sel.Index,
                     sel.Index);

        ipcom_printf("selectorLocalId = %s"IP_LF
                     "selectorLocalIdType = %s"IP_LF,
                     ipcom_inet_ntop(sel.domain, &sel.LocalId, str, sizeof(str)),
                     sel.domain == IP_AF_INET ? "idIpv4Addr" : "idIpv6Addr");


        ipcom_printf("selectorRemoteId = %s"IP_LF
                     "selectorRemoteIdType = %s"IP_LF,
                     ipcom_inet_ntop(sel.domain, &sel.RemoteId, str, sizeof(str)),
                     sel.domain == IP_AF_INET ? "idIpv4Addr" : "idIpv6Addr");

        ipcom_printf("selectorProtocol = %s"IP_LF,
                     ipcom_ipproto_name(sel.Protocol));

        ipcom_printf("selectorLocalPort = %d"IP_LF,
                     ip_ntohs(sel.LocalPort));
        ipcom_printf("selectorRemotePort = %d"IP_LF,
                     ip_ntohs(sel.RemotePort));
    }
}
/*
 *===========================================================================
 *                      ipnet_vrrp_add_addr
 *===========================================================================
 * Description: Adds a virtual router address.
 * Parameters:  netif - The network interface the VRIP will be assigned to.
 *              vrid - The VRID the address will be assigned to.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_vrrp_add_addr(Ipnet_netif *netif, Ip_u8 vrid, struct Ip_in_addr addr)
{
    Ipnet_vrrp_addr_t *addr_entry;

    if (ipnet->vrrp_addrs == IP_NULL)
    {
        ipnet->vrrp_addrs = ipcom_hash_new((Ipcom_hash_obj_func) ipnet_vrrp_obj_hash,
                                           (Ipcom_hash_key_func) ipnet_vrrp_obj_hash,
                                           (Ipcom_hash_cmp_func) ipnet_vrrp_hash_cmp);
        if (ipnet->vrrp_addrs == IP_NULL)
            return -IP_ERRNO_ENOMEM;
    }

    addr_entry = ipnet_vrrp_get_addr_entry(netif, vrid);
    if (addr_entry == IP_NULL)
    {
        addr_entry = ipcom_malloc(sizeof(Ipnet_vrrp_addr_t));
        if (addr_entry == IP_NULL)
            return -IP_ERRNO_ENOMEM;
        IPNET_IF_LOCK(netif);
        addr_entry->netif     = netif;
        addr_entry->vrid      = vrid;
        addr_entry->num_addrs = 1;
        addr_entry->addrs[0]  = addr;
    }
    else
    {
        Ipnet_vrrp_addr_t *a;
        int                i;

        i = ipnet_vrrp_addr_index(addr_entry, addr);
        if (i >= 0)
            return -IP_ERRNO_EEXIST;

        (void)ipcom_hash_remove(ipnet->vrrp_addrs, addr_entry);
        a = ipcom_realloc(addr_entry,
                          sizeof(Ipnet_vrrp_addr_t) + addr_entry->num_addrs * sizeof(Ipnet_vrrp_addr_t));
        if (a == IP_NULL)
            return -IP_ERRNO_ENOMEM;
        a->addrs[a->num_addrs++] = addr;
        addr_entry = a;
    }

    if (ipcom_hash_add(ipnet->vrrp_addrs, addr_entry) != IPCOM_SUCCESS)
    {
        ipcom_free(addr_entry);
        return -IP_ERRNO_ENOMEM;
    }

    IPCOM_LOG2(INFO, "VRRP: added address %s on %s",
               ipcom_inet_ntop(IP_AF_INET, &addr, ipnet->log_buf, sizeof(ipnet->log_buf)),
               netif->ipcom.name);

    return 0;
}
/*
 *===========================================================================
 *                    ipnet_debug_sock_to_str
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_GLOBAL char *
ipnet_debug_sock_to_str(Ipnet_socket *sock, char *str, Ip_size_t str_len)
{
#ifdef IPNET_DEBUG
#ifdef IPCOM_USE_INET6
    char src_str[IP_INET6_ADDRSTRLEN];
    char dst_str[IP_INET6_ADDRSTRLEN];
#else
    char src_str[IP_INET_ADDRSTRLEN];
    char dst_str[IP_INET_ADDRSTRLEN];
#endif
#endif /* IPNET_DEBUG*/

    switch (sock->ipcom.domain)
    {
#ifdef IPCOM_USE_INET
    case IP_AF_INET:
        (void)ipcom_inet_ntop(IP_AF_INET, &sock->ip4->saddr_n, src_str, sizeof(src_str));
        (void)ipcom_inet_ntop(IP_AF_INET, &sock->ip4->daddr_n, dst_str, sizeof(dst_str));
        break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
    case IP_AF_INET6:
        (void)ipcom_inet_ntop(IP_AF_INET6, &sock->ip6->saddr, src_str, sizeof(src_str));
        (void)ipcom_inet_ntop(IP_AF_INET6, &sock->ip6->daddr, dst_str, sizeof(dst_str));
        break;
#endif /* IPCOM_USE_INET6 */
    default:
        src_str[0] = '\0';
        dst_str[0] = '\0';
    }
    ipcom_snprintf(str, str_len, "fd:%d ref:%d, domain:%d proto:%d %s:%d %s:%d",
                   sock->ipcom.fd,
                   ipcom_atomic_get(&sock->ref_count),
                   sock->ipcom.domain,
                   sock->proto,
                   src_str, sock->sport,
                   dst_str, sock->dport);
    return str;
}
/*
 *===========================================================================
 *                    ipnet_config_add_gateway
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC int
ipnet_config_add_gateway(Ip_fd fd, char *ifname, char *option)
{
    char gw_str[IP_INET_ADDRSTRLEN];
    char *gw;

    gw = ipcom_strtok_r(option, " \t", &option);
    if (gw == IP_NULL)
    {
        IPCOM_LOG1(ERR,
                   "Wrong gateway format specified for interface %s, must be 'gateway <driver|gw_address>",
                   ifname);
        IP_PANIC();
        return -IP_ERRNO_EINVAL;
    }

    if (ipcom_strcmp(gw, "driver") == 0)
    {
        /* Get the IPv4 gateway address to use from the driver */
        struct Ip_ethreq ethreq;

        ipcom_strcpy(ethreq.ethr_name, ifname);
        if (ipcom_socketioctl(fd, IP_SIOCXETHGINET, &ethreq) < 0)
        {
            IPCOM_LOG1(ERR, "Failed to read the IPv4 gateway address "
                       "from the driver for %s", ifname);
            return ipcom_errno;
        }
        if (ethreq.ethru.inet.gateway.s_addr == 0xffffffff)
            return 0; /* using dhcp, ignore gateway */
        if (ethreq.ethru.inet.gateway.s_addr == 0)
        {
            IPCOM_LOG1(NOTICE, "No IPv4 gateway address set in driver for %s", ifname);
            return 0;
        }

        (void) ipcom_inet_ntop(IP_AF_INET, &ethreq.ethru.inet.gateway,
                               gw_str, sizeof(gw_str));
        gw = gw_str;
    }

    return ipnet_config_add_route(IP_AF_INET, "0.0.0.0", 0, gw);
}
/*
 *===========================================================================
 *                      ipnet_vrrp_del_addr
 *===========================================================================
 * Description: Deletes a virtual router address.
 * Parameters:  netif - The network interface the VRIP was assigned to.
 *              vrid - The VRID the address was assigned to.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_vrrp_del_addr(Ipnet_netif *netif, Ip_u8 vrid, struct Ip_in_addr addr)
{
    Ipnet_vrrp_addr_t *addr_entry;
    int                i;

    ip_assert(ipnet->vrrp_addrs != IP_NULL);

    addr_entry = ipnet_vrrp_get_addr_entry(netif, vrid);
    if (addr_entry == IP_NULL)
        return -IP_ERRNO_ESRCH;

    i = ipnet_vrrp_addr_index(addr_entry, addr);
    if (i < 0)
        return -IP_ERRNO_ESRCH;

    IPCOM_LOG2(INFO, "VRRP: deleted address %s on %s",
               ipcom_inet_ntop(IP_AF_INET, &addr, ipnet->log_buf, sizeof(ipnet->log_buf)),
               netif->ipcom.name);

    if (--addr_entry->num_addrs > 0)
    {
        for (;i < addr_entry->num_addrs; i++)
            addr_entry->addrs[i] = addr_entry->addrs[i + 1];
    }
    else
    {
        (void)ipcom_hash_remove(ipnet->vrrp_addrs, addr_entry);
        ipcom_free(addr_entry);

        if (ipnet->vrrp_addrs->elem == 0)
        {
            ipcom_hash_delete(ipnet->vrrp_addrs);
            ipnet->vrrp_addrs = IP_NULL;
        }
    }

    return 0;
}
/*
 *===========================================================================
 *                    ipl2tps
 *===========================================================================
 * Description: 
 * Parameters:  
 * Returns:     
 *
 */
IP_STATIC
IPCOM_PROCESS(ipl2tps)
{
    Ip_fd                   ipd_fd = IP_INVALID_SOCKET;
    Ip_bool                 ipd_init = 0;
#ifdef IPPPP_USE_PPPL2TP
    Ip_fd                   ipl2tp_ppp_ipc_recv_fd = IP_INVALID_SOCKET;
#endif

    ipcom_proc_init();

#ifdef IPPPP_USE_PPPL2TP
    if ((ipl2tp_ppp_ipc_recv_fd = ipppp_pppl2tp_pkt_que_open()) == IP_INVALID_SOCKET)
    {
        ipcom_printf("Cannot allocate data packet output socket, errno: %d"IP_LF, ipcom_errno);
        goto leave;
    }
#endif

    /* Get socket */
    if ((udp_fd = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, IP_IPPROTO_UDP)) < 0)
    {
        ipcom_printf("Cannot allocate socket, errno: %d"IP_LF,
                     ipcom_errno);
        goto leave;
    }
    else
    {
        union Ip_sockaddr_union uaddr;

        /* Bind to L2TP port */
        ipcom_memset(&uaddr, 0, sizeof(uaddr));

        uaddr.sin.sin_family        = IP_AF_INET;
        uaddr.sin.sin_addr.s_addr   = IP_INADDR_ANY;
        uaddr.sin.sin_port          = ip_htons(IPL2TP_PORT_NUMBER);
        uaddr.sin.sin_len           = sizeof(struct Ip_sockaddr_in);
        if (ipcom_bind(udp_fd, &uaddr.sa, sizeof(struct Ip_sockaddr_in)) < 0)
        {
            ipcom_printf("Cannot bind socket, errno: %d"IP_LF,
                         ipcom_errno);
            ipcom_socketclose(udp_fd);
            goto leave;
        }
    }

    /* Get socket */
    if ((ip_fd = ipcom_socket(IP_AF_INET, IP_SOCK_RAW, IP_IPPROTO_L2TP)) < 0)
    {
        ipcom_printf("Cannot allocate socket, errno: %d"IP_LF,
                     ipcom_errno);
        ipcom_socketclose(udp_fd);
        goto leave;
    }


    /* Init IPD */
    if (ipd_init == 0 &&
        ipcom_ipd_init("ipl2tp", IPCOM_SUCCESS, &ipd_fd) != IPCOM_SUCCESS)
    {
        ipcom_printf("Cannot initialize ipd: %s"IP_LF,
                     ipcom_strerror(ipcom_errno));
        goto leave;
    }

    ipd_init = 1;

    /* Enter read loop (never return) */
    for (;;)
    {
        Ip_fd_set                   fds;            
        Ipl2tp_example_fd_entry_t   *fd;
        int                         fd_max;
        struct Ip_timeval           tv;
        int                         ret;

        IP_FD_ZERO(&fds);

        IP_FD_SET(udp_fd, &fds);
        IP_FD_SET(ip_fd, &fds);
        IP_FD_SET(ipd_fd, &fds);
        fd_max = IP_MAX(udp_fd, ip_fd);
        fd_max = IP_MAX(fd_max, ipd_fd);

#ifdef IPPPP_USE_PPPL2TP
        IP_FD_SET(ipl2tp_ppp_ipc_recv_fd, &fds); 
        fd_max = IP_MAX(fd_max, ipl2tp_ppp_ipc_recv_fd);
#endif

        fd = IPCOM_LIST_FIRST(&ipl2tp_example_fds);
        while (fd != IP_NULL)
        {
            Ipl2tp_example_fd_entry_t   *nfd = IPCOM_LIST_NEXT(&fd->next);
            if (fd->data != IP_NULL)
            {
                IP_FD_SET(fd->fd, &fds);
                fd_max = IP_MAX(fd_max, fd->fd);
            }
            else
            {
                ipcom_list_remove(&fd->next);
                ipcom_socketclose(fd->fd);
                ipcom_free(fd);
            }

            fd = nfd;
        }

        tv.tv_sec   = 0;
        tv.tv_usec  = 10000;
        if ((ret = ipcom_socketselect(fd_max + 1, &fds, IP_NULL, IP_NULL, &tv)) > 0)
        {
            union Ip_sockaddr_union uaddr;
            int                     length;
            Ip_u8                   *buffer;

            if (IP_FD_ISSET(ipd_fd, &fds))
            {
                int event;

                event = ipcom_ipd_input(ipd_fd);
                if (event == IPCOM_IPD_EVENT_RECONFIGURE)
                    goto leave;
            }


#ifdef IPPPP_USE_PPPL2TP
            if (IP_FD_ISSET(ipl2tp_ppp_ipc_recv_fd, &fds))
            {
                Ipppp_pppl2tp_pkt_que_entry que_entry;
                Ipcom_netif *netif;
                Ipcom_pkt *pkt;
                char addrstr[IP_INET_ADDRSTRLEN];

                /* Read message and feed it to L2TP main input */
                if (ipppp_pppl2tp_pkt_que_recv(&que_entry, &uaddr) == IPCOM_SUCCESS)
                {
                    if ((que_entry.netif == IP_NULL) ||
                        (que_entry.pkt == IP_NULL))
                    {
                        ipcom_printf("ipl2tps: L2TPv2 pkt from %s:%d contains null ptr(s)."IP_LF,
                            ipcom_inet_ntop(IP_AF_INET, &uaddr.sin.sin_addr, addrstr, sizeof(addrstr)),
                            ipcom_ntohs(uaddr.sin.sin_port));
                    }
                    else
                    {
                        pkt = que_entry.pkt;
                        netif = que_entry.netif;
                        ipppp_pppl2tp_output(netif, pkt);
                    }
                }
                else
                    ipcom_printf("ipl2tps: PPP pkt recv failed."IP_LF);
            }
#endif /* IPPPP_USE_PPPL2TP */

            if (IP_FD_ISSET(udp_fd, &fds))
            {
                /* Read message and feed it to L2TP main input */
                if ((length = ipl2tp_l2tp_read(udp_fd, &buffer, &uaddr)) > 0)
                {
                    ipl2tp_api_l2tp_input(buffer, 
                                          buffer + HEADER_SPACE,
                                          (Ip_u16)length, 
                                          &uaddr, 
                                          IPL2TP_ATTR_TRANSPORT_UDP);

                    /* Free buffer if not in PPP output queue */
                    if (ipl2tp_buffer_free)
                    {
                        ipl2tp_cache_free(buffer);
                    }
                }
            }

            if (IP_FD_ISSET(ip_fd, &fds))
            {
                /* Read message and feed it to L2TP main input */
                if ((length = ipl2tp_l2tp_read(ip_fd, &buffer, &uaddr)) > 0)
                {
                    /* Remove the IP header before invoking */
                    ipl2tp_api_l2tp_input(buffer, 
                                          buffer + HEADER_SPACE + 20,
                                          (Ip_u16)(length - 20), 
                                          &uaddr, 
                                          IPL2TP_ATTR_TRANSPORT_IP);

                    /* Free buffer if not in PPP output queue */
                    if (ipl2tp_buffer_free)
                    {
                        ipl2tp_cache_free(buffer);
                    }
                }
            }

            fd = IPCOM_LIST_FIRST(&ipl2tp_example_fds);
            while (fd != IP_NULL)
            {
                Ipl2tp_example_fd_entry_t   *nfd = IPCOM_LIST_NEXT(&fd->next);
                if (fd->data!= IP_NULL && IP_FD_ISSET(fd->fd, &fds))
                {
                    /* Read message and feed it to L2TP main input */
                    if ((length = ipl2tp_l2tp_read(fd->fd, &buffer, &uaddr)) > 0)
                    {
                        if (fd->data != IP_NULL)
                        {
                            (*fd->data) (fd,
                                         buffer, 
                                         buffer + HEADER_SPACE, 
                                         length);
                        }

                        /* Free buffer if not in PPP output queue */
                        if (ipl2tp_buffer_free)
                        {
                            ipl2tp_cache_free(buffer);
                        }
                    }
                }

                fd = nfd;
            }
        }
        else if (ret < 0)
        {
            ipcom_printf("select error"IP_LF);
        }        
    }

leave:
    /* Have to call ipd init if not done yet */
    if (ipd_init == 0 &&
        ipcom_ipd_init("ipl2tp", IPCOM_ERR_FAILED, &ipd_fd) == IPCOM_SUCCESS)
    {
        ipd_init = 1;
    }
    if (ipd_init != 0)
    {
        (void)ipcom_ipd_exit("ipl2tp", ipd_fd);
    }

    ipcom_proc_exit();
}
/*
 *===========================================================================
 *                         ipcom_cmd_ttcp
 *===========================================================================
 */
int
ipcom_cmd_ttcp(int argc, char **argv)
{
    /* Uninitialized variables. */
    union Ip_sockaddr_union     addrme;
    char                        *host = IP_NULL;	/* ptr to name of host */
    int                         c;
    Ip_u32                      msec0, msec1, msecd, bpermsec, kbpersec;
    Ip_fd                       sockfd = -1;
    char                        *buf;		/* ptr to dynamic buffer */
    char                        *orgbuf = 0;     /* the buffer to free. */
    struct Ipcom_cmd_ttcp_data  context;

    /* misc initialized variables. */
#ifdef IPCOM_USE_TCP
    union Ip_sockaddr_union frominet;
    Ip_socklen_t           fromlen;
    int                   one = 1; /* for 4.3 BSD style setsockopt() */
    Ip_fd                  oldfd;	  /* fd of network socket */
#endif
    Ip_u32    nbytes = 0;		  /* bytes on net */
    int    trans = 1;	          /* 0=receive, !0=transmit mode. Default transmit! */

    /* Configuration variables. */
#if defined(IP_SO_X_VR)
    int    vr = 0;
#endif
    int    buflen = 8 * 1024;	/* length of buffer */
    int    nbuf   = 2 * 1024;	/* number of buffers to send in sinkmode */
    int    bufoffset = 0;	/* align buffer to this */
    int    bufalign = 16;	/* modulo this */
    int    options = 0;		/* socket options */
    unsigned short  port = DEFAULT_PORT;	/* TCP port number */
    unsigned short  meport = 0;       	/* local port, default 0. */
    int    sinkmode = 1;		/* 0=normal I/O, !0=sink/source mode */
    int    verbose = 0;		/* 0=print basic info, 1=print cpu rate, proc resource usage. */
    int    sockbufsize = -1;	/* Default socket buffer size to use. */
#ifdef IP_TCP_NODELAY
    int    nodelay = 0;		/* set TCP_NODELAY socket option. */
#endif
    int    enable = 1;
#ifdef TCP_OSERFC2385
    char   *md5opt = IP_NULL;
#endif
#ifdef IP_SO_REUSEPORT
    int     reuse_port = 0;
#endif
#ifdef IPCOM_TTCP_REENTRANT
    union Ip_sockaddr_union  addrhim;
#endif
    int     sock_error;
    int     mev = 0;                /* Use event logging */
    int     nend = 5;               /* Number of UDP end packets */
#ifdef IPCOM_CMD_TTCP_USE_ZEROCOPY_API
    int     zerocopy = 0;
#endif
    int     cpu = -1;
#ifdef IPSCTP
    Ip_bool sctp_type_conflict = IP_FALSE;
#endif

    /* Initialize some static data, default configuration. */
#ifdef IPCOM_USE_TCP
    int     proto = IP_IPPROTO_TCP;
    int     sock_type = IP_SOCK_STREAM;
    const char *proto_as_str = "TCP";
    context.udp         = 0;    /* Default is TCP. */
#else
    int     proto = IP_IPPROTO_UDP;
    int     sock_type = IP_SOCK_DGRAM;
    const char *proto_as_str = "UDP";
    context.udp         = 1;    /* Only UDP. */
#endif
    context.b_flag      = 0;
    context.touchdata   = 0;
    context.numCalls    = 0;
    context.err_no       = 0;
#ifdef IPCOM_USE_INET
    context.family      = IP_AF_INET;  /* default IPv4 */
#else
    context.family      = IP_AF_INET6;  /* default IPv6 */
#endif
    context.addrsize    = sizeof(struct Ip_sockaddr_in);

    (void)ipcom_proc_self();	/* when called from vxworks shell, this prevents a crash */

    (void)options;

    /* Init some more. */
    ipcom_memset(&addrhim, 0, sizeof (addrhim));
    IPCOM_SA_LEN_SET(&addrhim.sin, sizeof(struct Ip_sockaddr_in));
    addrhim.sin.sin_family = IP_AF_INET;

    ipcom_memset(&addrme, 0, sizeof(addrme));
    IPCOM_SA_LEN_SET(&addrme.sin, sizeof(struct Ip_sockaddr_in));
    addrme.sin.sin_family = IP_AF_INET;

#ifdef IP_PORT_INTEGRITY
    ipcom_init_libsocket();
#endif

    if (argc < 2)
        goto usage;
    ipcom_getopt_clear();
    while ((c = ipcom_getopt(argc, argv, "M:xdrstUuvBDTb:f:l:n:p:A:O:V:RP:me:a:cS")) != -1)
    {
        switch (c)
        {
#ifdef TCP_OSERFC2385
        case 'M':			/* MD5 signature option */
            md5opt = ip_optarg;
            break;
#endif
#ifdef IPCOM_USE_INET6
        case 'x':
            context.family = IP_AF_INET6;
            break;
#endif
        case 'm':
            mev = 1;
            break;
        case 'e':
            nend = ipcom_atoi(ip_optarg);
            break;
        case 'B':
            context.b_flag = 1;
            break;
        case 't':
            trans = 1;
            break;
        case 'r':
            trans = 0;
            break;
#ifdef IP_SO_DEBUG
        case 'd':
            options |= IP_SO_DEBUG;
            break;
#endif
        case 'D':
#ifdef IP_TCP_NODELAY
            nodelay = 1;
#else
            ipcom_fprintf(ip_stderr, "ttcp: -D option ignored: IP_TCP_NODELAY socket option not supported"IP_LF);
#endif
            break;
        case 'n':
            nbuf = ipcom_atoi(ip_optarg);
            break;
        case 'l':
            buflen = ipcom_atoi(ip_optarg);
            break;
        case 's':
#ifdef IPCOM_TTCP_USE_STDIOMODE
            sinkmode = !sinkmode;
#endif
            break;
        case 'p':
            port = (unsigned short)ipcom_atoi(ip_optarg);
            break;
        case 'P':
            meport = (unsigned short)ipcom_atoi(ip_optarg);
            break;
#ifdef IPCOM_CMD_TTCP_USE_ZEROCOPY_API
        case 'U':
            zerocopy = 1;
            /* fall through */
#endif
        case 'u':
            proto = IP_IPPROTO_UDP;
            sock_type = IP_SOCK_DGRAM;
            proto_as_str = "UDP";
            context.udp = 1;
            break;
        case 'v':
            verbose = 1;
            break;
        case 'A':
            bufalign = ipcom_atoi(ip_optarg);
            break;
        case 'O':
            bufoffset = ipcom_atoi(ip_optarg);
            break;
        case 'b':
#if defined(IP_SO_SNDBUF) || defined(IP_SO_RCVBUF)
            sockbufsize = ipcom_atoi(ip_optarg);
#else
            ipcom_fprintf(ip_stderr, "ttcp: -b option ignored: IP_SO_SNDBUF/IP_SO_RCVBUF socket options not supported"IP_LF);
#endif
            break;
#ifdef IP_SO_REUSEPORT
        case 'R':
            reuse_port = 1;
            break;
#endif
        case 'T':
            context.touchdata = 1;
            break;
#if defined(IP_SO_X_VR)
        case 'V':
            vr = ipcom_atoi(ip_optarg);
            break;
#endif
        case 'a':
            cpu = ipcom_atoi(ip_optarg);
            break;
#ifdef IPSCTP
        case 'c':
            proto = IP_IPPROTO_SCTP;
            sock_type = IP_SOCK_STREAM;
            proto_as_str = "SCTP";
            if (IP_TRUE == sctp_type_conflict)
            {
                ipcom_fprintf(ip_stderr, "ttcp: c/S conflict in command."IP_LF);
                goto errorout;
            }
            sctp_type_conflict = IP_TRUE;
            break;

        case 'S':
            proto = IP_IPPROTO_SCTP;
            sock_type = IP_SOCK_SEQPACKET;
            proto_as_str = "SCTP";
            if (IP_TRUE == sctp_type_conflict)
            {
                ipcom_fprintf(ip_stderr, "ttcp: c/S conflict in command."IP_LF);
                goto errorout;
            }
            sctp_type_conflict = IP_TRUE;
            break;
#endif
        default:
            goto usage;
        }
    }

#ifndef IP_PORT_LAS
    if (cpu >= 0)
    {
        Ip_cpu_set_t cpuset;

        if (cpu < 0 || cpu >= IP_CPU_SETSIZE)
        {
            ipcom_fprintf(ip_stderr, "ttcp: CPU must be between #0 and #%d"IP_LF, IP_CPU_SETSIZE-1);
            return -1;
        }
        IP_CPU_ZERO(&cpuset);
        IP_CPU_SET(cpu, &cpuset);
        if (ipcom_proc_cpu_affinity_set(0, &cpuset) != IPCOM_SUCCESS)
        {
            ipcom_fprintf(ip_stderr, "ttcp: Failed to set affinity to CPU #%d"IP_LF, cpu);
            return -1;
        }
    }
#endif /* IP_PORT_LAS */

    if(trans)
    {
        host = argv[ip_optind];

        if (host == 0)
            goto usage;
        sock_error = ipcom_getsockaddrbyaddrname(context.family, IP_FALSE, host, (struct Ip_sockaddr *)&addrhim);
        if(sock_error != 0)
        {
            ipcom_fprintf(ip_stderr, "ttcp error: ipcom_getsockaddrbyaddrname failed, errno = %d."IP_LF, sock_error);
            ttcperror(&context, "unknown host");
        }

        addrme.sin.sin_port  = ip_htons(meport);
        addrhim.sin.sin_port = ip_htons(port);   /* same port offset for IPv4 and IPv6 */
    }
    else
    {
        /* rcvr */
        addrme.sin.sin_port = ip_htons(port);
    }

#ifdef IPCOM_USE_INET6
    if(context.family == IP_AF_INET6)
    {
        context.addrsize = sizeof(struct Ip_sockaddr_in6);

        IPCOM_SA_LEN_SET(&addrme.sin6, sizeof(struct Ip_sockaddr_in6));
        addrme.sin6.sin6_family  = IP_AF_INET6;

        IPCOM_SA_LEN_SET(&addrhim.sin6, sizeof(struct Ip_sockaddr_in6));
        addrhim.sin6.sin6_family = IP_AF_INET6;
    }
#endif

    /* Send more than the sentinel size - UDPMINLEN. */
    if(context.udp && buflen <= UDPMINLEN)
        buflen = UDPMINLEN + 1;

    /* Align buffer. */
    if ((buf = (char *) ipcom_malloc(buflen + bufalign)) == (char *) IP_NULL)
        ttcperror(&context, "malloc");
    else
        orgbuf = buf;
    if (bufalign != 0)
        buf += (bufalign - ((Ip_ptrdiff_t) buf % bufalign) + bufoffset) % bufalign;

    /* Get a socket. */
    if ((sockfd = ipcom_socket(context.family, sock_type, proto)) == ERR)
        ttcperror(&context,"socket");

    if (trans)
    {
        if(sockbufsize < 0)
        {
            if(context.udp)
                sockbufsize = DEFAULT_UDP_SNDBUF;
            else
                sockbufsize = DEFAULT_TCP_SNDBUF;
        }
        ipcom_fprintf(ip_stdout, "ttcp-t: fd=%d, buflen=%d, nbuf=%d, align=%d/%d, port=%d",
                      sockfd, buflen, nbuf, bufalign, bufoffset, (int)port);
        if (sockbufsize)
            ipcom_fprintf(ip_stdout, ", sockbufsize=%d", sockbufsize);
        ipcom_fprintf(ip_stdout, "  %s  -> %s"IP_LF, proto_as_str, host);
    }
    else
    {
        if(sockbufsize < 0)
        {
            if(context.udp)
                sockbufsize = DEFAULT_UDP_RCVBUF;
            else
                sockbufsize = DEFAULT_TCP_RCVBUF;
        }
        ipcom_fprintf(ip_stdout, "ttcp-r: fd=%d, buflen=%d, nbuf=%d, align=%d/%d, port=%d",
                      sockfd, buflen, nbuf, bufalign, bufoffset, (int)port);
        if (sockbufsize)
            ipcom_fprintf(ip_stdout, ", sockbufsize=%d", sockbufsize);
        ipcom_fprintf(ip_stdout, "  %s"IP_LF, proto_as_str);
    }

    /* Code to open socket is moved to before the printf to get 'sockfd' right. */
    mes(trans, "socket");

    /* Set REUSE addr */
    if(ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_REUSEADDR, (char *)&enable, sizeof (int)) == ERR)
        ttcperror(&context, "setsockopt(IP_SO_REUSEADDR)");
    else
        mes(trans, "setsockopt(IP_SO_REUSEADDR)");

#ifdef IP_SO_REUSEPORT
    /* Set REUSE port */
    if(reuse_port)
    {
        if(ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_REUSEPORT, (char *)&reuse_port, sizeof (int)) == ERR)
            ttcperror(&context, "setsockopt(IP_SO_REUSEPORT)");
        else
            mes(trans, "setsockopt(IP_SO_REUSEPORT)");
    }
#endif

#ifdef IPCOM_USE_TCP
#ifdef TCP_OSERFC2385
    if (!udp && md5opt)
    {
        if (ipcom_setsockopt(sockfd, IPPROTO_TCP, TCP_OSERFC2385,
                             md5opt, strlen(md5opt)) == ERR)
        {
            ttcperror(&context, "setsockopt(IP_SO_REUSEADDR)");
        }
        else
        {
            mes(trans, "setsockopt(IP_SO_REUSEADDR)");
        }
    }
#endif
#endif

	/* Set routing table index in socket. */
#if defined(IP_SO_X_VR)
	if(vr != 0)
    {
	    if (ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_X_VR, (char *)&vr, 4) == ERR)
            ttcperror(&context, "setsockopt: vr");
	    else
            mes(trans, "setsockopt(vr)");
    }
#endif

    /* Bind the socket. */
    if(ipcom_bind(sockfd, (struct Ip_sockaddr *)&addrme, context.addrsize) == ERR)
        ttcperror(&context, "bind");
    else
#ifdef IPCOM_USE_INET6
        ipcom_fprintf(ip_stderr, "ttcp%s: bind %s %d"IP_LF, trans ? "-t" : "-r",
                      context.family == IP_AF_INET ? "IPv4" : "IPv6",
                      (int)ip_ntohs(addrme.sin.sin_port));
#else
    {
        char   tmpbuf[32];
        ipcom_sprintf(tmpbuf, "bind %d", ip_ntohs(addrme.sin.sin_port));
        mes(trans, tmpbuf);
    }
#endif

    /* Adjust socker buffer size. */
#if defined(IP_SO_SNDBUF) || defined(IP_SO_RCVBUF)
    if(sockbufsize)
    {
        if (trans)
        {
            if (ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_SNDBUF, (char *)&sockbufsize, sizeof sockbufsize) == ERR)
                ttcperror(&context,"setsockopt: sndbuf");
            else
                mes(trans, "setsockopt(sndbuf)");
        }
        else
        {
            if (ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_RCVBUF, (char *)&sockbufsize, sizeof sockbufsize) == ERR)
                ttcperror(&context,"setsockopt: rcvbuf");
            else
                mes(trans, "setsockopt(rcvbuf)");
        }
    }
#endif

    /* If TCP we need to connect else accept to remote side. */
#ifdef IPCOM_USE_TCP
    if (context.udp)
    {
        mes(trans, "opened");
    }
    else
    {
        /* We are the client if transmitting */
        if(trans)
        {
            if(options)
            {
                if(ipcom_setsockopt(sockfd, IP_SOL_SOCKET, options, (char *)&one, sizeof (one)) == ERR)
                    ttcperror(&context,"setsockopt");
            }
#ifdef IP_TCP_NODELAY
            if(nodelay)
            {
                if(ipcom_setsockopt(sockfd, IP_IPPROTO_TCP, IP_TCP_NODELAY, (char *)&one, sizeof (one)) == ERR)
                    ttcperror(&context,"setsockopt: nodelay");
                mes(trans, "nodelay");
            }
#endif

            if(ipcom_connect(sockfd, (struct Ip_sockaddr *)&addrhim, context.addrsize) == ERR)
                ttcperror(&context,"connect");
            else
                mes(trans, "connect");
        }

        /* Otherwise, we are the TCP server and should listen for the connections.  */
        else
        {
            if(ipcom_listen(sockfd, 0) == ERR)
                ttcperror(&context,"listen");
            else
                mes(trans, "listen");

            if(options)
            {
                if(ipcom_setsockopt(sockfd, IP_SOL_SOCKET, options, (char *)&one, sizeof (one)) == ERR)
                    ttcperror(&context,"setsockopt");
            }

            fromlen = context.addrsize;
            oldfd = sockfd;
#ifdef IPSCTP
            if ((IP_SOCK_SEQPACKET == sock_type)
                && (IP_IPPROTO_SCTP == proto))
            {
                #define TTCP_SCTP_RCV_BUF_LEN 128
                struct Ipsctp_event_subscribe events;
                union Ip_sockaddr_union  local_addr;
                struct Ipsctp_sndrcvinfo sinfo;
                Ip_u32                   len   = 0;
                char                     pbuf[TTCP_SCTP_RCV_BUF_LEN];
                int                      flags = 0;
                int                      ret   = 0;

                /* set data I/O event flag */
                ipcom_memset(&events, 0, sizeof(events));
                events.Ipsctp_data_io_event = 1;
                ret = ipcom_setsockopt(sockfd,
                                       IP_IPPROTO_SCTP,
                                       IPSCTP_EVENTS,
                                       (void *)&events,
                                       sizeof(events));
                if (ERR == ret)
                    ttcperror(&context, "setsockopt events");

                /* get the association identifier */
                ipcom_memset(pbuf, 0, TTCP_SCTP_RCV_BUF_LEN);
                len = sizeof(local_addr);
                /* Wait for connections */
                ret = ipsctp_recvmsg(sockfd,
                                     pbuf,
                                     TTCP_SCTP_RCV_BUF_LEN,
                                     &local_addr.sa,
                                     &len,
                                     &sinfo,
                                     &flags);
                sockfd = ipsctp_peeloff(sockfd, sinfo.sinfo_assoc_id);
                if (IP_SOCKERR == sockfd)
                    ttcperror(&context,"peeloff");
            }
            else
#endif
            {
                if((sockfd = ipcom_accept(sockfd, (struct Ip_sockaddr *)&frominet, &fromlen)) == ERR)
                    ttcperror(&context,"accept");
            }

            {
                union Ip_sockaddr_union peer;
                Ip_socklen_t            peerlen = context.addrsize;

                /* Close the mother socket. */
                if(ipcom_socketclose(oldfd) == ERR)
                    ttcperror(&context,"close of oldfd.");

#if defined(IP_SO_RCVBUF)
                /* Had to add this code in addition to the above because some stacks don't inherit
                 * the receive buffer size from the server socket. /Lennart Bang Enea Ose Systems 980116.
                 */
                if(sockbufsize)
                {
                    if(ipcom_setsockopt(sockfd, IP_SOL_SOCKET, IP_SO_RCVBUF, (char *)&sockbufsize, sizeof sockbufsize) == ERR)
                        ttcperror(&context,"setsockopt: rcvbuf");
                    else
                        mes(trans, "setsockopt(rcvbuf)");
                }
#endif

                if(ipcom_getpeername(sockfd, (struct Ip_sockaddr *) &peer, &peerlen) == ERR)
                    ttcperror(&context, "getpeername");

#ifdef IPCOM_USE_INET
                if(context.family == IP_AF_INET)
                {
                    char addr[16];
                    ipcom_fprintf(ip_stderr, "ttcp-r: accept from %s"IP_LF,
                                  ipcom_inet_ntop(IP_AF_INET, &peer.sin.sin_addr, addr, sizeof(addr)));
                }
#endif
#ifdef IPCOM_USE_INET6
                if(context.family == IP_AF_INET6)
                {
                    char addr[40];
                    ipcom_fprintf(ip_stderr, "ttcp-r: accept from %s"IP_LF,
                                  ipcom_inet_ntop(IP_AF_INET6, &peer.sin6.sin6_addr, addr, sizeof(addr)));
                }
#endif
            }
        }
    }
#endif

    /* Begin the TTCP performance test. */
    msec0 = get_millisec();

    if (mev)
    {
#if defined (IP_PORT_INTEGRITY)
#include "ipcom_integrity.h"
        extern void EventLogOn(void);
#if IP_VER < 500
        extern void EventLogMask(unsigned int newmask);
        EventLogMask(~0);
#endif
        EventLogOn();
#endif
    }

    if(sinkmode)
    {
        register int cnt;

        /* Transmit side. */
        if(trans)
        {
            pattern(buf, buflen);
            if(context.udp)
            {
                (void) NWRITE(sockfd, buf, UDPMINLEN, &context);	/* rcvr start */
                ipcom_millisleep(500);                  /* arp time */
                /* Renew the start time to not include the 500 msec sleep */
                msec0 = get_millisec();
            }
#ifdef IPCOM_CMD_TTCP_USE_ZEROCOPY_API
            if (zerocopy)
            {
                while (nbuf-- && ZERONWRITE(sockfd, buflen, &context) == buflen)
                    nbytes += buflen;
            }
            else
#endif
            {
                while (nbuf-- && NWRITE(sockfd, buf, buflen, &context) == buflen)
                    nbytes += buflen;
            }
            if(context.udp)
                (void) NWRITE(sockfd, buf, UDPMINLEN, &context);	/* rcvr end */
        }

        /* Receive side. */
        else
        {
            if(context.udp)
            {
                int going = 0;

#ifdef IPCOM_CMD_TTCP_USE_ZEROCOPY_API
                if (zerocopy)
                {
                    while ((cnt = ZeroNread(sockfd, &context)) > 0)
                    {
                        if(cnt <= UDPMINLEN)
                        {
                            if(going)
                                break;	/* "EOF" */
                            going = 1;
                            msec0 = get_millisec();
                        }
                        else
                        {
                            nbytes += cnt;
                        }
                    }
                }
                else
#endif
                {
                    while ((cnt = Nread(sockfd, buf, buflen, &context)) > 0)
                    {
                        if(cnt <= UDPMINLEN)
                        {
                            if(going)
                                break;	/* "EOF" */
                            going = 1;
                            msec0 = get_millisec();
                        }
                        else
                        {
                            nbytes += cnt;
                        }
                    }
                }
            }
#ifdef IPCOM_USE_TCP
            else
            {
                while ((cnt = Nread(sockfd, buf, buflen, &context)) > 0)
                {
                    nbytes += cnt;
                }
            }
#endif
        }
    }

#ifdef IPCOM_TTCP_USE_STDIOMODE
    /* non sinkmode. */
    else
    {
        register int cnt;

        if(trans)
        {
            /* Read from standard input and send to other side. */
            while ((cnt = ipcom_fread(buf, buflen, 1, ip_stdin)) > 0 &&
                   NWRITE(sockfd, buf, cnt) == cnt)
                nbytes += cnt;
        }
        else
        {
            /* Read from network and print on stdout. */
            while ((cnt = Nread(sockfd, buf, buflen, &context)) > 0)
            {
                ipcom_fwrite(buf, cnt, 1, ip_stdout);
                nbytes += cnt;
            }
        }
    }
#endif

    if (mev)
    {
#if defined (IP_PORT_INTEGRITY)
        extern void EventLogOff(void);
        EventLogOff();
#elif defined (IP_PORT_RTCORE)
        extern void ip_trace(char *what, char *file, int line, int id, void *data);
        extern void ip_tracedump(void);
        ip_trace("[ASSERT]", __FILE__, __LINE__, ipcom_getpid(), IP_NULL);
        ip_tracedump();
#endif
    }

    /* Read the final time and calculate some statistics. */
    msec1 = get_millisec();
    msecd = msec1 - msec0;
    if (msecd == 0)
        msecd = 1;
    bpermsec = nbytes / msecd;   /* Bytes per millisecond. */
    kbpersec = nbytes / 128;
    kbpersec = kbpersec * 125 / msecd; /* kilobyte per sec. */

    /* End of test assemble statistice. */
    if(context.err_no)
    {
        ipcom_fprintf(ip_stdout, "ttcp%s: socket errno: %d."IP_LF, trans ? "-t" : "-r", context.err_no);
    }

    if(context.udp && trans)
    {
        int j;

        for (j = 0; j < nend; j++)
        {
            ipcom_millisleep(100); /* Let the reading side catch up. */
            (void) NWRITE(sockfd, buf, UDPMINLEN, &context);	/* rcvr end */
        }
    }

    /* Print the statistics. */
    ipcom_fprintf(ip_stdout, "ttcp%s: %lu bytes in %lu milliseconds = %lu KB/sec, %lu B/msec +++"IP_LF,
                  trans ? "-t" : "-r", nbytes, msecd, kbpersec, bpermsec);

    ipcom_fprintf(ip_stdout, "ttcp%s: %lu I/O calls, msec/call = %lu, calls/sec = %lu"IP_LF,
                  trans ? "-t" : "-r",
                  context.numCalls,
                  msecd / context.numCalls,
                  1000 * context.numCalls / msecd);

    if (verbose)
        ipcom_fprintf(ip_stdout, "ttcp%s: buffer address %p"IP_LF, trans ? "-t" : "-r", buf);


    /* Test succeeded, goto errorout to cleanup. */
    goto errorout;

    /* Usage. */
 usage:
    ipcom_fprintf(ip_stderr,
                  "Usage: ttcp -t [-options] host [ < in ]"IP_LF
                  "ttcp -r [-options > out]"IP_LF
                  "Common options:"IP_LF
                  "-x      use IPv6 instead of IPv4"IP_LF
                  "-l ##	length of bufs read from or written to network (default 8192)"IP_LF
#ifdef IPCOM_CMD_TTCP_USE_ZEROCOPY_API
                  "-U	use UDP and interpeak zero copy API instead of TCP"IP_LF
#endif
                  "-u	use UDP (default is TCP)"IP_LF
#ifdef IPSCTP
                  "-c	use SCTP STREAM type(default is TCP)"IP_LF
                  "-S	use SCTP SEQPACKET type(default is TCP)"IP_LF
#endif
                  "-p ##	port number to send to or listen at (default DEFAULT_PORT)"IP_LF);
    ipcom_fprintf(ip_stderr,
                  "-s	-t: source a pattern to network"IP_LF
                  "-r	sink (discard) all data from network"IP_LF
                  "-e ##	number of packets to end UDP transmission"IP_LF
                  "-m	use event logging (not supported on all platforms)"IP_LF
                  "-A	align the start of buffers to this modulus (default 16384)"IP_LF
                  "-O	start buffers at this offset from the modulus (default 0)"IP_LF
                  "-v	verbose: print more statistics"IP_LF
                  "-d	set SO_DEBUG socket option (if supported)"IP_LF);
    ipcom_fprintf(ip_stderr,
                  "-b ##	set socket buffer size (if supported)"IP_LF
                  "Options specific to -t:"IP_LF
                  "-n##	number of source bufs written to network (default 2048)"IP_LF
                  "-D	don't buffer TCP writes (sets TCP_NODELAY socket option)"IP_LF
                  "Options specific to -r:"IP_LF
                  "-B	for -s, only output full blocks as specified by -l (for TAR)"IP_LF
                  "-T	\"touch\": access each byte as it's read"IP_LF
#if defined(IP_SO_X_VR)
                  "-V      virtual router index (if supported)"IP_LF);
#endif
    /* fall through to cleanup...*/

    /* Free buffer and close socket if any error. */
 errorout:
#ifndef IP_PORT_LAS
    if (cpu >= 0)
        ipcom_proc_cpu_affinity_clr(0);
#endif /* IP_PORT_LAS */
    if(orgbuf)
        ipcom_free(orgbuf);
    if(sockfd != -1)
    {
        if(ipcom_socketclose(sockfd) == ERR)
            ipcom_fprintf(ip_stderr, "ttcp error: close of sockfd. (line %d, errno %d)."IP_LF, __LINE__, ipcom_errno);
    }

#ifdef IP_PORT_INTEGRITY
    ipcom_shutdown_libsocket();
#endif

    return 0;
}
/*
 *===========================================================================
 *                    ipnet_nat_proxy_sip_localaddrprocess
 *===========================================================================
 * Description: Process the private transport address
 * Parameters:  pmsgstr    - pointer to message
 *              pipaddrstr - pointer to buffer for the local address string
 *              pgaddrstr  - pointer to buffer for the gloab address string
 *              pcallid    - pointer to the call id string
 *              type       - type of search
 *              ppend      - pointer to pointer to last byte of message
 * Returns:     The next character position after the parsed string or
 *	            IP_NULL if parse fails
 */
IP_STATIC char *
ipnet_nat_proxy_sip_localaddrprocess(char *pmsgstr,
                                     char *pipaddrstr,
                                     char *pgaddrstr,
                                     char *pcallid,
                                     int  type,
                                     Ipnet_nat_proxy_param *param,
                                     char **ppend)
{
    int localport, newport;
    int len, diff;
    Ip_u32 ipaddr;
    char *pstart;
    char tmpholder[30];
    Ipnet_nat_proxy_tuple proxy_tuple;

    (void)pcallid;
    SIP_SKIP_SPACES(pmsgstr);

    pstart = pmsgstr;
    pmsgstr = ipnet_nat_proxy_sip_addrportstrparse(pmsgstr, &ipaddr, &localport, type);

    if (pmsgstr == IP_NULL)
        return IP_NULL;

    if (!localport)
        localport = SIP_DEFAULT_PORT;

    if (type == SIP_ADDRESS_PORT_STRING)
    {
        ipaddr = ip_htonl(ipaddr);
        (void)ipcom_inet_ntop(IP_AF_INET, &ipaddr, pipaddrstr, 16);
        ipaddr = ip_ntohl(ipaddr);
    }
    else  /* PORT string only */
    {
        ipaddr = ipcom_inet_addr(pipaddrstr);
        ipaddr = ip_ntohl(ipaddr);
    }

    /* make sure it is the private address, otherwise no translation */

    if (ipaddr != param->tuple.private_addr)
        return pmsgstr;

    /* create a bind entry for this transport address if it hasn't been created yet */
    if (param->tuple.private_port != localport)
    {
        ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
        proxy_tuple.protocol     = IP_IPPROTO_UDP;
        proxy_tuple.private_addr = ipaddr;
        proxy_tuple.private_port = localport;
        newport = ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                              IPNET_NAT_SIP_ENTRY_MEDIA_TIMEOUT,
                                              param->mapping,
                                              IP_FALSE,         /* Use port translation */
                                              IP_TRUE,          /* Inbound session */
                                              IP_NULL,
                                              IP_NULL);

        if (newport < 0)
        {
            IPCOM_LOG2(ERR, "ipnet_nat_proxy_sip_localaddrprocess() :: Failed to add mapping for address = 0x%08x, port = %d",
                       ipaddr, localport);
        }
        else
        {
            IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_sip_localaddrprocess() :: Added mapping for address = 0x%08x, port = %d",
                       ipaddr, localport);
        }
    }
    else
    {
        newport = param->nat_port;
        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_sip_localaddrprocess() :: Mapping already existed for address = 0x%08x, port = %d",
                   ipaddr, localport);
    }

    /* compose the new modified string */

    len = 0;
    if (type == SIP_ADDRESS_PORT_STRING)
    {
        ipaddr = ip_htonl(param->nat_addr);
        (void)ipcom_inet_ntop(IP_AF_INET, &ipaddr, tmpholder, 16);
        ipcom_strcpy(pgaddrstr, tmpholder);
        len = ipcom_strlen(tmpholder);
        tmpholder[len++]= ':';
    }
    ipcom_sprintf(tmpholder + len, "%d", newport);

    IPCOM_LOG1(DEBUG, "ipnet_nat_proxy_sip_localaddrprocess() new str: %s", tmpholder);


    if (ipnet_nat_proxy_sip_modmsg(tmpholder, ipcom_strlen(tmpholder),
                                   pstart, pmsgstr - pstart, ppend) < 0)
    {
        return IP_NULL;
    }

    diff = ipcom_strlen(tmpholder) - (pmsgstr - pstart);
    return pmsgstr + diff;
}
/*
 *===========================================================================
 *                    ipdnsc_getipnodebyname
 *===========================================================================
 * Description: See ipdnsc.h for description.
 * Parameters:
 * Returns:
 */
IP_PUBLIC struct Ip_hostent*
ipdnsc_getipnodebyname(const char *name, int af, int flags, int *error_num)
{
	int literal_ipv4;
    int ifc_ipv4 = 1;
    struct Ip_in_addr inaddr4;
    int literal_ipv6;
    int ifc_ipv6 = 1;
#ifdef IPCOM_USE_INET6
	struct Ip_in6_addr inaddr6;
#endif
    struct Ip_hostent *he, *he_ipv6, *he_ipv4, *he_ipv4_mapped;
    Ipdnsc_ns_spec dns;

    he = he_ipv6 = he_ipv4 = he_ipv4_mapped = IP_NULL;

    /* Check if supplied name is a literal address string */
    literal_ipv4 = ipcom_inet_pton(IP_AF_INET, (IP_CONST char *)name, &inaddr4);

#ifdef IPCOM_USE_INET6
    literal_ipv6 = ipcom_inet_pton(IP_AF_INET6, (IP_CONST char *)name, &inaddr6);
#else
    literal_ipv6 = 0;
#endif

    /* Check if active interface flag is given */
	if ((flags & IPDNSC_AI_ADDRCONFIG) != 0)
    {
        ifc_ipv4 = 0;

#ifndef IPCOM_USE_INET6
        ifc_ipv6 = 0;
#endif /* IPCOM_USE_INET6 */
    }

    /************* Check arguments ***************/
    *error_num = IPDNSC_HOST_NOT_FOUND;
    if (af != IP_AF_INET && af != IP_AF_INET6)
    {
        /* Address family not supported */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    if (literal_ipv6 && (af == IP_AF_INET))
    {
        /* Address family AF_INET not consistent with literal ipv6 address */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    if (literal_ipv4 && (af == IP_AF_INET6) && !(flags & IPDNSC_AI_V4MAPPED))
    {
        /* Address family AF_INET6 not consistent with literal ipv4 address unless AI_V4MAPPED flag was set */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    if (literal_ipv4 && !ifc_ipv4)
    {
        /* Literal ipv4 address not possible with no configured ipv4 interfaces */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    if ((af == IP_AF_INET) && !ifc_ipv4)
    {
        /* Address family AF_INET not possible with no configured ipv4 interfaces */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    if ((af == IP_AF_INET6) && !ifc_ipv6)
    {
        /* Address family AF_INET6 not possible with no configured ipv6 interfaces */
  	    *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }
    /********************* Arguments OK ***********************/

    /* If name was a literal address no resolver calls are required */
	if (literal_ipv4 || literal_ipv6)
    {
        Ip_s32 type;
        char *ptr;
        Ip_u8 addr[16], buf[40];
        Ip_u8 in6addr_mapped[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};

        if (literal_ipv4)
        {
            if (flags & IPDNSC_AI_V4MAPPED)
            {
                type = IP_AF_INET6;
                ipcom_memcpy(addr, in6addr_mapped, sizeof(in6addr_mapped));
                ipcom_memcpy(addr+sizeof(in6addr_mapped), (char *)&inaddr4, IPDNSC_INADDRSZ);
                ptr = (char *)addr;
                if (ipcom_inet_ntop(IP_AF_INET6, addr, (char *)buf, sizeof(buf)) == IP_NULL)
                {
                    return IP_NULL;
                }
                name = (char *)buf;
            }
            else
            {
                type = IP_AF_INET;
                ptr = (char *)&inaddr4;
            }
        }
#ifdef IPCOM_USE_INET6
        else if (literal_ipv6)
        {
            type = IP_AF_INET6;
            ptr = (char *)&inaddr6;
        }
#endif
        else
        {
            return IP_NULL;
        }

        /* Create a host entry structure */
        he = ipdnsc_hostent_create(type);
        if (!he)
        {
            return IP_NULL;
        }
        /* Insert the address */
        if (ipdnsc_hostent_insert_addr(he, ptr))
        {
            goto err_out;
        }
        /* Insert the hostname */
        if (ipdnsc_hostent_insert_name(he, (char *)name))
        {
            goto err_out;
        }

        return he;
	}


    /************** Supplied name was a domain name ******************/
    if(ipdnsc.init != IP_TRUE)
    {
        *error_num = IPDNSC_NO_RECOVERY;
        return IP_NULL;
    }

    /* Configure the DNS */
    if (ipdnsc_default(&dns))
    {
        *error_num = IPDNSC_NO_RECOVERY;
	    return IP_NULL;
    }

    /* Resolve the ipv6 address if required */
    if (ifc_ipv6 && af == IP_AF_INET6)
    {
        he_ipv6 = ipdnsc_resolve_name((Ip_u8 *)name, IP_AF_INET6, IP_TRUE, &dns, (Ip_s32 *)error_num);
        if (!he_ipv6 && !(flags & IPDNSC_AI_V4MAPPED))
        {
            goto err_out;
        }
	}

    /* Resolve the ipv4 address if required */
    if (ifc_ipv4)
    {
        if (af == IP_AF_INET)
        {
            he_ipv4 = ipdnsc_resolve_name((Ip_u8 *)name, IP_AF_INET, IP_TRUE, &dns, (Ip_s32 *)error_num);
            if (!he_ipv4)
            {
                goto err_out;
            }
        }
        else
        {
            /* mapped ipv4 addresses always wanted ||
               mapped ipv4 addresses wanted if no ipv6 addresses found */
            if (((flags & IPDNSC_AI_V4MAPPED) && (flags & IPDNSC_AI_ALL)) || ((flags & IPDNSC_AI_V4MAPPED) && !he_ipv6))
            {
                he_ipv4 = ipdnsc_resolve_name((Ip_u8 *)name, IP_AF_INET, IP_TRUE, &dns, (Ip_s32 *)error_num);

                if (!he_ipv4 && !he_ipv6)
                {
                    goto err_out;
                }

                if (he_ipv4)
                {
                    he_ipv4_mapped = ipdnsc_hostent_convert(he_ipv4);
                    ipdnsc_hostent_free(he_ipv4);
                    he_ipv4 = IP_NULL;
                    if (!he_ipv4_mapped)
                    {
                        goto err_out;
                    }
                }
            }
        }
    }

    if (he_ipv6 && he_ipv4_mapped)
    {
        he = ipdnsc_hostent_merge(he_ipv6, he_ipv4_mapped);
        ipdnsc_hostent_free(he_ipv6);
        ipdnsc_hostent_free(he_ipv4_mapped);
    }
    else if (he_ipv6 && !he_ipv4_mapped)
    {
        he = he_ipv6;
    }
    else if (!he_ipv6 && he_ipv4_mapped)
    {
        he = he_ipv4_mapped;
    }
    else
    {
        he = he_ipv4;
    }

    return (he);

err_out:
    if (he_ipv6)
        ipdnsc_hostent_free(he_ipv6);

    if (he_ipv4)
        ipdnsc_hostent_free(he_ipv4);

    if (he_ipv4_mapped)
        ipdnsc_hostent_free(he_ipv4_mapped);


    ipdnsc_hostent_free(he);
    return IP_NULL;

}
/*
 *===========================================================================
 *                    ipnet_config_add_inet6_addr
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC int
ipnet_config_add_inet6_addr(Ip_fd fd, char *ifname, char *option)
{
    char *opt;
    char *inet6_addr;
    char *prefix_len;
    char *argv[] = {
        "ifconfig",
        "-silent",
        IP_NULL,
        "inet6",
        "add",
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL
    };
    int argc = 6;
    char inet6_addr_str[IP_INET6_ADDRSTRLEN];
    char prefixlen_str[4];

    argv[2] = ifname;

    for (;;)
    {
        /* Parse options */
        opt = ipcom_strtok_r(option, " \t/", &option);
        if (opt == IP_NULL)
        {
            IPCOM_LOG0(ERR, "too few arguments");
            return -IP_ERRNO_EINVAL;
        }
        if (ipcom_strcmp(opt, "tentative") == 0)
            argv[argc++] = opt;
        else
            break;
    }

    inet6_addr = opt;
    if (ipcom_strcmp(inet6_addr, "driver") == 0)
    {
        /* Get the IPv6 address to use from the driver */
        struct Ip_ethreq ethreq;

        ipcom_strcpy(ethreq.ethr_name, ifname);
        if (ipcom_socketioctl(fd, IP_SIOCXETHGINET6, &ethreq) < 0)
        {
            IPCOM_LOG1(ERR, "Failed to read the IPv6 address from the driver for %s", ifname);
            return ipcom_errno;
        }
        inet6_addr = inet6_addr_str;
        (void) ipcom_inet_ntop(IP_AF_INET6, &ethreq.ethru.inet6.addr,
                               inet6_addr_str, sizeof(inet6_addr_str));

        ipcom_sprintf(prefixlen_str, "%u", ethreq.ethru.inet6.prefixlen);
        prefix_len = prefixlen_str;
    }
    else
    {
        prefix_len = ipcom_strtok_r(option, " \t/", &option);
        if (prefix_len == IP_NULL)
        {
            IPCOM_LOG0(ERR, "prefix len is missing or format is invalid");
            return -IP_ERRNO_EINVAL;
        }
    }

    argv[5] = inet6_addr;
    argv[argc++] = "prefixlen";
    argv[argc++] = prefix_len;

    return ipnet_config_cmd_ifconfig(argc, argv);
}
/*
 *===========================================================================
 *                    ipnet_config_add_route
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC int
ipnet_config_add_route(int domain, char *addr, int prefix_len, char *gateway)
{
    char *argv[] = {
        "route",
        "add",
        "-silent",
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
        IP_NULL,
    };
    int argc = 4;
    int max_prefix_len;
    union Ipnet_addr mask;
    union Ipnet_addr dst_addr;
    char dst_str[IP_INET6_ADDRSTRLEN];
    char prefix_len_str[4];
    int ret;

#ifdef IPCOM_USE_INET
    if (domain == IP_AF_INET)
    {
        max_prefix_len = 32;
        argv[3] = "-inet";
    }
    else
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
    if (domain == IP_AF_INET6)
    {
        max_prefix_len = 128;
        argv[3] = "-inet6";
    }
    else
#endif /* IPCOM_USE_INET6 */
    {
        /* Unsupported domain */
        IPCOM_LOG1(ERR, "unsupported domain: %d", domain);
        return -IP_ERRNO_EAFNOSUPPORT;
    }

    if (prefix_len == -1)
        /* Host route */
        prefix_len = max_prefix_len;

    if (addr == IP_NULL || gateway == IP_NULL || prefix_len < 0 || prefix_len > max_prefix_len)
        return -IP_ERRNO_EINVAL;

    if (ipcom_inet_pton(domain, addr, &dst_addr) != 1)
    {
        IPCOM_LOG2(ERR, "invalid %s address format: %s",
            (domain == IP_AF_INET) ? "inet" : "inet6", addr);
        return -IP_ERRNO_EINVAL;
    }

    /* Apply mask */
    ipcom_memset(&mask, 0, sizeof(mask));
    ipnet_route_create_mask(&mask, prefix_len);
    ipnet_route_apply_mask(&dst_addr, &mask, max_prefix_len);
    (void) ipcom_inet_ntop(domain, &dst_addr, dst_str, sizeof(dst_str));

    argv[argc++] = "-static";

    if (prefix_len == max_prefix_len)
        argv[argc++] = "-host";
    else
    {
        argv[argc++] = "-net";
        argv[argc++] = "-prefixlen";
        ipcom_sprintf(prefix_len_str, "%d", prefix_len);
        argv[argc++] = prefix_len_str;
    }

    argv[argc++] = dst_str;
    argv[argc++] = gateway;

    ret = ipnet_config_cmd_route(argc, argv);
    if (ret == IP_ERRNO_EEXIST)
        return 0;
    return ret;
}
static void
ipsecctrl_saTables_print(Argvars *vars, Ip_u8 direction, Ip_u8 protocol, int domain)
{
    Ipipsec_ioctl_sactrl   sa_next;
    Ipipsec_ioctl_sactrl   sa_u64;
    Ip_err   retval;
    int  i, num;
    Ip_tag tags[41];
    char table[32];
#ifdef IPCOM_USE_INET6
    char str[IP_INET6_ADDRSTRLEN];
#else
    char str[16];
#endif

    /* pre-init print stuff. */
    ipcom_sprintf(table, "ipsecSa%s%s",
                  protocol == IP_IPPROTO_AH ? "Ah" : "Esp",
                  direction == IPIPSEC_SADIR_INPUT ? "In" :
                  direction == IPIPSEC_SADIR_OUTPUT ? "Out" :
                  "Any");

    /* Fill in the info tags. */
    tags[0] = IPIPSEC_SACTRLT_GET_SaSelector;
    tags[2] = IPIPSEC_SACTRLT_GET_SaCreator;
    tags[4] = IPIPSEC_SACTRLT_GET_Encapsulation;
    tags[6] = IPIPSEC_SACTRLT_GET_EncAlg;
    tags[8] = IPIPSEC_SACTRLT_GET_EncKeyLength;
    tags[10] = IPIPSEC_SACTRLT_GET_AuthAlg;
    tags[12] = IPIPSEC_SACTRLT_GET_AuthKeyLength;
    tags[14] = IPIPSEC_SACTRLT_GET_RepWinSize;
    tags[16] = IPIPSEC_SACTRLT_GET_LimitSeconds;
    tags[18] = IPIPSEC_SACTRLT_GET_LimitKbytes;
    tags[20] = IPIPSEC_SACTRLT_GET_AccSeconds;
    tags[22] = IPIPSEC_SACTRLT_GET_InAccKbytes;
    tags[24] = IPIPSEC_SACTRLT_GET_DecryptErrors;
    tags[26] = IPIPSEC_SACTRLT_GET_AuthErrors;
    tags[28] = IPIPSEC_SACTRLT_GET_ReplayErrors;
    tags[30] = IPIPSEC_SACTRLT_GET_PolicyErrors;
    tags[32] = IPIPSEC_SACTRLT_GET_PadErrors;
    tags[34] = IPIPSEC_SACTRLT_GET_OtherReceiveErrors;
    tags[36] = IPIPSEC_SACTRLT_GET_OutAccKbytes;
    tags[38] = IPIPSEC_SACTRLT_GET_OutSendErrors;
    tags[40] = IP_TAG_END;

    ipcom_printf(IP_LF"***** %stable ::"IP_LF, table);

    /* Get and print the SAs in MIB format. */
    sa_next.arg.next.spi_n = 0;

    for (num = 0;;)
    {
        ipcom_memcpy(&sa_next.sa, &sa_next.arg.next, sizeof(Ipipsec_sa_handle));
        sa_next.arg.next.direction = direction;
        sa_next.arg.next.protocol  = protocol;
        sa_next.arg.next.domain    = (Ip_u8)domain;
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_NEXT_SA, &sa_next) < 0)
        {
            ipcom_printf("ipsecctrl: ipcom_socketioctl(NEXT_SA) failed (%s)"IP_LF,
                         ipcom_strerror(ipcom_errno));
            return;
        }

        if (sa_next.arg.next.spi_n == 0)
            break;

        retval = ipipsec_sactrl(&sa_next.arg.next, tags);
        if (retval != IPCOM_SUCCESS)
        {
            ipcom_printf("ipsecctrl: ipipsec_sactrl(info) failed, error = %d"IP_LF, retval);
            return;
        }

        /* SA id */
        ipcom_printf(IP_LF"%sEntry #%d:"IP_LF
                     "%sAddresstype = %d"IP_LF
                     "%sAddress = %s"IP_LF
                     "%sSpi = 0x%lx"IP_LF
                     , table, num
                     , table, domain == IP_AF_INET ? 1 : domain == IP_AF_INET6 ? 2 : 3
                     , table, ipcom_inet_ntop(sa_next.arg.next.domain, &sa_next.arg.next.dst, str, sizeof(str))
                     , table, ip_ntohl(sa_next.arg.next.spi_n));

        /* SA MIB entries */
        for (i = 0; tags[i] != IP_TAG_END; i += 2)
        {
            switch(tags[i])
            {
            case IPIPSEC_SACTRLT_GET_SaSelector :
                ipcom_printf("%sSelector = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_SaCreator :
                ipcom_printf("%sCreator = %s"IP_LF, table, mib_Creator[tags[i+1]]);
                break;

            case IPIPSEC_SACTRLT_GET_Encapsulation :
                ipcom_printf("%sEncapsulation = %s"IP_LF, table, mib_Encapsulation[tags[i+1]]);
                break;

            case IPIPSEC_SACTRLT_GET_EncAlg :
                ipcom_printf("%sEncAlg = %s"IP_LF, table, mib_EncAlg[tags[i+1]]);
                break;

            case IPIPSEC_SACTRLT_GET_EncKeyLength :
                ipcom_printf("%sEncKeyLength = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_AuthAlg :
                ipcom_printf("%sAuthAlg = %s"IP_LF, table, mib_AuthAlg[tags[i+1]]);
                break;

            case IPIPSEC_SACTRLT_GET_AuthKeyLength :
                ipcom_printf("%sAuthKeyLength = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_RepWinSize :
                ipcom_printf("%sRepWinSize = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_LimitSeconds :
                ipcom_printf("%sLimitSeconds = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_LimitKbytes :
                ipcom_printf("%sLimitKbytes = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_AccSeconds :
                ipcom_printf("%sAccSeconds = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_InAccKbytes :
                ipcom_printf("%sInAccKbytes = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_OutAccKbytes :
                ipcom_printf("%sOutAccKbytes = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_DecryptErrors :
                ipcom_printf("%sDecryptErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_AuthErrors :
                ipcom_printf("%sAuthErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_ReplayErrors :
                ipcom_printf("%sReplayErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_PolicyErrors :
                ipcom_printf("%sPolicyErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_PadErrors :
                ipcom_printf("%sPadErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_OtherReceiveErrors :
                ipcom_printf("%sOtherReceiveErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            case IPIPSEC_SACTRLT_GET_OutSendErrors :
                ipcom_printf("%sOutSendErrors = %ld"IP_LF, table, tags[i+1]);
                break;

            default:
                ipcom_printf("error: unknown tag type '%ld'"IP_LF, tags[i]);
                break;
            }
        } /* for */

        ipcom_memcpy(&sa_u64.sa, &sa_next.arg.next, sizeof(Ipipsec_sa_handle));
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_InUserOctets, &sa_u64) == 0)
            ipcom_printf("%sInUserOctets = %ld"IP_LF, table, IP_U64_GETLO(sa_u64.arg.u64)); /*lint !e530 */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_InUserPackets, &sa_u64) == 0)
            ipcom_printf("%sInUserPackets = %ld"IP_LF, table, IP_U64_GETLO(sa_u64.arg.u64)); /*lint !e530 */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_OutUserOctets, &sa_u64) == 0)
            ipcom_printf("%sOutUserOctets = %ld"IP_LF, table, IP_U64_GETLO(sa_u64.arg.u64)); /*lint !e530 */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_OutUserPackets, &sa_u64) == 0)
            ipcom_printf("%sOutUserPackets = %ld"IP_LF, table, IP_U64_GETLO(sa_u64.arg.u64)); /*lint !e530 */
    }
}
/*
 *===========================================================================
 *                         ipsecctrl_sa
 *===========================================================================
 */
static int
ipsecctrl_sa(Argvars *vars)
{
    Ipipsec_ioctl_sactrl   sa_next;
    Ipipsec_ioctl_sactrl   sa_group;
    int       num;
    Ip_err    retval;
    Ip_tag    tags[7];
#ifdef IPCOM_USE_INET6
    char str[IP_INET6_ADDRSTRLEN];
    char str2[IP_INET6_ADDRSTRLEN];
#else
    char str[16], str2[16];
#endif

    /* Print all SAs. */
    ipcom_printf("SA - Security Associations:"IP_LF);

    sa_next.arg.next.spi_n = 0;  /* get first SA. */

    for (num = 0;;)
    {
        ipcom_memcpy(&sa_next.sa, &sa_next.arg.next, sizeof(Ipipsec_sa_handle));
        sa_next.arg.next.direction = IPIPSEC_SADIR_ANY;
        sa_next.arg.next.protocol  = 0;
        sa_next.arg.next.domain    = 0;

        /* Get first/next Selector */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_NEXT_SA, &sa_next) < 0)
        {
            ipcom_printf("ipsecctrl: ipcom_socketioctl(NEXT_SA) failed (%s)"IP_LF,
                         ipcom_strerror(ipcom_errno));
            return -1;
        }

        if (sa_next.arg.next.spi_n == 0)
            break;

        /* Get additional information. */
        tags[0] = IPIPSEC_SACTRLT_GET_AuthAlg;
        tags[2] = IPIPSEC_SACTRLT_GET_EncAlg;
        tags[4] = IP_TAG_END;
        retval = ipipsec_sactrl(&sa_next.arg.next, tags);
        if (retval != IPCOM_SUCCESS)
        {
            ipcom_printf("ipsecctrl: ipipsec_sactrl(info) failed, error = %d"IP_LF, retval);
            return -1;
        }
        ipcom_printf("[%2d] ::  %-6s %s spi=0x%-5lx src=%-17s dst=%-17s auth=%s",
                     ++num,
                     sa_next.arg.next.direction == IPIPSEC_SADIR_INPUT ? "input" :
                     sa_next.arg.next.direction == IPIPSEC_SADIR_OUTPUT ? "output" : "any",
                     ipcom_ipproto_name(sa_next.arg.next.protocol),
                     ip_ntohl(sa_next.arg.next.spi_n),
                     ipcom_inet_ntop(sa_next.arg.next.domain, &sa_next.arg.next.src, str, sizeof(str)),
                     ipcom_inet_ntop(sa_next.arg.next.domain, &sa_next.arg.next.dst, str2, sizeof(str2)),
                     mib_AuthAlg[tags[1]]);


        if (sa_next.arg.next.protocol == IP_IPPROTO_ESP)
            ipcom_printf(" enc=%s", mib_EncAlg[tags[3]]);
        if (sa_next.arg.next.priority != 0)
            ipcom_printf(" pri=%d", (int)sa_next.arg.next.priority);
        if (sa_next.arg.next.dscp != 0)
            ipcom_printf(" DSCP=%d", (int)sa_next.arg.next.dscp);
        ipcom_printf(IP_LF);

        /* Get grouped info */
        ipcom_memcpy(&sa_group.sa, &sa_next.arg.next, sizeof(Ipipsec_sa_handle));
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_Grouped, &sa_group) < 0)
        {
            ipcom_printf("ipsecctrl: ipcom_socketioctl(Grouped) failed (%s)"IP_LF,
                         ipcom_strerror(ipcom_errno));
            return -1;
        }
        if (sa_group.arg.group.spi_n) /*lint !e530 */
        {
            ipcom_printf("           ->   %s spi=0x%-5lx src=%-17s dst=%-17s",
                         ipcom_ipproto_name(sa_group.arg.group.protocol),
                         ip_ntohl(sa_group.arg.group.spi_n),
                         ipcom_inet_ntop(sa_next.arg.next.domain, &sa_next.arg.next.src, str, sizeof(str)),
                         ipcom_inet_ntop(sa_group.arg.group.domain, &sa_next.arg.next.dst, str2, sizeof(str2)));
            if (sa_group.arg.group.priority != 0)
                ipcom_printf(" pri=%d", (int)sa_group.arg.group.priority);
            if (sa_group.arg.group.dscp != 0)
                ipcom_printf(" DSCP=%d", (int)sa_group.arg.group.dscp);
            ipcom_printf(IP_LF);
        }
    }

    ipcom_printf("Total of %d SAs."IP_LF, num);
    return 0;
}
/*
 *===========================================================================
 *                         ipsecctrl_flows
 *===========================================================================
 */
static int
ipsecctrl_flows(Argvars *vars)
{
    Ipipsec_ctrl_selector   sel;
    char   srcport[32], dstport[32];
    int    num = 0;
    char   str[IP_INET6_ADDRSTRLEN];
    char   str2[IP_INET6_ADDRSTRLEN+1];

    ipcom_printf("Flows:"IP_LF);

    sel.Index = 0;  /* get first selector. */

    /* Get and print the Selectors in MIB format. */
    for (;;)
    {
        /* Get first/next Selector */
        if (ipcom_socketioctl(vars->fd, IP_SIOCXIPSEC_SELECTOR, &sel) < 0)
        {
            ipcom_printf("ipsecctrl: ipcom_socketioctl(SELECTOR) failed (%s)"IP_LF,
                         ipcom_strerror(ipcom_errno));
            return -1;
        }

        if (sel.Index == 0)
            break;

        /* Print selector aka flow: */
        num++;
        ipcom_printf("[%ld] :: %s ", sel.Index, FLOW_TYPE(sel.flowtype));

        if (sel.flowpri != 0)
            ipcom_printf("pri=%d ", (signed char)sel.flowpri);

        /* source */
        if (sel.LocalPort != sel.LocalMaxPort)
            ipcom_sprintf(srcport, "%d-%d",
                          (int)ip_ntohs(sel.LocalPort), (int)ip_ntohs(sel.LocalMaxPort));
        else if (sel.LocalPort == 0)
            ipcom_strcpy(srcport, "any");
        else
            ipcom_sprintf(srcport, "%d", (int)ip_ntohs(sel.LocalPort));
        if (ipcom_memcmp(&sel.LocalId, &sel.LocalMaxId, sizeof(union Ip_in_addr_union)))
            ipcom_sprintf(str2, "-%s",
                          ipcom_inet_ntop(sel.domain, &sel.LocalMaxId, str, sizeof(str)));
        else
            *str2 = '\0';
        ipcom_printf("src=%s%s/%d:%s ",
                     ipcom_inet_ntop(sel.domain, &sel.LocalId, str, sizeof(str)),
                     str2,
                     ipipsec_addrmasklen(sel.domain, (Ip_u8 *)&sel.local_mask),
                     srcport);

        /* destination */
        if (sel.RemotePort != sel.RemoteMaxPort)
            ipcom_sprintf(dstport, "%d-%d",
                          (int)ip_ntohs(sel.RemotePort), (int)ip_ntohs(sel.RemoteMaxPort));
        else if (sel.RemotePort == 0)
            ipcom_strcpy(dstport, "any");
        else
            ipcom_sprintf(dstport, "%d", (int)ip_ntohs(sel.RemotePort));
        if (ipcom_memcmp(&sel.RemoteId, &sel.RemoteMaxId, sizeof(union Ip_in_addr_union)))
            ipcom_sprintf(str2, "-%s",
                          ipcom_inet_ntop(sel.domain, &sel.RemoteMaxId, str, sizeof(str)));
        else
            *str2 = '\0';
        ipcom_printf("dst=%s%s/%d:%s ",
                     ipcom_inet_ntop(sel.domain, &sel.RemoteId, str, sizeof(str)),
                     str2,
                     ipipsec_addrmasklen(sel.domain, (Ip_u8 *)&sel.remote_mask),
                     dstport);

        ipcom_printf("%s", ipcom_ipproto_name(sel.Protocol));
        switch (sel.Protocol)
        {
        case IP_IPPROTO_ICMP:
        case IP_IPPROTO_ICMPV6:
            if (sel.ports[0] || sel.ports[1])
                ipcom_printf(" type=%d code=%d", sel.ports[0], sel.ports[1]);
            break;
        case IP_IPPROTO_MH:
            if (sel.ports[0])
                ipcom_printf(" type=%d", sel.ports[0]);
            break;
        default:
            break;
        }

        /* Print SA */
        if (sel.sa.domain)
        {
            if (sel.sa.domain == IPIPSEC_AF_BYPASS)
                ipcom_printf(" %s SA: %s",
                             sel.direction == IPIPSEC_SADIR_INPUT ? "<-" :
                             sel.direction == IPIPSEC_SADIR_OUTPUT ? "->" : "--",
                             IPSECSATYPENAME(sel.sa.satype));
            else
                ipcom_printf(" %s SA: %s spi=0x%lx src=%s dst=%s",
                             sel.direction == IPIPSEC_SADIR_INPUT ? "<-" :
                             sel.direction == IPIPSEC_SADIR_OUTPUT ? "->" : "--",
                             IPSECSATYPENAME(sel.sa.satype),
                             ip_ntohl(sel.sa.spi_n),
                             ipcom_inet_ntop(sel.sa.domain, &sel.sa.src, str, sizeof(str)),
                             ipcom_inet_ntop(sel.sa.domain, &sel.sa.dst, str2, sizeof(str2)));
        }
        else
            ipcom_printf(" %s SA: none",
                         sel.direction == IPIPSEC_SADIR_INPUT ? "<-" :
                         sel.direction == IPIPSEC_SADIR_OUTPUT ? "->" : "--");
        ipcom_printf(IP_LF);
    }
    ipcom_printf("Total of %d flows."IP_LF, num);

    return 0;
}
/*
 *===========================================================================
 *                      ipnet_ip_mib_cb_ipCidrRouteTable
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC Ip_bool
ipnet_ip_mib_cb_ipCidrRouteTable(Ipnet_route_entry *rt, Ipsnmp_route_walk *rwp)
{
    Ip_s32 tmp, lex, len = 0;

    /* Early exit if exact match was found previously */
    if(rwp->bestrt != IP_NULL &&
        (rwp->cmd == IPSNMP_MIB_COMMAND_GET
        || rwp->cmd == IPSNMP_MIB_COMMAND_TEST
        || rwp->cmd == IPSNMP_MIB_COMMAND_SET))
        goto exit;

    if(rt->netif == IP_NULL)
        goto exit;

    /* Do not use hidden entries */
    if(IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_X_HIDDEN))
        goto exit;

    /* Route must be usable */
    if(IP_BIT_ISFALSE(rt->hdr.flags, IPNET_RTF_UP))
        goto exit;

    /* Do not use link layer entries */
    if(IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_LLINFO))
        goto exit;

    /* Do not use loopback entries */
    if (IP_BIT_ISSET(rt->netif->ipcom.flags, IP_IFF_LOOPBACK))
        goto exit;

    ip_assert(rt->hdr.key != IP_NULL);

    /* Do not use multicast entries */
    if(IP_IN_CLASSD(*(Ip_u32 *)rt->hdr.key))
        goto exit;

    /* Do not use broadcast entries */
    if(ip_ntohl(*(Ip_u32 *)rt->hdr.key) == IP_INADDR_BROADCAST)
        goto exit;

    if(rwp->count_only == IP_TRUE)
    {
        count++;
        goto exit;
    }

    rwp->buf[0] = '\0';
    /* ipCidrRouteDest */
    if(ipcom_inet_ntop(IP_AF_INET, rt->hdr.key, &rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len) == IP_NULL)
        goto exit;
    len = ipcom_strlen(rwp->buf);
    if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, ".") < 0)
        goto exit;
    len = ipcom_strlen(rwp->buf);

    /* ipCidrRouteMask */
    if(rt->hdr.mask == IP_NULL)
    {
        if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, "255.255.255.255") < 0)
            goto exit;
    }
    else
    {
        if(ipcom_inet_ntop(IP_AF_INET, rt->hdr.mask, &rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len) == IP_NULL)
            goto exit;
    }
    len = ipcom_strlen(rwp->buf);
    if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, ".") < 0)
        goto exit;
    len = ipcom_strlen(rwp->buf);

    /* ipCidrRouteTos */
    tmp = 0;
    if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, "%d.", (int)tmp) < 0)
        goto exit;
    len = ipcom_strlen(rwp->buf);

    /* ipCidrRouteNextHop */
    if(IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_GATEWAY))
    {
        struct Ip_sockaddr_in *sa;

        ip_assert(rt->gateway != IP_NULL);
        sa = (struct Ip_sockaddr_in *)rt->gateway;
        if(ipcom_inet_ntop(IP_AF_INET, &sa->sin_addr.s_addr, &rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID - len) == IP_NULL)
            goto exit;
    }
    else
    {
        if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, "0.0.0.0") < 0)
            goto exit;
    }
    len = ipcom_strlen(rwp->buf);
    if(ipcom_snprintf(&rwp->buf[len], IPSNMP_CONFIG_MAX_OBJECT_ID-len, ".") < 0)
        goto exit;
    len = ipcom_strlen(rwp->buf);
    if(len)
        rwp->buf[len-1] = '\0';

    lex = ipsnmp_util_lexcmp_oid(rwp->buf, rwp->id);
    if(rwp->cmd == IPSNMP_MIB_COMMAND_NEXT)
    {
        if(lex > 0)
        {
            if(rwp->bestrt == IP_NULL || ipsnmp_util_lexcmp_oid(rwp->buf, rwp->best) < 0)
            {
                ipcom_strcpy(rwp->best, rwp->buf);
                rwp->bestrt = rt;
            }
        }
    }
    else
    {
        if(lex == 0)
        {
            ipcom_strcpy(rwp->best, rwp->buf);
            rwp->bestrt = rt;
        }
    }

exit:
    /* Do not delete the entry */
    return IP_FALSE;
}