/*
 *===========================================================================
 *                    ipnet_sysctl_route_add_addr
 *===========================================================================
 * Description: Adds an address in route table format.
 * Parameters:  buf - the buffer to where the address should be written.
 *              domain - the domain this address is part of.
 *              rt_addr - a address or mask from the route table.
 * Returns:     The address to where the next data should be written in
 *              the buffer.
 *
 */
IP_STATIC void *
ipnet_sysctl_route_add_addr(void *buf, int domain, void *rt_addr)
{
    union Ip_sockaddr_union *addr;

    addr = (union Ip_sockaddr_union *) buf;
#ifdef IPCOM_USE_INET
    if (domain == IP_AF_INET)
    {
        struct Ipnet_ipv4_key *key = (struct Ipnet_ipv4_key *) rt_addr;

        ipcom_memset(addr, 0, sizeof(struct Ip_sockaddr_in));
        addr->sin.sin_family = IP_AF_INET;
        IPCOM_SA_LEN_SET(&addr->sin, sizeof(struct Ip_sockaddr_in));
        ipcom_memcpy(&addr->sin.sin_addr, &key->addr, sizeof(addr->sin.sin_addr));
    }
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
    if (domain == IP_AF_INET6)
    {
        struct Ipnet_ipv6_key *key = (struct Ipnet_ipv6_key *) rt_addr;

        ipcom_memset(addr, 0, sizeof(struct Ip_sockaddr_in6));
        addr->sin6.sin6_family = IP_AF_INET6;
        IPCOM_SA_LEN_SET(&addr->sin6, sizeof(struct Ip_sockaddr_in6));
        ipcom_memcpy(&addr->sin6.sin6_scope_id, &key->scope_id, sizeof(addr->sin6.sin6_scope_id));
        ipcom_memcpy(&addr->sin6.sin6_addr, &key->addr, sizeof(addr->sin6.sin6_addr));
    }
#endif /* IPCOM_USE_INET6 */

    return (Ip_u8 *) buf + IPCOM_SA_LEN_GET(&addr->sa);
}
IP_STATIC void *
ipnet_sysctl_if_add_ip4_addr(void *buf, Ip_u32 addr_n)
{
    struct Ip_sockaddr_in *in = (struct Ip_sockaddr_in *) buf;

    ipcom_memset(in, 0, sizeof(struct Ip_sockaddr_in));
    in->sin_family = IP_AF_INET;
    IPCOM_SA_LEN_SET(in, sizeof(struct Ip_sockaddr_in));
    ipcom_memcpy(&in->sin_addr, &addr_n, sizeof(in->sin_addr));

    return in + 1;
}
/*
 *===========================================================================
 *                    ipnet_sysctl_if_add_ip6_addr
 *===========================================================================
 * Description: Adds a IPv6 socket address to the buffer.
 * Parameters:  buf - the buffer where the socket address should be written.
 *              addr - the address in network byte order.
 *              is_scoped - IP_TRUE if the address or mask is scoped.
 *              scope_id - The scope_id to use if the mask/address is scoped.
 * Returns:     Pointer to where next address should be written.
 *
 */
IP_STATIC void *
ipnet_sysctl_if_add_ip6_addr(void *buf,
                             IP_CONST struct Ip_in6_addr *addr,
                             Ip_bool is_scoped,
                             Ip_u32 scope_id)
{
    struct Ip_sockaddr_in6 *in6 = (struct Ip_sockaddr_in6 *) buf;

    ipcom_memset(in6, 0, sizeof(struct Ip_sockaddr_in6));
    in6->sin6_family   = IP_AF_INET6;
    IPCOM_SA_LEN_SET(in6, sizeof(struct Ip_sockaddr_in6));
    in6->sin6_scope_id = (is_scoped ? scope_id : 0);
    IPNET_IP6_SET_ADDR(&in6->sin6_addr, addr);

    return in6 + 1;
}
/*
 *===========================================================================
 *                    ipnet_loopback_ioctl
 *===========================================================================
 * Description: IO control. Accept join/leave from multicast groups.
 * Parameters:  netif - The network interface on which to perform the operation.
 *              command - The command to perform.
 *              data - Data associated with the command.
 * Returns:     0 = success, 0< = error code.
 *
 */
IP_STATIC int
ipnet_loopback_ioctl(Ipnet_netif *netif, Ip_u32 command, void *data)
{
    struct Ip_ifreq *ifreq = data;
    int              ret = 0;

    switch (command)
    {
    case IP_SIOCXOPEN:
#ifdef IP_PORT_LKM
        /* Call linux loopback device */
        ipnet_if_drv_ioctl(netif, IP_SIOCXOPEN, IP_NULL);
#endif /* IP_PORT_LKM */
        ipnet_kioevent(netif, IP_EIOXRUNNING, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
        ipnet_kioevent(netif, IP_EIOXUP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
        break;

    case IP_SIOCXCLOSE:
#ifdef IP_PORT_LKM
        /* Call linux loopback device */
        ipnet_if_drv_ioctl(netif, IP_SIOCXCLOSE, IP_NULL);
#endif /* IP_PORT_LKM */
        ipnet_kioevent(netif, IP_EIOXDOWN, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
        ipnet_kioevent(netif, IP_EIOXSTOP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
        break;

    case IP_SIOCGIFHWADDR:
        /* Switch to our if type */
        ifreq->ip_ifr_addr.sa_family = netif->ipcom.type;
        /* Return */
        break;

    case IP_SIOCGIFLLADDR:
        ifreq->ip_ifr_addr.sa_family = IP_AF_LINK;
        IPCOM_SA_LEN_SET(&ifreq->ip_ifr_addr, 0);
        break;

    case IP_SIOCIFDESTROY:
        if (ipcom_strcmp(netif->ipcom.name, IPNET_IF_LOOPBACK_NAME) == 0)
            ret = -IP_ERRNO_EPERM;
        else
        {
            ret = ipcom_if_detach(&netif->ipcom);
            if (ret >= 0)
                ret = ipcom_if_free(&netif->ipcom);
        }
        break;

#ifdef IPCOM_USE_INET
    case IP_SIOCXADDMULTI_IN:
    case IP_SIOCXDELMULTI_IN:
#endif
#ifdef IPCOM_USE_INET6
    case IP_SIOCXADDMULTI_IN6:
    case IP_SIOCXDELMULTI_IN6:
#endif
        break;
    default:
        return -IP_ERRNO_EINVAL;
    }
    return ret;
}
/*
 *===========================================================================
 *                    ipnet_sysctl_route_dump_cb
 *===========================================================================
 * Description: Checks if this entry should be added and adds it if it should.
 * Parameters:  rt - the route entry to add
 *              data - callback data.
 * Returns:     IP_FALSE (the entry should never be deleted).
 *
 */
IP_STATIC Ip_bool
ipnet_sysctl_route_dump_cb(Ipnet_route_entry *rt, Ipnet_sysctl_route_data *data)
{
    struct Ipnet_rt_msghdr *rt_msg;
    Ip_u16                  len;

    if (rt->next)
        (void)ipnet_sysctl_route_dump_cb(rt->next, data);

    if (rt->narrow)
        (void) ipnet_sysctl_route_dump_cb(rt->narrow, data);

    if ((rt->hdr.flags & data->flags) != data->flags)
        return IP_FALSE;

    /* Do not list the 255.255.255.255, 224.0.0.0/4 or IPv6 multicast routes. */
    if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_X_MCAST_RO | IPNET_RTF_X_BCAST_RO | IPNET_RTF_X_HIDDEN))
          return IP_FALSE;

    len = ipnet_sysctl_route_dump_elem_len(rt);
    data->bytes_written += len;

    if (data->bytes_written > data->buf_len)
    {
        /* Buffer to small */
        if (data->buf != IP_NULL)
            data->soerrno = -IP_ERRNO_ENOMEM;
        return IP_FALSE;
    }

    rt_msg = (struct Ipnet_rt_msghdr *) data->buf;
    data->buf = (Ip_u8 *) data->buf + sizeof(struct Ipnet_rt_msghdr);

    ipcom_memset(rt_msg, 0, sizeof(struct Ipnet_rt_msghdr));
    rt_msg->rtm_msglen  = len;
    rt_msg->rtm_version = IPNET_RTM_VERSION;
    rt_msg->rtm_type    = IPNET_RTM_GET;
    rt_msg->rtm_index   = rt->netif ? rt->netif->ipcom.ifindex : 0;
    rt_msg->rtm_table   = data->table;
    rt_msg->rtm_flags   = rt->hdr.flags;
    rt_msg->rtm_use     = rt->use;
    rt_msg->rtm_rmx     = rt->metrics;

    /* add destination address */
    IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_DST);
    data->buf = ipnet_sysctl_route_add_addr(data->buf, IPNET_ROUTE_GET_FAMILY(rt->head), rt->hdr.key);

    if (rt->gateway)
    {
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_GATEWAY);
        ipcom_memcpy(data->buf, rt->gateway, IPCOM_SA_LEN_GET(rt->gateway));
        data->buf = (Ip_u8 *) data->buf + IPCOM_SA_LEN_GET(rt->gateway);
    }
    else if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_CLONING))
    {
        struct Ip_sockaddr_dl  *dl = data->buf;
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_GATEWAY);
        ipcom_memset(data->buf, 0, sizeof(struct Ip_sockaddr_dl));
        IPCOM_SA_LEN_SET(dl, sizeof(struct Ip_sockaddr_dl));
        dl->sdl_family = IP_AF_LINK;
        ip_assert(rt->netif != IP_NULL);
        if (rt->netif != IP_NULL)
        {
            dl->sdl_index  = (Ip_u16)rt->netif->ipcom.ifindex;
            dl->sdl_type   = (Ip_u8)rt->netif->ipcom.type;
        }
        data->buf = (Ip_u8 *) data->buf + sizeof(struct Ip_sockaddr_dl);
    }

    if (rt->hdr.mask)
    {
        /* add destination mask */
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_NETMASK);
        data->buf = ipnet_sysctl_route_add_addr(data->buf, IPNET_ROUTE_GET_FAMILY(rt->head), rt->hdr.mask);
    }

    ip_assert((Ip_u16)((Ip_ptrdiff_t) data->buf - (Ip_ptrdiff_t) rt_msg) == len);

    return IP_FALSE;
}
/*
 *===========================================================================
 *                         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;
}
예제 #7
0
/*
 *===========================================================================
 *                    ipnet_rtnetlink_route_delroute_family
 *===========================================================================
 * Description: Delete a route via NETLINK.
 * Parameters:  data - Message payload
 *              len  - Length of message.
 *              nlh  - NETLINK message header
 *              arg  - Reference to route attributes.
 *
 * Returns:     0 - Success
 *             <0 - Failure
 *
 */
IP_GLOBAL int
ipnet_rtnetlink_route_delroute_family(Ipnet_netlink_mem_t   *mem,
                                      struct Ip_nlmsghdr    *nlh,
                                      struct Ip_rtattr      **rta,
                                      int                   family)
{
    int                      ret = -1;
    Ip_u16                   vr;
    Ip_u32                   table;
    Ip_u32                   ifindex = 0;
    Ip_bool                  is_multipath_route = IP_FALSE;
    Ip_size_t                mpa_size = 0;
    struct Ip_rtnexthop      *nh = IP_NULL;
    struct Ip_sockaddr       *gw = IP_NULL;
    void                     *mask = IP_NULL;
    struct Ip_rtmsg          *rtm = IP_NLMSG_DATA(nlh);

    union Ip_sockaddr_union  ugw;
    Ipnet_rtnetlink_key_t    ukey;

#ifdef IPMPLS
    struct Ip_sockaddr_mpls  gw_mpls;
#endif /* IPMPLS */

    table = IPCOM_ROUTE_TABLE_GET(rtm->rtm_table);

#ifdef IPNET_USE_ROUTE_TABLE_NAMES
    if (rta[IP_RTA_TABLE_NAME -1])
    {
        if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_RTA_TABLE_NAME - 1]),
                                               &vr,
                                               &table) < 0)
            return -IP_ERRNO_ESRCH;
    }
    else
#endif
    {
        if (mem->vr == IPCOM_VR_ANY)
            return -IP_ERRNO_EINVAL;

        vr = ipnet_rtnetlink_vr(rta[IP_RTA_VR - 1], mem->vr);

        if (rta[IP_RTA_TABLE - 1])
            table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_TABLE - 1]));
    }

    if (rta[IP_RTA_OIF -1])
    {
        if (rta[IP_RTA_OIF-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
			return -IP_ERRNO_EINVAL;

        ifindex = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_OIF-1]));
     }

    if (rta[IP_RTA_MULTIPATH -1])
    {
        is_multipath_route = IP_TRUE;
        nh = IP_RTA_DATA(rta[IP_RTA_MULTIPATH-1]);
        mpa_size = IP_RTA_PAYLOAD(rta[IP_RTA_MULTIPATH-1]);

        if ((nh == IP_NULL) || (mpa_size == 0))
            return -IP_ERRNO_EINVAL;
    }

    if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, ifindex, &mask, rtm->rtm_dst_len, rta[IP_RTA_DST-1])) < 0)
        return ret;

    if ((ret = ipnet_rtnetlink_route_gw_setup(family, ifindex, &ugw, &gw, rta[IP_RTA_GATEWAY-1])) < 0)
        return ret;

    if (rta[IP_RTA_GATEWAY-1])
        gw = &ugw.sa;

#ifdef IPMPLS
    if (rta[IP_RTA_NH_PROTO-1])
    {
        Ip_u32 nh_type;

        /* IPNET MPLS pseudo-wire route */
        if (rta[IP_RTA_NH_PROTO-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        if (rta[IP_RTA_NH_PROTO_DATA-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        ipcom_memcpy(&nh_type, IP_RTA_DATA(rta[IP_RTA_NH_PROTO-1]), sizeof(Ip_u32));

        if (nh_type !=  IPNET_ETH_P_MPLS_UNICAST)
            return -IP_ERRNO_EINVAL;

        ipcom_memset(&gw_mpls, 0, sizeof(gw_mpls));
        gw_mpls.smpls_family = IP_AF_MPLS;
        IPCOM_SA_LEN_SET(&gw_mpls, sizeof (struct Ip_sockaddr_mpls));
        ipcom_memcpy(&gw_mpls.smpls_key, IP_RTA_DATA(rta[IP_RTA_NH_PROTO_DATA-1]), sizeof(Ip_u32));

        gw = (struct Ip_sockaddr *)&gw_mpls;
    }
#endif

    while(1)
    {
        if (is_multipath_route)
        {
            ifindex = nh->rtnh_ifindex;

            if (nh->rtnh_len > sizeof(struct Ip_rtnexthop))
            {
                /* Multihop route has gateway */
                if ((ret = ipnet_rtnetlink_route_gw_setup(family, ifindex, &ugw, &gw, IP_RTNH_DATA(nh))) < 0)
                    return ret;
            }
        }

        /* Delete the route */
        ret = ipnet_route_delete2(family,
                                  vr,
                                  table,
                                  &ukey,
                                  mask,
                                  gw,
                                  ifindex,
                                  0,
                                  0,
                                  0);

        if (is_multipath_route)
        {
            mpa_size -= IP_RTNH_ALIGN(nh->rtnh_len);
            gw = IP_NULL;

            if (mpa_size > 0)
                nh = IP_RTNH_NEXT(nh);
            else
                break;
        }
        else
            break;

    }

    return is_multipath_route ? 0 : ret;
}
예제 #8
0
/*
 *===========================================================================
 *                    ipnet_rtnetlink_route_newroute_family
 *===========================================================================
 * Description: Add a new rotue via NETLINK.
 * Parameters:  data - Message payload
 *              len  - Length of message.
 *              nlh  - NETLINK message header
 *              arg  - Reference to route attributes.
 *
 * Returns:     0 - Success
 *             <0 - Failure
 *
 */
IP_GLOBAL int
ipnet_rtnetlink_route_newroute_family(Ipnet_netlink_mem_t   *mem,
                                      struct Ip_nlmsghdr    *nlh,
                                      struct Ip_rtattr      **rta,
                                      int                   family)
{
    Ipnet_netif                   *netif = IP_NULL;
    Ipnet_ppp_peer                *p = IP_NULL;
    int                           ret  = -1;
    Ip_u16                        vr;
    Ip_u32                        table;
    Ip_bool                       is_multipath_route = IP_FALSE;
    Ip_ssize_t                    mpa_size = 0;
    struct Ip_rtnexthop           *nh = IP_NULL;
    struct Ipnet_rt_metrics       metrics;
    struct Ip_sockaddr            *gw   = IP_NULL;
    void                          *mask = IP_NULL;
    struct Ip_sockaddr_dl         gw_dl;
    struct Ipnet_route_add_param  param;
    struct Ip_rtmsg               *rtm   = IP_NLMSG_DATA(nlh);

    union Ip_sockaddr_union       ugw;
    Ipnet_rtnetlink_key_t         ukey;

#ifdef IPMPLS
    struct Ip_sockaddr_mpls       gw_mpls;
#endif /* IPMPLS */
#ifdef IPNET_USE_ROUTE_COOKIES
    Ipnet_rt_cookie               cookie;
#endif /* IPNET_USE_ROUTE_COOKIES */

    table = IPCOM_ROUTE_TABLE_GET(rtm->rtm_table);

    /* Prepare route parameters */
    ipcom_memset(&param, 0, sizeof(param));
    ipcom_memset(&metrics,0,sizeof(metrics));
    param.flags = IPNET_RTF_STATIC | IPNET_RTF_UP | IPNET_RTF_DONE;

#ifdef IPNET_USE_ROUTE_TABLE_NAMES
    if (rta[IP_RTA_TABLE_NAME -1])
    {
        if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_RTA_TABLE_NAME - 1]),
                                               &vr,
                                               &table) < 0)
            return -IP_ERRNO_ESRCH;
    }
    else
#endif
    {
        if (mem->vr == IPCOM_VR_ANY)
            return -IP_ERRNO_EINVAL;

        vr = ipnet_rtnetlink_vr(rta[IP_RTA_VR - 1], mem->vr);

        if (rta[IP_RTA_TABLE - 1])
            table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_TABLE - 1]));
    }

    if (rta[IP_RTA_OIF -1])
    {
        int ifindex = 0;

        if (rta[IP_RTA_OIF-1]->rta_len != IP_RTA_LENGTH(sizeof(int)))
			return -IP_ERRNO_EINVAL;

        ifindex = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_OIF-1]));
        netif = ipnet_if_indextonetif(vr, ifindex);
        if (netif)
            p = netif->private_data;
    }

    if (rta[IP_RTA_MULTIPATH -1])
    {
        is_multipath_route = IP_TRUE;
        nh = IP_RTA_DATA(rta[IP_RTA_MULTIPATH-1]);
        mpa_size = IP_RTA_PAYLOAD(rta[IP_RTA_MULTIPATH-1]);

        if ((nh == IP_NULL) || (mpa_size == 0))
            return -IP_ERRNO_EINVAL;
    }

    if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, &mask, rtm->rtm_dst_len, rta[IP_RTA_DST-1])) < 0)
        return ret;

    if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, rta[IP_RTA_GATEWAY-1])) < 0)
        return ret;

#ifdef IPCOM_USE_INET
    if (family == IP_AF_INET)
    {
        if (gw != IP_NULL)
        {
            /* Check if it is a pure gateway or not */
            if (p == IP_NULL || p->peer4.s_addr != ugw.sin.sin_addr.s_addr)
                param.flags |= IPNET_RTF_GATEWAY;
        }
    }
    else
#endif
#ifdef IPCOM_USE_INET6
    if (family == IP_AF_INET6)
    {
        if (gw != IP_NULL)
        {
            /* Check if it is a pure gateway or not */
            if (p == IP_NULL || !IP_IN6_ARE_ADDR_EQUAL(&ugw.sin6.sin6_addr, &p->peer6))
                param.flags |= IPNET_RTF_GATEWAY;
        }
    }
    else
#endif
    {
        return -IP_ERRNO_EAFNOSUPPORT;
    }

    if (!mask)
        param.flags |= IPNET_RTF_HOST;

    if (rtm->rtm_type == IP_RTN_PROXY)
    {
        /* This is a proxy arp network route */
        IP_BIT_CLR(param.flags, IPNET_RTF_CLONING);
        IP_BIT_SET(param.flags, IPNET_RTF_PROTO2);

        if (rta[IP_RTA_PROXY_ARP_LLADDR - 1])
        {
            /* This is a network proxy arp with specific lladdr */
            if (netif == IP_NULL)
               return -IP_ERRNO_EINVAL;

            ipcom_memset(&gw_dl, 0, sizeof(struct Ip_sockaddr_dl));

            IPCOM_SA_LEN_SET(&gw_dl, sizeof(struct Ip_sockaddr_dl));
            gw_dl.sdl_family = IP_AF_LINK;
            gw_dl.sdl_index  = (Ip_u16)netif->ipcom.ifindex;
            gw_dl.sdl_alen   = (Ip_u8) netif->ipcom.link_addr_size;
            gw_dl.sdl_type   = (Ip_u8) netif->ipcom.type;

            ipcom_memcpy(IP_SOCKADDR_DL_LLADDR(&gw_dl),
                         IP_RTA_DATA(rta[IP_RTA_PROXY_ARP_LLADDR - 1]),
                         IPNET_ETH_ADDR_SIZE);

            gw = (struct Ip_sockaddr *) &gw_dl;
            IP_BIT_SET(param.flags, IPNET_RTF_LLINFO);
        }
    }
    else if (rtm->rtm_type == IP_RTN_PROHIBIT)
        IP_BIT_SET(param.flags, IPNET_RTF_REJECT);
    else if (rtm->rtm_type == IP_RTN_THROW)
        IP_BIT_SET(param.flags, IPNET_RTF_SKIP);
    else if (rtm->rtm_type == IP_RTN_UNREACHABLE)
    {
        if (netif == IP_NULL)
            netif = ipnet_loopback_get_netif(vr);
        IP_BIT_CLR(param.flags, IPNET_RTF_UP);
    }
    else if (rtm->rtm_type == IP_RTN_CLONE)
        IP_BIT_SET(param.flags, IPNET_RTF_CLONING);
    else if (rtm->rtm_type == IP_RTN_BLACKHOLE)
        IP_BIT_SET(param.flags, IPNET_RTF_BLACKHOLE);

#ifdef IPMPLS
    /* Check for IPNET MPLS pseudo-wire route */
    if (rta[IP_RTA_NH_PROTO-1])
    {
        Ip_u32 nh_type;

        if (rta[IP_RTA_NH_PROTO-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        if (rta[IP_RTA_NH_PROTO_DATA-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        ipcom_memcpy(&nh_type, IP_RTA_DATA(rta[IP_RTA_NH_PROTO-1]), sizeof(Ip_u32));

        if (nh_type !=  IPNET_ETH_P_MPLS_UNICAST)
            return -IP_ERRNO_EINVAL;

        ipcom_memset(&gw_mpls, 0, sizeof(gw_mpls));
        gw_mpls.smpls_family = IP_AF_MPLS;
        IPCOM_SA_LEN_SET(&gw_mpls, sizeof (struct Ip_sockaddr_mpls));

        ipcom_memcpy(&gw_mpls.smpls_key,
                     IP_RTA_DATA(rta[IP_RTA_NH_PROTO_DATA-1]),
                     sizeof(Ip_u32));

        IP_BIT_CLR(param.flags,IPNET_RTF_GATEWAY);
        gw = (struct Ip_sockaddr *)&gw_mpls;
    }
    else
    {
        /* Check if cloning flag shall be set */
        if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_HOST | IPNET_RTF_GATEWAY | IPNET_RTF_REJECT | IPNET_RTF_BLACKHOLE))
            IP_BIT_SET(param.flags, IPNET_RTF_CLONING);
    }
#endif

    if (rta[IP_RTA_METRICS-1])
    {
        int rlen = (int)IP_RTA_PAYLOAD(rta[IP_RTA_METRICS-1]);
        Ip_u32 dummy = 0;
        struct Ip_rtattr *rtax = (struct Ip_rtattr*)IP_RTA_DATA(rta[IP_RTA_METRICS-1]);

        metrics.rmx_expire = IPCOM_ADDR_INFINITE;
        param.metrics      = &metrics;

        for(;rlen > 0; rlen -= (int)rtax->rta_len,rtax = IP_RTA_NEXT(rtax,dummy))
        {
            switch (rtax->rta_type)
            {
            case IP_RTAX_MTU:
                ipcom_memcpy(&metrics.rmx_mtu, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            case IP_RTAX_RTT:
                ipcom_memcpy(&metrics.rmx_rtt, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            case IP_RTAX_RTTVAR:
                ipcom_memcpy(&metrics.rmx_rttvar, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            }
        }
    }

#ifdef IPNET_USE_ROUTE_COOKIES
    if (rta[IP_RTA_COOKIE-1])
    {
        if (IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1]) > sizeof(cookie))
            return -IP_ERRNO_EINVAL;

        ipcom_memset(&cookie, 0, sizeof(cookie));
        ipcom_memcpy(&cookie,
                     IP_RTA_DATA(rta[IP_RTA_COOKIE-1]),
                     IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1]));

        param.cookie = &cookie;
    }
#endif /* IPNET_USE_ROUTE_COOKIES */

    while (1)
    {
        if (is_multipath_route && (gw == IP_NULL))
        {
            netif                = ipnet_if_indextonetif(vr, nh->rtnh_ifindex);
            metrics.rmx_hopcount = nh->rtnh_hops;
            metrics.rmx_expire   = IPCOM_ADDR_INFINITE;
            param.metrics        = &metrics;

            if (nh->rtnh_len > sizeof(struct Ip_rtnexthop))
            {
                /* Multihop route has gateway */
                IP_BIT_CLR(param.flags, IPNET_RTF_CLONING);
                IP_BIT_SET(param.flags, IPNET_RTF_GATEWAY);

                if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, IP_RTNH_DATA(nh))) < 0)
                    return ret;
            }
            mpa_size -= IP_RTNH_ALIGN(nh->rtnh_len);
            if (mpa_size > 0)
                nh = IP_RTNH_NEXT(nh);
        }

        param.domain       = family;
        param.vr           = vr;
        param.table        = table;
        param.netif        = netif;
        param.key          = &ukey;
        param.netmask      = mask;
        param.gateway      = gw;
        param.pid          = mem->pid;
        param.seq          = nlh->nlmsg_seq;
        param.no_ref_count = IP_TRUE;

        /* Try to add the route */
        if (netif)
            IPNET_IF_LOCK(netif);

        ret = ipnet_route_add(&param);

        if (netif != IP_NULL)
            IPNET_IF_UNLOCK(netif);

        if (is_multipath_route)
        {
            gw = IP_NULL;

            if (ret < 0 && ret != -IP_ERRNO_EEXIST)
                goto rollback;

            if (mpa_size == 0)
            {
                ret = 0;
                break;
            }
            else if (mpa_size < 0)
            {
                /* Malformed packet */
                ret = -IP_ERRNO_EINVAL;
                goto rollback;
            }
        }
        else
            break;
    }

    return ret;

rollback:
    (void)ipnet_rtnetlink_route_delroute_family(mem, nlh, rta, family);
    return ret;
}
/*
 *===========================================================================
 *                    ipnet_rtnetlink_neigh_newneigh_family
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_GLOBAL int
ipnet_rtnetlink_neigh_newneigh_family(Ipnet_netlink_mem_t  *mem,
                                      struct Ip_nlmsghdr   *nlmsg,
                                      struct Ip_rtattr     **rta,
                                      int                  family)
{
    Ipnet_netif                  *netif;
    Ipnet_route_entry            *rt;
    int                          nd_state;
    int                          ret = -IP_ERRNO_EAFNOSUPPORT;
    Ip_u16                       vr;
    Ip_u32                       table = IPCOM_ROUTE_TABLE_DEFAULT;
    struct Ip_sockaddr_dl        dl;
    struct Ip_ndmsg              *ndm = IP_NLMSG_DATA(nlmsg);
    void                         *link_addr = IP_NULL;
    Ipnet_rtnetlink_key_t        ukey;

     /* Destination address required */
    if (rta[IP_NDA_DST-1] == IP_NULL)
        return -IP_ERRNO_EINVAL;

#ifdef IPNET_USE_ROUTE_TABLE_NAMES
    if (rta[IP_NDA_TABLE_NAME -1])
    {
        /* Extract router and table from name */
        if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_NDA_TABLE_NAME - 1]),
                                               &vr,
                                               &table) < 0)
            return -IP_ERRNO_ESRCH;
    }
    else
#endif /* IPNET_USE_ROUTE_TABLE_NAMES */
    {
        if (mem->vr == IPCOM_VR_ANY)
            return -IP_ERRNO_EINVAL;

        vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr);

        if (rta[IP_NDA_TABLE - 1])
            table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_NDA_TABLE - 1]));
    }

    netif = ipnet_if_indextonetif(vr, ndm->ndm_ifindex);

    if (netif && (netif->vr_index != vr))
         return -IP_ERRNO_EINVAL;

    if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, IP_NULL, -1, rta[IP_NDA_DST-1])) < 0)
        return ret;

    if (rta[IP_NDA_LLADDR-1])
        /* Neighbors link layer address */
        link_addr = IP_RTA_DATA(rta[IP_NDA_LLADDR-1]);
    else if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_PROXY))
    {
        /* Proxy ARP entry. Use Link layer address of local netif */
        if (netif == IP_NULL)
            return -IP_ERRNO_EINVAL;

        link_addr = netif->ipcom.link_addr;
    }

    /* Check if neighbor route exists */
    ret = ipnet_route_lookup(family,
                             vr,
                             table,
                             IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE,
                             &ukey,
                             netif ? netif->ipcom.ifindex : 0,
                             netif ? netif->ipcom.ifindex : 0,
                             &rt);

    if ((ret == IPNET_ROUTE_PERFECT_MATCH)
        && (rt->netif == netif)
        && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_HOST)
        && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_LLINFO))
    {
        if (rt->data == IP_NULL)
            return -IP_ERRNO_EINVAL;

        /* Get the neighbour state */
        nd_state = *(Ipnet_nd_state_t *)rt->data;

        if (((ndm->ndm_state == IP_NUD_INCOMPLETE)
             && (nd_state != IPNET_ND_UNINITIALIZED)
             && (nd_state != IPNET_ND_INCOMPLETE))
            || ((ndm->ndm_state == IP_NUD_DELAY)
                && (nd_state != IPNET_ND_STALE)))
        {
            return -IP_ERRNO_EINVAL;
        }
    }
    else
    {
        struct Ipnet_rt_metrics      metrics;
        struct Ipnet_route_add_param param;

        /* Create new neighbor entry */
        if (ndm->ndm_state == IP_NUD_DELAY)
            ndm->ndm_state = IP_NUD_INCOMPLETE;

        ipcom_memset(&param, 0, sizeof(struct Ipnet_route_add_param));
        ipcom_memset(&dl, 0, sizeof(struct Ip_sockaddr_dl));

        param.domain     = family;
        param.vr         = vr;
        param.table      = table;
        param.key        = &ukey;
        param.netif      = netif;
        param.flags      = IPNET_RTF_UP | IPNET_RTF_HOST | IPNET_RTF_LLINFO | IPNET_RTF_DONE;
        param.gateway    = (struct Ip_sockaddr*) &dl;

        if (ndm->ndm_state != IP_NUD_PERMANENT)
        {
            ipcom_memset(&metrics, 0, sizeof(metrics));
            /* The correct timeout will be set when the ND state is set,
               but must be != infinite for now */
            metrics.rmx_expire = 1;
            param.metrics = &metrics;
        }

        IPCOM_SA_LEN_SET(&dl, sizeof(struct Ip_sockaddr_dl));
        dl.sdl_family    = IP_AF_LINK;
        dl.sdl_index     = (Ip_u16)(netif ? netif->ipcom.ifindex : 0);
        dl.sdl_alen      = 6;
        dl.sdl_type      = IP_IFT_ETHER;

        if (link_addr)
            ipcom_memcpy(&dl.sdl_data[0], link_addr,dl.sdl_alen);

        if (netif)
            IPNET_IF_LOCK(netif);

        /* Add new neighbor */
        ret = ipnet_route_add(&param);

        if (netif)
            IPNET_IF_UNLOCK(netif);

        if (ret < 0)
            return ret;

        /* Get the new neighbor route entry */
        ret = ipnet_route_lookup(family,
                                 vr,
                                 table,
                                 IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE,
                                 &ukey,
                                 netif ? netif->ipcom.ifindex : 0,
                                 netif ? netif->ipcom.ifindex : 0,
                                 &rt);
        if (ret < 0)
            return ret;
    }

#ifdef IPNET_COMPENSATE_UNRELIABLE_NETLINK
    /* Set neighbor acknowledge bit */
    IP_BIT_SET(rt->hdr.flags, IPNET_RTF_X_NEIGH_ACK);
#endif

    if (IP_BIT_ISSET(ndm->ndm_flags,IP_NTF_PROXY))
        /* Proxy ARP entry */
        IP_BIT_SET(rt->hdr.flags, IPNET_RTF_PROTO2);

    if (IP_BIT_ISSET(ndm->ndm_state, IP_NUD_PERMANENT))
        ipnet_route_set_lifetime(rt, IPCOM_ADDR_INFINITE);

    nd_state = ipnet_rtnetlink_neigh_nud2nc(ndm->ndm_state);

#ifdef IPCOM_USE_INET
    if (family == IP_AF_INET)
        ret = ipnet_rtnetlink_ip4_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta);
    else
#endif
#ifdef IPCOM_USE_INET6
    if (family == IP_AF_INET6)
        ret = ipnet_rtnetlink_ip6_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta);
    else
#endif
        return -IP_ERRNO_EAFNOSUPPORT;

    return ret;
}