/*
 *===========================================================================
 *                    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);
    }
}
/*
 *===========================================================================
 *                    ipnet_gre_seq_cmp
 *===========================================================================
 * Description: Compares the sequence number of two packets.
 * Parameters:  netif - A GRE tunnel interface.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_STATIC int
ipnet_gre_seq_cmp(IP_CONST Ipcom_pkt *pkt1, IP_CONST Ipcom_pkt *pkt2)
{
    Ip_u32 seqnum1 = IP_GET_NTOHL(&pkt1->data[pkt1->start]);
    Ip_u32 seqnum2 = IP_GET_NTOHL(&pkt2->data[pkt2->start]);

    if (IPCOM_IS_LT(seqnum1, seqnum2))
        return 1;

    if (IPCOM_IS_GT(seqnum1, seqnum2))
        return -1;

    return 0;
}
示例#3
0
/*
 *===========================================================================
 *                    ipnet_nat_proxy_h225_msg
 *===========================================================================
 * Description: Track H.225 packets.
 * This function handles the H.225/Q.931 call signalling packets.  NAT calls
 * this function after translating the address and port number in the IP and
 * TCP headers, and when the source or destination port is the H.323 port
 * registered to NAT during the H.323 ALG registration (the standard port for
 * H.323 protocol is 1720).  The H.225 call signalling protocol is used in H.323
 * to establish connection between two end-points.
 *
 * This function must look at both an outbound as well as an inbound packet.
 * H.323 connection can be attempted from local to global endpoint or vice
 * versa.
 *
 * NOTE 1:
 * The fields in the H.225 message are ASN.1 encoded. However, due to schedule,
 * constraint, rather than employing an ASN.1 decoder, this function uses a simple
 * strategy to look for the ip+port address in the H.225 payload by taking advantage
 * of the fact that the port number always follows immediately after the ip address.
 * Since the ALG can get the expected ip address from NAT, it can search byte by
 * byte for this ip address to locate where it is in the H.225 payload..
 *
 * Local host (L) <---------------> NAT <----------------> Global host (G)
 *
 * The tuple of IP address and TCP/UDP port number is sometimes called transport
 * address in some publications, and the same terminology will be used here.
 *
 * Case 1:  Local (L) endpoint to global (G) endpoint connection
 *
 * L starts a TCP connection to G and the H.225 call signaling session starts.
 * The establishment of this TCP connection is all handled by NAT, so by now,
 * NAT should have the bind entry for this connection.  During the H.225 session,
 * L will embed its sourceCallSignalAddress (ip/port) and G's transport address
 * in the H.225 payload to G.  Thus, this function must parse for each H.225
 * outbound packet, look for L's transport address, and substitute it with the
 * translated address obtained from NAT.
 *
 * In addition, this function must also observe the H.225 inbound packets from G
 * to look for the H.245 transport address (ip/port) in the connect message from
 * G.  The port number provided by G here will serve as the H.245 port number.
 * L will use this port number to open the H.245 TCP connection with G.  So upon
 * obtaining the H.245 port number, this function must also register the H.245
 * ALG agent to NAT .
 *
 * Case 2:  Global (G) endpoint to local (L) endpoint connection
 *
 * G starts a TCP connection to L via the NAT's H.323 static entry and the H.225
 * call signaling session starts.  As in case 1, the establishment of this TCP
 * connection is all handled by NAT, so by now, NAT should have the bind entry
 * for this connection.  During the H.225 session, G will embed its sourceCall-
 * SignalAddress and L's global transport address in the H.225 payload.  So, this
 * function must examine all inbound H.225 packets and substitute L's global
 * transport address with its real transport address.
 *
 * In addition, this function must also observe the H.225 outbound packets
 * to look for L's H.245 transport address which is embedded in the connect
 * message from L to G. The port number embedded in this message will serve as
 * the H.245 port number.
 *
 * G will use this port number to open the H.245 TCP connection with L.  So, this
 * function must also register the H.245 ALG agent to NAT upon obtaining the
 * H.245 port number.  Furthermore, since the H.245 TCP connection will be
 * started from G to L, this function must create a new TCP bind entry for the
 * impending H.245 port connection.
 *
 * NOTE 2:
 * TCP sequence adjustment is not required since it is a binary substitution of
 * IP address and port number in the payload.  However, the checksum in the TCP
 * header must be adjusted when the TCP payload is modified.
 *
 * Parameters:  appdata   - pointer to application data.
 *              applen    - pointer to length of application data.
 *              param     - pointer to proxy parameters.
 * Returns:     1 = Packet modified.
 *              0 = Packet untouched.
 *             -1 = Drop packet.
 */
IP_STATIC int
ipnet_nat_proxy_h225_msg(Ip_u8 *appdata,
                         int    applen,
                         Ipnet_nat_proxy_param *param)
{
    Ipnet_nat_proxy_tuple proxy_tuple;
    Ip_u8   *data, *data_start;
    Ip_u16  local_port, port;
    Ip_u32  remote_address, local_address, ip_addr;
    int     data_length, tmp;
    Ip_bool mod = IP_FALSE;

    data_length = applen;       /* length of TCP payload */
    if (data_length <= 0)
    {
        /* no payload to look at */
        return 0;
    }

    /* go to start of TCP payload */
    data = appdata;
    data_start = data;
    local_address  = param->tuple.private_addr;
    local_port     = param->tuple.private_port;
    remote_address = param->tuple.public_addr;

    if (param->incoming == IP_FALSE)    /* outbound packet */
    {
        /* get the H.225 TCP bind descriptor using L's translated source transport
           address from the source address in the TCP/IP header.  From the bind
           descriptor, obtain L's real transport address and the session flow (i.e.
           who starts the connection first) of this connection.
        */
        if (param->inbound == IP_FALSE)     /* session started by L */
        {
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: outbound packet, outbound session. "
                              "Look for sourceCallSignalAddress from local host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's sourceCallSignalAddress match */
                if (ip_addr == local_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    if (port == local_port)
                    {
                        /* replace with L's translated sourceCallSignalAdress */
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "Replace local address and port (0x%08x:%d) in H225 payload.",
                                           ip_addr, port);

                        IP_SET_HTONL(data, param->nat_addr);
                        IP_SET_HTONS(data + 4, param->nat_port);
                        data += 6;
                        mod = IP_TRUE;

                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "Translated local address and port are (0x%08x:%d)",
                                           param->nat_addr, param->nat_port);
                    }
                    else    /* port != bind_info.local_transport */
                    {
                        data++;

                        IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: matched local address found, "
                                          "but not the port number.");
                    }
                }
                else    /* ip_addr != bind_info.local_addr */
                {
                    data++;
                }
            }
        }
        else    /* session started by G */
        {
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: outbound packet, inbound session. "
                              "Look for H245Address from local host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's H245Address port */
                if (ip_addr == local_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "Found H245Address (0x%08x:%d) in H225 payload.",
                                       ip_addr, port);

                    /* register H.245 ALG to NAT */
                    /* if NAPT, create a H.245 TCP bind entry to prepare for H.245
                       connection request from G */

                    ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
                    proxy_tuple.protocol     = param->tuple.protocol;
                    proxy_tuple.private_addr = local_address;
                    proxy_tuple.private_port = port;
                    proxy_tuple.public_addr  = remote_address;

                    tmp = ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                                      0,
                                                      param->mapping,
                                                      IP_TRUE,          /* Use port translation */
                                                      IP_TRUE,          /* Inbound session */
                                                      ipnet_nat_proxy_h245,
                                                      IP_NULL);
                    if (tmp < 0)
                    {
                        IPCOM_LOG2(ERR, "ipnet_nat_proxy_h225_msg() :: Failed to add mapping for address = 0x%08x, port = %d",
                                         local_address, port);
                    }
                    else
                    {
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: Added mapping for address = 0x%08x, port = %d",
                                           local_address, port);
                    }

                    IP_SET_HTONL(data, param->nat_addr);
                    IP_SET_HTONS(data + 4, (Ip_u16)tmp);
                    data += 6;
                    mod = IP_TRUE;

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "Translated local address and port are (0x%08x:%d)",
                                       param->nat_addr, tmp);

                }
                else    /* ip_addr != bind_info.local_addr */
                {
                    data++;
                }
            }/* while */
        }   /* else: session started by G */
    }
    else    /* inbound packet */
    {
        if (param->inbound == IP_FALSE)     /* session started by L */
        {

            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: inbound packet, outbound session. "
                              "Look for H245Address from remote host.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for G's H245Address match */
                if (ip_addr == remote_address)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "H245Address from remote host found (0x%08x:%d).",
                                       ip_addr, port);

                    /* register H.245 ALG to NAT */
                    ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
                    proxy_tuple.protocol     = param->tuple.protocol;
                    proxy_tuple.public_addr  = ip_addr;
                    proxy_tuple.public_port  = port;
                    proxy_tuple.private_addr = param->tuple.private_addr;

                    if (ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                                    0,
                                                    param->mapping,
                                                    IP_TRUE,          /* Use port translation */
                                                    IP_FALSE,         /* Outbound session */
                                                    ipnet_nat_proxy_h245,
                                                    IP_NULL) < 0)
                    {
                        IPCOM_LOG2(ERR, "ipnet_nat_proxy_h225_msg() :: Failed to add mapping for address = 0x%08x, port = %d",
                                         remote_address, port);
                    }
                    else
                    {
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: Added mapping for address = 0x%08x, port = %d",
                                           remote_address, port);
                    }
                }
                data++;
            }
        }
        else    /* session started by G */
        {
            /* search for L's transport address in the H225 payload.
               If found, translate it to its real transport address. */
            IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_h225_msg() :: inbound packet, inbound session."
                              "Translate global transport to its local transport.");

            while ((data + 6) <= (data_start + data_length))
            {
                ip_addr = IP_GET_NTOHL(data);

                /* look for L's translated address match */
                if (ip_addr == param->nat_addr)
                {
                    port = IP_GET_NTOHS(data + 4);

                    IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                      "global ip match found in H.225 payload (0x%08x:%d).",
                                       ip_addr, port);

                    if (port == param->nat_port)
                    {
                        IP_SET_HTONL(data, local_address);
                        IP_SET_HTONS(data + 4, local_port);
                        data += 6;
                        mod = IP_TRUE;

                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h225_msg() :: "
                                          "After translation local_address and port are (0x%08x:%d).",
                                           local_address, local_port);
                    }
                    else    /* port != bind_info.global_transport */
                    {
                        data++;
                    }
                }
                else    /* ip_addr != bind_info.global_addr */
                {
                    data++;
                }
            }   /* while */
        }   /* else (session started by G) */
    }

    return mod == IP_TRUE ? 1 : 0;
}
示例#4
0
/*
 *===========================================================================
 *                    ipnet_nat_proxy_h245_msg
 *===========================================================================
 * Description: Track H.245 packets.
 * This function handles the H.245 call control packets.  NAT calls this function
 * after translating the address and port number in the IP and TCP headers, and
 * when the source or destination port is the H.245 port. The H.245 port was
 * negotiated during the H.225 call signaling session and registered to NAT.
 * The H.245 call control protocol is used in H.323 to negotiate call parameters
 * between two end-points.  For example, it negotiates the UDP connections for
 * the open logical channels for RTP and RTCP streams between the two endpoints.
 * In addition, it also negotiates the TCP connection for the T1.120 session.
 *
 * NOTE 1:
 * The H.245 message is ASN.1 encoded. However, due to resource and schedule
 * constraint, rather than employing an ASN.1 decoder, this function uses a simple
 * work-around alternative which appears to work just as well.  This work-around
 * strategy looks for the ip+port address in the H.245 payload by taking advantage
 * of the fact that the port number always follows immediately after the ip address.
 * Since the ALG can get the expected ip address from NAT, it can search byte by
 * byte for this ip address to locate where it is in the H.245 payload.
 *
 * Local host (L) <---------------> NAT <----------------> Global host (G)
 *
 * The tuple of IP address and TCP/UDP port number is sometimes called transport
 * address in some publications, and the same terminology will be used here.
 *
 * For an outbound packet, L may be sending its transport addresses to G to let
 * G make TCP/UDP connections to it.  Similarly, for an inbound packet, G may be
 * sending its transport addresses to L to let L make TCP/UDP connections to it.
 * Since only the L's transport addresses in the payload need to be translated,
 * it is sufficient to examine only the outbound packets.
 *
 * NOTE 2:
 * TCP sequence adjustment is not required since it is a binary substitution of
 * IP address and port number in the payload.  However, the checksum in the TCP
 * header must be adjusted when the TCP payload is modified.
 *
 * Parameters:  appdata   - pointer to application data.
 *              applen    - pointer to length of application data.
 *              param     - pointer to proxy parameters.
 * Returns:     1 = Packet modified.
 *              0 = Packet untouched.
 *             -1 = Drop packet.
 */
IP_STATIC int
ipnet_nat_proxy_h245_msg(Ip_u8 *appdata,
                         int    applen,
                         Ipnet_nat_proxy_param *param)
{
    Ipnet_nat_proxy_tuple proxy_tuple;
    Ip_u8   *data, *data_start;
    Ip_u16  port;
    Ip_u32  local_address, ip_addr;
    int     data_length;
    Ip_bool mod = IP_FALSE;

    if (param->incoming == IP_TRUE)
        return 0;
    data_length = applen;       /* length of TCP payload */
    if (data_length <= 0)
    {
        /* no payload to look at */
        return 0;
    }

    /* go to start of TCP payload */
    data = appdata;
    data_start = data;
    local_address = param->tuple.private_addr;    /* L's local ip */

    /* search for L's ip address in the H.245 payload for all possible
      TCP/UDP connections that may be negotiated.  For each one found, create
      create a static TCP/UDP control block to get the translated transport address.
      We must do this for all L's ip address found in the H.245 payload
      since we are not decoding the ASN.1 message thus don't really know
      which ones will actually be used.  A check is made to prevent creation
      of duplicate bind entries.  The ID of each created static entry is recorded
      so the ALG can delete them when the Netmeeting session is terminated */

    while ((data + 6) <= (data_start + data_length))
    {
        ip_addr = IP_GET_NTOHL(data);

        /* look for L's global address match */
        if (ip_addr == local_address)
        {
            port = IP_GET_NTOHS(data + 4);
            if (port > IPNET_NAT_H323_LOWER_EPHEMERAL_PORT_VALUE)
            {
                IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h245_msg() :: Local host address found in H245 payload. "
                                  "ip = 0x%08x, port = %d", ip_addr, port);

                /* Do not add a mapping for the TCP T1.120 port here. Should be taken care of by NAT rules */
                if (port != IPNET_NAT_T1_120_PORT)
                {
                    /* create a bind entry for this transport address if it hasn't been created yet */
                    ipcom_memset(&proxy_tuple, 0, sizeof(proxy_tuple));
                    proxy_tuple.protocol     = IP_IPPROTO_UDP;
                    proxy_tuple.private_addr = local_address;
                    proxy_tuple.private_port = port;
                    proxy_tuple.public_addr  = param->tuple.public_addr;
                    if (ipnet_nat_proxy_add_mapping(&proxy_tuple,
                                                    IPNET_NAT_H323_MEDIA_TIMEOUT,
                                                    param->mapping,
                                                    IP_FALSE,         /* NAT port = local source port */
                                                    IP_TRUE,          /* Inbound session */
                                                    IP_NULL,
                                                    IP_NULL) < 0)
                    {
                        IPCOM_LOG2(ERR, "ipnet_nat_proxy_h245_msg() :: Failed to add mapping for address = 0x%08x, port = %d",
                                         local_address, port);
                    }
                    else
                    {
                        IPCOM_LOG2(DEBUG, "ipnet_nat_proxy_h245_msg() :: Added mapping for address = 0x%08x, port = %d",
                                           local_address, port);
                    }
                }

                /* Replace L's local address with its global address in the payload */
                IP_SET_HTONL(data, param->nat_addr);
                data += 6;
                mod = IP_TRUE;

                IPCOM_LOG1(DEBUG, "ipnet_nat_proxy_h245_msg() :: "
                                  "Translated local address is (0x%08x)",
                                  param->nat_addr);
            }
            else    /* detected port number is not a valid ephemeral port value */
            {
                IPCOM_LOG1(INFO, "ipnet_nat_proxy_h245_msg() :: detected port invalid, ignore! port = %d", port);
                data++;
            }
        }
        else    /* ip_addr != local_address */
        {
            data++;
        }
    }   /* end while loop */

    return mod == IP_TRUE ? 1 : 0;
}
/*
 *===========================================================================
 *                    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;
}