/*
 *===========================================================================
 *                    ipnet_loopback_get_frame_info
 *===========================================================================
 * Description: Extracts frame information from a packet sent on the loopback
 *              device.
 * Parameters:  netif - Loopback interface
 *              pkt - The packet to extract information from
 *              pkt_type - Will contain the packet type if != IP_NULL
 *              frame_type - Will contain the frame type if != IP_NULL
 *              src - Unused since the loopback does not have a link layer address.
 *              dst - Unused since the loopback does not use link layer address.
 * Returns:
 *
 */
IP_STATIC int
ipnet_loopback_get_frame_info(struct Ipnet_netif_struct *netif,
                              Ipcom_pkt *pkt,
                              Ip_u8 *pkt_type,
                              Ip_u16 *frame_type,
                              Ip_u8 *src,
                              Ip_u8 *dst)
{
    IPCOM_UNUSED_ARG(src);
    IPCOM_UNUSED_ARG(dst);

    if (frame_type != IP_NULL)
    {
        Ip_u8 network_type = pkt->data[pkt->start + netif->ipcom.link_hdr_size];

        if ((network_type & 0xf0) == 0x40)
            *frame_type = ip_htons(IP_IPPROTO_IPV4);
        else if ((network_type & 0xf0) == 0x60)
            *frame_type = ip_htons(IP_IPPROTO_IPV6);
        else
            return -IP_ERRNO_EINVAL;
    }

    if (pkt_type != IP_NULL)
        *pkt_type = IP_PACKET_HOST;

    return 0;
}
/*
 *===========================================================================
 *                    ipnet_gre_tmo
 *===========================================================================
 * Description: Reassembly timeout handler.
 * Parameters:  netif - A GRE tunnel interface.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_STATIC void
ipnet_gre_seq_tmo(Ipnet_netif *netif)
{
    Ipnet_pkt_gre *gre_hdr;
    Ipcom_pkt     *pkt;
    Ipnet_gre_t   *gre = netif->ipcom.pdrv;

    /* RFC 2890, chapter 2.2
       ...
       Under no circumstances should a packet wait more that
       OUTOFORDER_TIMER milliseconds in the buffer.  If a packet has been
       waiting that long, the receiver MUST immediately traverse the buffer
       in sorted order, decapsulating packets (and ignoring any sequence
       number gaps) until there are no more packets in the buffer that have
       been waiting longer than OUTOFORDER_TIMER milliseconds. The "last
       successfully decapsulated sequence number" should then be set to the
       last packet so decapsulated.
       ...
    */
    pkt = ipcom_pqueue_get_next(gre->reassembly_queue);
    ip_assert(pkt != IP_NULL);
    gre->recv_seqnum = IP_GET_NTOHL(&pkt->data[pkt->start]);

    do
    {
        pkt = ipcom_pqueue_remove_next(gre->reassembly_queue);
        /* Discard the sequence number option and make place for a GRE header */
        pkt->start += IPNET_GRE_OPT_SEQNUM_SIZE - IPNET_GRE_HDR_SIZE;

        gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
        gre_hdr->flags_reserved0_ver = 0;
        if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_IPV4))
            gre_hdr->protocol_type = ip_htons(0x0800);
        else if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_IPV6))
            gre_hdr->protocol_type = ip_htons(0x86DD);

        (void) ipnet_gre_input(netif, pkt);
        ++gre->recv_seqnum;
    } while (IP_NULL != (pkt = ipcom_pqueue_get_next(gre->reassembly_queue))
             && gre->recv_seqnum == IP_GET_NTOHL(&pkt->data[pkt->start]));

    if (pkt != IP_NULL)
    {
        Ip_u32 timeout = IPNET_PKT_GET_TIMEOUT_ABS(pkt);

        /* More out of order packets waiting, schedule a timeout of
           the time remaining until this packet sequence timer timeouts */
        (void) ipnet_timeout_schedule((timeout < ipnet->msec_now
                                       ? 0
                                       : timeout - ipnet->msec_now),
                                      (Ipnet_timeout_handler) ipnet_gre_seq_tmo,
                                      netif,
                                      &gre->reassembly_tmo);
    }
}
IP_GLOBAL int
ipnet_loopback_if_setup(Ipnet_netif *netif)
{
    int ret = 0;
#ifdef IPCOM_USE_INET6
    struct Ip_in6_addr lo_addr = IP_IN6ADDR_LOOPBACK_INIT;
    struct Ipnet_route_add_param param;
    struct Ipnet_ipv6_key        key;
    struct Ipnet_ipv6_key        mask;

    /* Add ::1 loopback address to interface */
    (void)ipnet_ip6_add_addr(netif,
                             &lo_addr,
                             IP_TRUE,
                             0xffffffff,
                             0xffffffff,
                             128);

    /* All packets matching ff02::/16 with no scope specified
       must be rejected */
    ipcom_memset(&key, 0, sizeof(key));
    ipcom_memset(&mask, 0, sizeof(mask));
    ipcom_memset(&param, 0, sizeof(param));

    key.addr.in6.addr16[0] = ip_htons(0xff02);
    ipnet_route_create_mask(&mask.addr, 16);

    param.domain  = IP_AF_INET6;
    param.key     = &key;
    param.netmask = &mask;
    param.flags   = IPNET_RTF_X_HIDDEN | IPNET_RTF_REJECT | IPNET_RTF_DONE;
    param.netif   = netif;
    param.vr      = netif->vr_index;
    param.table   = IPCOM_ROUTE_TABLE_DEFAULT;

    ret = ipnet_route_add(&param);

    if (ret < 0)
        IPCOM_LOG2(ERR, "Failed to setup routes on %s in vr %d",
                   netif->ipcom.name,
                   netif->vr_index);

#endif /* IPCOM_USE_INET6 */

#ifdef IPCOM_USE_INET
    /* Add 127.0.0.1 loopback address to interface */
    (void)ipnet_ip4_add_addr(netif,
                             ip_htonl(IP_INADDR_LOOPBACK),
                             ip_htonl(0xff000000),
                             IP_TRUE,
                             IPNET_ADDR_TYPE_UNICAST);
#endif /* IPCOM_USE_INET */

    return ret;
}
IP_STATIC int
ipnet_loopback_ip6_create_addr(Ipnet_netif *netif,
                               IP_CONST struct Ip_in6_addr* prefix,
                               struct Ip_in6_addr* in6_addr)
{
    (void) netif;

    in6_addr->in6.addr16[0] = prefix->in6.addr16[0];
    in6_addr->in6.addr16[1] = prefix->in6.addr16[1];
    in6_addr->in6.addr16[2] = prefix->in6.addr16[2];
    in6_addr->in6.addr16[3] = prefix->in6.addr16[3];
    in6_addr->in6.addr16[4] = 0;
    in6_addr->in6.addr16[5] = 0;
    in6_addr->in6.addr16[6] = 0;
    in6_addr->in6.addr16[7] = ip_htons(1);
    return 0;
}
/*
 *===========================================================================
 *                    ipcom_getservbyport
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_PUBLIC struct Ip_servent *
ipcom_getservbyport(int port, char *proto)
{
    static struct Ip_servent se;
    static char *s_aliases[3] = { IP_NULL, IP_NULL, IP_NULL };
    struct Ipcom_service *sp;

    sp = ipcom_find_service_byport((int)ip_ntohs(port), proto);
    if(sp == IP_NULL)
        return IP_NULL;

    se.s_name    = sp->name;
    s_aliases[0] = sp->alias1;
    s_aliases[1] = sp->alias2;
    se.s_aliases = s_aliases;
    se.s_port    = (int)ip_htons(sp->port);
    se.s_proto   = sp->proto;

    return &se;
}
IP_STATIC
IPCOM_PROCESS( ipcom_cmd_smptest_client_spawn )
{
    union
    {
        struct Ip_sockaddr          sa;
#ifdef IPCOM_USE_INET6
        struct Ip_sockaddr_in6      sin6;
#endif
        struct Ip_sockaddr_storage  ss;
        struct Ip_sockaddr_in       sin;
    } addr;
    Ip_fd socket;
    unsigned char *buf = 0;
    int portadd = spawn_number_client++;
    int opt_val = 1;
    int sec = SECONDS_CLIENT;
    int send = 0;
    int num_sends = 0;
    unsigned long num_bytes = 0;

    ipcom_proc_init();

    ipcom_memset( &addr.ss, 0, sizeof( addr.ss ) );
    ipcom_memcpy( &addr.sa, smp_opt_client.res->ai_addr, smp_opt_client.res->ai_addrlen );

    socket = ipcom_socket( smp_opt_client.res->ai_family, smp_opt_client.res->ai_socktype, smp_opt_client.res->ai_protocol );
    if (socket == IP_SOCKERR)
    {
        ipcom_printf("Failed to create socket for thread %d: %s"IP_LF, portadd, ipcom_strerror(ipcom_errno));
        return_client = 1;
        ipcom_sem_post( sem_wait_client );
        return;
    }

    if ( 0 != ipcom_setsockopt(socket, IP_SOL_SOCKET, IP_SO_REUSEPORT, &opt_val, sizeof (opt_val)) )
    {
        ipcom_printf("ipcom_setsockopt failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_client = 1;
        ipcom_sem_post( sem_wait_client );
        return;
    }

    if ( 0 != ipcom_socketioctl( socket, IP_X_SIOCSINTR, &sec ) )
    {
        ipcom_printf("ipcom_socketioctl failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_client = 1;
        ipcom_sem_post( sem_wait_client );
        return;
    }

    addr.sin.sin_port = ip_htons( smp_opt_client.port + portadd );   /* port is in the same place for IPv4 and IPv6 */
    if ( smp_opt_client.tcp )
    {
        if ( 0 != ipcom_connect( socket, &addr.sa, smp_opt_client.res->ai_addrlen ) )
        {
            ipcom_printf("Thread %d failed to connect: %s"IP_LF, portadd, ipcom_strerror(ipcom_errno));
            return_client = 1;
            ipcom_sem_post( sem_wait_client );
            return;
        }
        if ( verbose )
            ipcom_printf("Thread %d connected to port %d"IP_LF, portadd, smp_opt_client.port + portadd );
    }

    buf = ipcom_malloc( smp_opt_client.num_bytes );
    while ( 1 )
    {
        if ( smp_opt_client.tcp )
            send = ipcom_send( socket, buf, smp_opt_client.num_bytes, 0);
        else
            send = ipcom_sendto( socket, buf, smp_opt_client.num_bytes, 0, &addr.sa, smp_opt_client.res->ai_addrlen );

        if ( send > 0 )
           num_bytes += send;
        num_sends++;
        if ( send == 0 )
        {
           ipcom_printf( "Error: Disconnected"IP_LF );
           return_client = 1;
           break;
        }
        else if ( send < 0 )
        {
            if ( ipcom_errno == IP_ERRNO_EINTR )
            {
                if ( verbose )
                {
                    ipcom_printf("Thread %d done."IP_LF, portadd );
                    ipcom_printf("  Sends: %d"IP_LF, num_sends );
                    ipcom_printf("  MB/s:  %f"IP_LF, ((float)(num_bytes)/(1024.0f*1024.0f) )/5.0f );
                }
                break;
            }
            return_client = 1;
            ipcom_printf("Error on thread %d: %s"IP_LF, portadd, ipcom_strerror(ipcom_errno));
            break;
        }
    }

    if ( verbose )
    {
        if ( spawn_number_client != smp_opt_client.num_sock )
        {
            ipcom_printf("Error. Only %d client-sockets seemed to work."IP_LF, spawn_number_client );
        }
    }

    ipcom_socketclose( socket );

    ipcom_free( buf );
    if ( 0 == ipcom_atomic_sub_and_return( &num_wait_client, 1 ) )
        ipcom_sem_post( sem_wait_client );

    ipcom_proc_exit();
}
IP_STATIC
IPCOM_PROCESS( ipcom_cmd_smptest_server_spawn )
{
    union
    {
        struct Ip_sockaddr          sa;
#ifdef IPCOM_USE_INET6
        struct Ip_sockaddr_in6      sin6;
#endif
        struct Ip_sockaddr_storage  ss;
        struct Ip_sockaddr_in       sin;
    } addr;

    Ip_fd  listen_socket;
    Ip_fd  connect_sock = 0;
    int    opt_val = 1;
    int    portadd = spawn_number_server++;
    int    bytes = 0;
    int    sec = SECONDS_CLIENT + SECONDS_SERVER;
    unsigned char *buf = 0;
    int    num_recives = 0;
    unsigned long num_bytes = 0;
    struct Ip_sockaddr from;
    struct Ip_linger   linger;
    Ip_socklen_t       from_length = 0;
    int     retry_count = 0;

    ipcom_proc_init();

    ipcom_memset( &addr.ss, 0, sizeof( addr.ss ) );
    ipcom_memcpy( &addr.sa, smp_opt_server.res->ai_addr, smp_opt_server.res->ai_addrlen );

    linger.l_onoff  = 1;
    linger.l_linger = 2;

    listen_socket = ipcom_socket( smp_opt_server.res->ai_family, smp_opt_server.res->ai_socktype, smp_opt_server.res->ai_protocol );
    if (listen_socket == IP_SOCKERR)
    {
        ipcom_printf("Failed to create socket: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_server = 1;
        ipcom_sem_post( sem_wait_server );
        return;
    }

    if ( 0 != ipcom_setsockopt(listen_socket, IP_SOL_SOCKET, IP_SO_REUSEPORT, &opt_val, sizeof (opt_val)) )
    {
        ipcom_printf("ipcom_setsockopt failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_server = 1;
        ipcom_sem_post( sem_wait_server );
        return;
    }

    addr.sin.sin_port = ip_htons( smp_opt_server.port + portadd );   /* port is in the same place for IPv4 and IPv6 */
    if ( 0 != ipcom_bind( listen_socket, &addr.sa, sizeof( addr.ss ) ) )
    {
        ipcom_printf("Failed to bind: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_server = 1;
        ipcom_sem_post( sem_wait_server );
        return;
    }

    buf = ipcom_malloc( smp_opt_server.num_bytes );

    if ( smp_opt_server.tcp )
    {
        if ( -1 == ipcom_listen( listen_socket, 0 ) )
        {
            ipcom_printf("Listen failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
            return_server = 1;
            ipcom_sem_post( sem_wait_server );
            return;
        }
        if ( verbose )
            ipcom_printf("Thread %d listens to %s:%d"IP_LF, portadd, smp_opt_server.res->ai_canonname, ip_ntohs( addr.sin.sin_port ) );
    }

    if ( 0 == ipcom_atomic_sub_and_return( &listen_wait, 1 ) )
    {
        /* Send to the test-server or write to stdout? */
        if ( server_out >= 0 )
        {
            char ready[] = "Ready.";
            ipcom_socketwrite(server_out, ready, 8 );
        }
        else
            ipcom_printf("Ready."IP_LF );
    }

    if ( 0 != ipcom_socketioctl( listen_socket, IP_X_SIOCSINTR, &sec ) )
    {
        ipcom_printf("ipcom_socketioctl failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
        return_server = 1;
        ipcom_sem_post( sem_wait_server );
        return;
    }

    if ( smp_opt_server.tcp )
    {
retry:
        connect_sock = ipcom_accept( listen_socket, IP_NULL, 0 );
        if ( -1 == connect_sock )
        {
            if ( ipcom_errno == IP_ERRNO_EINTR )
            {
                if ( verbose )
                {
                    if ( ++retry_count < 5 )
                    {
                        ipcom_printf("Accept failed for thread %d: %s.. Retrying."IP_LF, portadd, ipcom_strerror(ipcom_errno));
                        goto retry;
                    }
                }
            }
            ipcom_printf("Accept failed for thread %d: %s"IP_LF, portadd, ipcom_strerror(ipcom_errno));
            return_server = 1;
            ipcom_socketclose( listen_socket );
            ipcom_sem_post( sem_wait_server );
            return;
        }

        if ( 0 != ipcom_socketioctl( connect_sock, IP_X_SIOCSINTR, &sec ) )
        {
            ipcom_printf("ipcom_socketioctl failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
            return_server = 1;
            ipcom_sem_post( sem_wait_server );
            return;
        }

        if ( 0 != ipcom_setsockopt( connect_sock, IP_SOL_SOCKET, IP_SO_LINGER, &linger, sizeof (linger)) )
        {
            ipcom_printf("ipcom_setsockopt failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
            return_server = 1;
            ipcom_sem_post( sem_wait_server );
            return;
        }
    }

    while ( 1 )
    {
        num_recives++;
        if ( smp_opt_server.tcp )
            bytes = ipcom_recv( connect_sock, buf, smp_opt_server.num_bytes, 0);
        else
        {
            bytes = ipcom_recvfrom( listen_socket, buf, smp_opt_server.num_bytes, 0, &from, &from_length );
            if ( num_recives == 1 && 0 == smp_opt_server.tcp )
            {
                sec = SECONDS_CLIENT+2+portadd;
                if ( 0 != ipcom_socketioctl( listen_socket, IP_X_SIOCSINTR, &sec ) )
                {
                    ipcom_printf("ipcom_socketioctl failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
                    return_server = 1;
                    ipcom_sem_post( sem_wait_server );
                    return;
                }
            }
        }

        if ( bytes > 0 )
            num_bytes += bytes;
        if (bytes == 0)
        {
            if ( verbose )
            {
                ipcom_printf("Thread %d done."IP_LF, portadd );
                ipcom_printf("  Recives: %d"IP_LF, num_recives );
                ipcom_printf("  MB/s:    %f"IP_LF, ((float)(num_bytes)/(1024.0f*1024.0f) )/5.0f );
            }
            break;
        }
        else if (bytes < 0)
        {
            if ( ipcom_errno == IP_ERRNO_EINTR )
            {
                if ( smp_opt_server.tcp )
                {
                    return_server = 1;
                    ipcom_printf("Error! Out of time!"IP_LF );
                    break;
                }
                else
                {
                    if ( verbose )
                    {
                        ipcom_printf("Thread %d done."IP_LF, portadd );
                        ipcom_printf("  Recives: %d"IP_LF, num_recives );
                        ipcom_printf("  MB/s:    %f"IP_LF, ((float)(num_bytes)/(1024.0f*1024.0f) )/5.0f );
                    }
                    break;
                }
            }
            else if ( bytes == IP_SOCKERR )    /* Connection reset by peer */
            {
            }

            return_server = 1;
            ipcom_printf("recv failed: %s"IP_LF, ipcom_strerror(ipcom_errno));
            return;
        }
    }

    if ( verbose )
    {
        if ( spawn_number_server != smp_opt_server.num_sock )
        {
            ipcom_printf("Error. Only %d server-sockets seemed to work."IP_LF, spawn_number_server );
        }
    }

    if ( smp_opt_server.tcp )
        ipcom_shutdown( connect_sock, IP_SHUT_RDWR );

    ipcom_free( buf );
    ipcom_socketclose( listen_socket );

    if ( 0 == ipcom_atomic_sub_and_return( &num_wait_server, 1 ) )
        ipcom_sem_post( sem_wait_server );

    if ( smp_opt_server.tcp )
        ipcom_socketclose( connect_sock );

    ipcom_proc_exit();
}
/*
 *===========================================================================
 *                    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;
}
/*
 *===========================================================================
 *                    ipcom_forwarder_get_fw_if
 *===========================================================================
 * Context:     fw
 * Description: Obtain forwarding interface index corresponding to 'msg' from fw cache
 *              In case of a match the packet referred to by 'msg' is updated
 *              with destination hw address and IP header info.
 *              source MAC must be added before packet is sent.
 * Parameters:  'msg' : packet descriptor
 * Returns:     index of interface, or -1 if no fw cache entry matches 'msg'
 *
 */
IP_STATIC __inline__ int ipcom_forwarder_get_fw_if(fw_msg_t *msg)
{
#ifdef IPCOM_USE_INET
    fw_cache_entry_v4_t *e4;
    Ip_u32 dst4;
#endif
#ifdef IPCOM_USE_INET6
    fw_cache_entry_v6_t *e6;
#endif
#ifdef IPCOM_USE_FORWARDER_VLAN
    Ip_u16 vid;
    fw_cache_entry_vlan_t *ev;
#endif

#ifdef IPCOM_USE_FORWARDER_IPSEC
    fw_ipsec_replay_t fw_ipsec_replay;

    if (msg->ipsec_dec)
    {
        int offset = LL_ETH_HDR_SZ;
#ifdef IPCOM_USE_FORWARDER_VLAN
        if (IP_UNLIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                        msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN))
            offset = LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD;
#endif /* IPCOM_USE_FORWARDER_VLAN */

        if (ipcom_forwarder_ipsec_decrypt_packet(msg, &fw_ipsec_replay, offset) < 0)
        {
            msg->tbl[0] = IPCOM_FORWARDER_PACKET_IPSEC_DROP_PKT; /* Drop packet */
            return -1;
        }
    }
#endif /* IPCOM_USE_FORWARDER_IPSEC */

    if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4))
    {
#ifdef IPCOM_USE_INET
        /* IPv4 */
        e4 = &ipcom_fw.fw_cache_tbl_v4[msg->fw_key];
        dst4 = *(Ip_u32 *)(msg->packet + LL_ETH_HDR_SZ + 16);
        if (IP_UNLIKELY(dst4 != e4->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY(dst4 != (++e4)->ip4.addr) || IP_UNLIKELY(e4->ip4.rci != *ipcom_fw.rt_cache_id))

#endif
#endif
#endif
#endif
#endif
#endif
#endif
                goto nomatch;
        }

        IP_INCREMENTAL_CHECKSUM(((Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ)));
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &e4->mac_if, &e4->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ) <= 0)
        {
            Ipnet_pkt_ip *ip = (Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ);

            /* Restore IPv4 header */
            ip->ttl++;
            ip->sum = 0;
            ip->sum = ipcom_in_checksum(ip, IPCOM_IP_HEADER_LENGTH);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = e4->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = e4->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[e4->mac_if.ix[0]].src_type[0];
        *(Ip_u16 *)(msg->packet + 10) = ipcom_fw.fw_port[e4->mac_if.ix[0]].src_type[1];
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_IPv4);
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        return e4->mac_if.ix[0];
#endif /* IPCOM_USE_INET */
    }
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6))
    {
#ifdef IPCOM_USE_INET6
        /* IPv6 */
        e6 = &ipcom_fw.fw_cache_tbl_v6[msg->fw_key];

        if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != e6->ip6.addr[0])) ||
            IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
            IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
            || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
            )
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                )
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                    IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                    IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                    || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                    )
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                        IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                        IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                        || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                        )
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                            IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                            IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                            || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                            )
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                )
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                    IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                    IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                    || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                    )
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 24) != (++e6)->ip6.addr[0])) ||
                                        IP_UNLIKELY((*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + 32) != e6->ip6.addr[1])) ||
                                        IP_UNLIKELY(e6->ip6.rci != *ipcom_fw.rt_cache_id)
#ifdef IPCOM_USE_FORWARDER_VLAN
                                        || e6->ip6.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6
#endif
                                        )
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }

        SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ)) - 1);
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &e6->mac_if, &e6->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ) <= 0)
        {
            /* Restore IPv6 header */
            SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ)) + 1);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u64 *)(msg->packet - 2) = e6->mac_if.dst_mac64;
        *(Ip_u64 *)(msg->packet + 6) = *(Ip_u64 *)&ipcom_fw.fw_port[e6->mac_if.ix[0]].src_type[0];

        return e6->mac_if.ix[0];
#endif /* IPCOM_USE_INET6 */
    }
#ifdef IPCOM_USE_FORWARDER_VLAN
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN))
    {
#ifdef IPCOM_USE_INET
        /* IPv6 table is also used for VLAN entries, both IPv4 and IPv6 */
        ev = (fw_cache_entry_vlan_t *)&ipcom_fw.fw_cache_tbl_v6[msg->fw_key];
        dst4 = *(Ip_u32 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 16);
        vid = *(Ip_u16 *)(msg->packet + LL_ETH_HDR_SZ); /* UP/CFI/VID word */

        if (IP_UNLIKELY(ev->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                        dst4 != ev->ipvlan.a.addr_v4 ||
                        vid != ev->ipvlan.vlan_id ||
                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                            dst4 != ev->ipvlan.a.addr_v4 ||
                            vid != ev->ipvlan.vlan_id ||
                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                dst4 != ev->ipvlan.a.addr_v4 ||
                                vid != ev->ipvlan.vlan_id ||
                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                    dst4 != ev->ipvlan.a.addr_v4 ||
                                    vid != ev->ipvlan.vlan_id ||
                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                        dst4 != ev->ipvlan.a.addr_v4 ||
                                        vid != ev->ipvlan.vlan_id ||
                                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                            dst4 != ev->ipvlan.a.addr_v4 ||
                                            vid != ev->ipvlan.vlan_id ||
                                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                                dst4 != ev->ipvlan.a.addr_v4 ||
                                                vid != ev->ipvlan.vlan_id ||
                                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
                                                    dst4 != ev->ipvlan.a.addr_v4 ||
                                                    vid != ev->ipvlan.vlan_id ||
                                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }

        IP_INCREMENTAL_CHECKSUM(((Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)));
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &ev->mac_if, &ev->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD) <= 0)
        {
            Ipnet_pkt_ip *ip = (Ipnet_pkt_ip *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD);

            /* Restore IPv4 header */
            ip->ttl--;
            ip->sum = 0;
            ip->sum = ipcom_in_checksum(ip, IPCOM_IP_HEADER_LENGTH);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = ev->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = ev->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[0];
        *(Ip_u32 *)(msg->packet + 10) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[1];
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_VLAN);
        *(Ip_u16 *)(msg->packet + 14) = ev->ipvlan.vlan_id;
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 16) = ip_htons(IP_IPv4);
#endif
        return ev->mac_if.ix[0];
#endif /* IPCOM_USE_INET */
    }
    else if (IP_LIKELY(msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN))
    {
#ifdef IPCOM_USE_INET6
        /* IPv6 table is also used for VLAN entries, both IPv4 and IPv6 */
        ev = (fw_cache_entry_vlan_t *)&ipcom_fw.fw_cache_tbl_v6[msg->fw_key];
        vid = *(Ip_u16 *)(msg->packet + LL_ETH_HDR_SZ); /* UP/CFI/VID word */

        if (IP_UNLIKELY(ev->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                        vid != ev->ipvlan.vlan_id ||
                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
        {
#if IPCOM_FORWARDER_CACHE_WAYS > 1
            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                            vid != ev->ipvlan.vlan_id ||
                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 2
                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                vid != ev->ipvlan.vlan_id ||
                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 3
                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                    vid != ev->ipvlan.vlan_id ||
                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 4
                        if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                        (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                        vid != ev->ipvlan.vlan_id ||
                                        ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 5
                            if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                            (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                            vid != ev->ipvlan.vlan_id ||
                                            ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 6
                                if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                                (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                                vid != ev->ipvlan.vlan_id ||
                                                ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#if IPCOM_FORWARDER_CACHE_WAYS > 7
                                    if (IP_UNLIKELY((++ev)->ipvlan.type != IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN ||
                                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 24) != ev->ipvlan.a.addr_v6[0]) ||
                                                    (*(Ip_u64 *)(msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD + 32) != ev->ipvlan.a.addr_v6[1]) ||
                                                    vid != ev->ipvlan.vlan_id ||
                                                    ev->ipvlan.rci != *ipcom_fw.rt_cache_id))
#endif
#endif
#endif
#endif
#endif
#endif
#endif
                                        goto nomatch;
        }


        SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD),
                    GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)) - 1);
#ifdef IPCOM_USE_FORWARDER_IPSEC
        if (ipcom_forwarder_ipsec_encrypt_packet(msg, &ev->mac_if, &ev->ipsec, &fw_ipsec_replay, LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD) <= 0)
        {
            /* Restore IPv6 header */
            SET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD), GET_V6_HOPL((msg->packet + LL_ETH_HDR_SZ + LL_ETH_VLAN_ADD)) + 1);
            return -1;
        }
#endif /* IPCOM_USE_FORWARDER_IPSEC */
        *(Ip_u32 *)(msg->packet - 2) = ev->mac_if.dst_mac[0];
        *(Ip_u32 *)(msg->packet + 2) = ev->mac_if.dst_mac[1];
        *(Ip_u32 *)(msg->packet + 6) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[0];
        *(Ip_u32 *)(msg->packet + 10) = ipcom_fw.fw_port[ev->mac_if.ix[0]].src_type[1];
        *(Ip_u16 *)(msg->packet + 12) = ip_htons(IP_VLAN);
        *(Ip_u16 *)(msg->packet + 14) = ev->ipvlan.vlan_id;
#ifdef IPCOM_USE_FORWARDER_IPSEC
        *(Ip_u16 *)(msg->packet + 16) = ip_htons(IP_IPv6);
#endif
        return ev->mac_if.ix[0];
#endif /* IPCOM_USE_INET6 */
    }
#endif /* IPCOM_USE_FORWARDER_VLAN */

nomatch:

#ifdef IPCOM_USE_FORWARDER_IPSEC
    if (msg->ipsec_dec == 0xff)
    {
        int ret, offset = LL_ETH_HDR_SZ;

        if (msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV4_VLAN ||
            msg->tbl[0] == IPCOM_FORWARDER_PACKET_TYPE_IPV6_VLAN)
        {
            offset += LL_ETH_VLAN_ADD;
        }

#ifdef FW_IPSEC_DEBUG
        ipcom_printf("FW_IPSEC :: re-encrypt packet! offset=%d msg->fw_key: %x msg->len=%d(%d) msg->packet=%p(%p)\n",
                     offset, msg->fw_key, msg->len, ipcom_forwarder_ipnet_msg_len(msg),
                     msg->packet, ipcom_forwarder_ipnet_msg_packet(msg));

#endif

        /* Restore buffer pointer and length */
        msg->len    = ipcom_forwarder_ipnet_msg_len(msg);
        msg->packet = ipcom_forwarder_ipnet_msg_packet(msg);

        /* Re-encrypt packet */
        ret = ipcom_forwarder_ipsec_recrypt(&msg->packet[offset], msg->len - offset);
        if (ret < 0)
        {
#ifdef FW_IPSEC_DEBUG
            ipcom_printf("FW_IPSEC :: re-encrypt packet failed!!!\n");

#endif
            ipsec_recrypt_fail++;
            msg->tbl[0] = IPCOM_FORWARDER_PACKET_IPSEC_DROP_PKT; /* Drop packet */
            return -1;
        }
        ipsec_recrypt_ok++;
    }
#endif /* IPCOM_USE_FORWARDER_IPSEC */

    return -1;
}
/*
 *===========================================================================
 *                    ipnet_gre_input
 *===========================================================================
 * Description: Handler for received GRE packets.
 * Parameters:  pkt - Received GRE packet.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_gre_input(Ipnet_netif *netif, Ipcom_pkt *pkt)
{
    Ipnet_pkt_gre *gre_hdr;
    int            gre_hdr_start = pkt->start;

    IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV4 | IPCOM_PKT_FLAG_IPV6);
    gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
    pkt->start += IPNET_GRE_HDR_SIZE;

    /* First 16-bits non-null, verify bits and version. */
    if (gre_hdr->flags_reserved0_ver)
    {
        /* RFC 2784: 2.3. Reserved0 (bits 1-12)
           A receiver MUST discard a packet where any of bits 1-5 are non-zero,
           unless that receiver implements RFC 1701. Bits 6-12 are reserved for
           future use. These bits MUST be sent as zero and MUST be ignored on
           receipt.

           RFC 2890: 2. Extensions to GRE Header
           Key present (bit 2)
           Sequence Number Present (bit 3)
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_MUSTBE0))
            goto cleanup;

        /* RFC 2784: 2.3.1. Version Number (bits 13-15)
           The Version Number field MUST contain the value zero.
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_VERSION))
            goto cleanup;

        /* RFC 2784: 2.5. Checksum (2 octets)
           The Checksum field contains the IP (one's complement) checksum sum of
           the all the 16 bit words in the GRE header and the payload packet.
           For purposes of computing the checksum, the value of the checksum
           field is zero. This field is present only if the Checksum Present bit
           is set to one.
        */
        if (IPNET_GRE_CHECKSUM_PRESENT(gre_hdr))
        {
            pkt->start += IPNET_GRE_OPT_CSUM_SIZE;

            /* IP checksum must be ok. */
            if (ipcom_in_checksum(&gre_hdr->flags_reserved0_ver, pkt->end - gre_hdr_start) != 0)
                goto cleanup;
        }

#ifdef IPNET_USE_RFC2890
        /* RFC 2890: 2.1. Key Field (4 octets)
           The Key field contains a four octet number which was inserted by the
           encapsulator. The actual method by which this Key is obtained is
           beyond the scope of the document. The Key field is intended to be
           used for identifying an individual traffic flow within a tunnel. For
           example, packets may need to be routed based on context information
           not present in the encapsulated data.  The Key field provides this
           context and defines a logical traffic flow between encapsulator and
           decapsulator.  Packets belonging to a traffic flow are encapsulated
           using the same Key value and the decapsulating tunnel endpoint
           identifies packets belonging to a traffic flow based on the Key Field
           value.
        */
        if (IPNET_GRE_KEY_PRESENT(gre_hdr))
        {
            Ip_u32 key;

            key = IP_GET_NTOHL(&pkt->data[pkt->start]);
            IPNET_GRE_PKT_SET_KEY(pkt, key);
            pkt->start += IPNET_GRE_OPT_KEY_SIZE;
        }

        /* RFC 2890: 2.2. Sequence Number (4 octets)
           The Sequence Number field is a four byte field and is inserted by the
           encapsulator when Sequence Number Present Bit is set. The Sequence
           Number MUST be used by the receiver to establish the order in which
           packets have been transmitted from the encapsulator to the receiver.
           The intended use of the Sequence Field is to provide unreliable but
           in-order delivery. If the Key present bit (bit 2) is set, the
           sequence number is specific to the traffic flow identified by the Key
           field. Note that packets without the sequence bit set can be
           interleaved with packets with the sequence bit set.
        */
        if (IPNET_GRE_SEQNUM_PRESENT(gre_hdr))
        {
            Ipnet_gre_t *gre = netif->ipcom.pdrv;
            Ip_u32       seqnum;

            seqnum = IP_GET_NTOHL(&pkt->data[pkt->start]);
            if (seqnum == gre->recv_seqnum)
                /* In order segment */
                ++gre->recv_seqnum;
            else if (IPCOM_IS_LT(seqnum, gre->recv_seqnum))
                /* Out of order */
                goto cleanup;
            else
            {
                Ip_u32 outoforder_timer;
                int max_perflow_buffer;

                max_perflow_buffer = ipcom_sysvar_get_as_int0("ipnet.gre.MAX_PERFLOW_BUFFER", 3);
                outoforder_timer = ipcom_sysvar_get_as_int0("ipnet.gre.OUTOFORDER_TIMER", 1000);

                if (max_perflow_buffer <= 0)
                    /* No buffering allowed */
                    goto cleanup;

                while (ipcom_pqueue_size(gre->reassembly_queue) >= max_perflow_buffer)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    /* Call the timeout handler to force the oldest packet(s)
                       to be inputted to the stack and the receive sequence
                       number to advanced */
                    ipnet_gre_seq_tmo(netif);
                }

                if (gre_hdr->protocol_type == ip_htons(0x0800))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV4);
                else if (gre_hdr->protocol_type == ip_htons(0x86DD))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV6);

                IPNET_PKT_SET_TIMEOUT_ABS(pkt, ipnet->msec_now + outoforder_timer);
                if (gre->reassembly_tmo == IP_NULL)
                    if (ipnet_timeout_schedule(outoforder_timer,
                                               (Ipnet_timeout_handler) ipnet_gre_seq_tmo,
                                               netif,
                                               &gre->reassembly_tmo) < 0)
                        goto cleanup;
                if (ipcom_pqueue_insert(gre->reassembly_queue, pkt) != IPCOM_SUCCESS)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    goto cleanup;
                }
                return 0;
            }
            pkt->start += IPNET_GRE_OPT_SEQNUM_SIZE;
        }
#endif /* IPNET_USE_RFC2890 */
    }

    /**/
    if (netif->ipcom.link_tap)
        /* start is PAYLOAD == the inner IP header; ipstart points to the PREVIOUS
         * IP header */
        netif->ipcom.link_tap(&netif->ipcom, pkt, IP_PACKET_HOST, gre_hdr->protocol_type, pkt->start, pkt->start);

#ifdef IPCOM_USE_INET
    /* IPv4 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x0800))
        return ipnet_ip4_input(netif, pkt);
#endif

#ifdef IPCOM_USE_INET6
    /* IPv6 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x86DD))
        return ipnet_ip6_input(netif, pkt);
#endif

    /* Unsupported Protocol Type, drop. */

 cleanup:
    ipcom_pkt_free(pkt);
    return -IP_ERRNO_EINVAL;
}
Beispiel #12
0
/*
 *===========================================================================
 *                    ipnet_sock_udp_send
 *===========================================================================
 * Description: Adds a UDP header to the application data.
 * Parameters:  sock - The socket to use when sending.
 *              msg - The send parameters.
 *              pkt - Packet that contains the UDP payload.
 * Returns:     ==0 = success, <0 = error code.
 *
 */
IP_STATIC int
ipnet_sock_udp_send(Ipnet_socket *sock, IP_CONST struct Ip_msghdr *msg, Ipcom_pkt *pkt)
{
    Ipnet_sock_udp_ops *ops = (Ipnet_sock_udp_ops *) sock->ops;
    Ip_u16              to_port_n;
    Ipnet_pkt_udp      *udp;
    int                 ret;

    IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 1, IPCOM_WV_NETDEVENT_START,
                       ipnet_sock_udp_send, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
    IPNET_STATS(udp_output++);
    ip_assert(sock->ipcom.type == IP_SOCK_DGRAM);
    ip_assert(sock->proto == IP_IPPROTO_UDP);

    if (msg->msg_name == IP_NULL)
        to_port_n = ip_htons(sock->dport);
    else
        /* Ok for both IPv4 and IPv6 */
        to_port_n = ((struct Ip_sockaddr_in*) msg->msg_name)->sin_port;

    pkt->start -= IPNET_UDP_HDR_SIZE;

    /* Fill in UDP header. */
    udp = (Ipnet_pkt_udp *)&pkt->data[pkt->start];

    if (sock->sport)
        udp->sport = ip_htons(sock->sport);
    else
    {
        /* UDP socket unbound, get a temporary port. */
        udp->sport = ipnet_next_ephemeral_port(sock);
        if (udp->sport == 0)
        {
            IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                              1, 2, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_SEND,
                              ipnet_sock_udp_send, IPCOM_WV_NETD_ADDRINUSE,
                              IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
            IPNET_STATS(udp_output_eaddrinuse++);
            ret = -IP_ERRNO_EADDRINUSE;
            goto errout;
        }
        udp->sport = ip_htons(udp->sport);
    }

    udp->dport = to_port_n;
    udp->sum   = 0;
#ifdef IPCOM_ZEROCOPY
    udp->ulen  = (Ip_u16) ip_htons(ipcom_pkt_get_length(pkt));
#else
    udp->ulen  = (Ip_u16) ip_htons(pkt->end - pkt->start);
#endif

    /* Update the checksum with the UDP header */
#ifdef IPCOM_USE_HW_CHECKSUM_TX
    if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_HW_CHECKSUM))
#endif
#ifdef IPCOM_ZEROCOPY
    	if (IP_BIT_ISFALSE(msg->msg_flags, IP_MSG_ZBUF))
#endif
    		pkt->chk += ipcom_in_checksum_update(&pkt->data[pkt->start], IPNET_UDP_HDR_SIZE);
    IPCOM_MIB2(udpOutDatagrams++);
    IPCOM_MIB2_U64_INC(udpHCOutDatagrams);

    return ops->inet.network_send(sock, msg, pkt);

errout:
    ipcom_pkt_free(pkt);
    return ret;
}