Beispiel #1
0
/*
 *===========================================================================
 *                     ipnet_udp_fast_deliver_data
 *===========================================================================
 * Description: Deliver an UDP packet to a socket that is bound to a unicast
 *              address.
 * Parameters:  sock - The socket the packet will be delivered to.
 *              pkt - The UDP packet to deliver.
 *              nat_t - NAT_T processing.
 * Returns:     IP_TRUE - The fast path processing was successful.
 *              IP_FALSE - Need to use the normal receive path.
 *
 */
IP_STATIC Ip_bool
ipnet_udp_fast_deliver_data(Ipnet_socket *sock, Ipcom_pkt *pkt, int *nat_t)
{
    Ipnet_pkt_udp       *udp_hdr;
    int                  original_start = pkt->start;
    Ipcom_socket_eventcb event_cb = sock->ipcom.event_cb;

    udp_hdr = (Ipnet_pkt_udp*) &pkt->data[pkt->tlstart];
    if (IP_UNLIKELY(pkt->end - pkt->tlstart < IPNET_UDP_HDR_SIZE
                    || pkt->end - pkt->tlstart < ip_ntohs(udp_hdr->ulen)))
        return IP_FALSE;

    if (udp_hdr->sum != 0 && IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_LOOPED_BACK))
    {
        Ip_u16 chksum;
        /* Only check packets that was not created locally */
#ifdef IPCOM_USE_HW_CHECKSUM_RX
        if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_HW_CHECKSUM))
            chksum = 0;
        else if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_TL_CHECKSUM))
            chksum = ipcom_in_checksum_finish(pkt->chk);
        else
#endif /* IPCOM_USE_HW_CHECKSUM_RX */
        {
            pkt->chk += ipcom_in_checksum_update(&pkt->data[pkt->tlstart],
                                                 (Ip_size_t) ip_ntohs(udp_hdr->ulen));
            chksum = ipcom_in_checksum_finish(pkt->chk);
        }

        if (IP_UNLIKELY(chksum != 0))
            return IP_FALSE;
    }

    pkt->fd    = sock->ipcom.fd;
    pkt->start = pkt->tlstart + IPNET_UDP_HDR_SIZE;

    if (IP_UNLIKELY(ipnet_udp_encapsulation(sock, pkt, nat_t)))
    {
        /* Continue processing */
        ipcom_pkt_set_info(pkt, IPNET_PKT_INFO_ENCAP_UDP, sizeof(*udp_hdr), udp_hdr);
        pkt->start = original_start;
        return IP_FALSE;
    }

    if (ipnet_queue_received_packet(pkt, sock) != 0)
    {
        pkt->start = original_start;
        return IP_FALSE;
    }

    if (event_cb != IP_NULL)
        event_cb(&sock->ipcom, pkt, IP_SOEVENT_CB_INPKT);

    IPCOM_MIB2(udpInDatagrams++);
    IPCOM_MIB2_U64_INC(udpHCInDatagrams);
    return IP_TRUE;
}
Beispiel #2
0
/*
 *===========================================================================
 *                    ipnet_udp_input
 *===========================================================================
 * Description: Input handler for packet to UDP sockets. This function will
 *              NOT free the packet if the operation fails.
 * Parameters:  pkt - The received packet, must be freed by the called if
 *              this function fails.
 *              is_multicast - Set to IP_TRUE if the packet was sent to a
 *              multicast address.
 *              proto - The protocol number.
 *              src_addr - The address (Ipv4 or IPv6) of the sender.
 *              dst_addr - The address (Ipv4 or IPv6) of the receiver (this system).
 *              socklookup - A pointer to a function that can return the socket
 *              that is associated with this packet.
 *              nat_t   - NAT traversal; if used.
 * Returns:     0 >= number of matching sockets, <0 = error code
 *
 */
IP_GLOBAL int
ipnet_udp_input(Ipcom_pkt *pkt, Ip_bool is_multicast,
                IP_CONST void* src_addr, IP_CONST void* dst_addr,
                Ipnet_sock_lookup_f socklookup,
                int   *nat_t)
{
    Ipnet_pkt_udp *udp_hdr;
    int            retval;
    int            ulen_h;
    Ipnet_socket  *sock;
    Ip_u16         src_port;
    Ip_u16         dst_port;
    Ip_bool        pkt_no_rx_cache;


    IPCOM_WV_MARKER_1 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_VERBOSE, 1, 3, IPCOM_WV_NETDEVENT_START,
                       ipnet_udp_input, IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
    IPNET_STATS(udp_input++);
    IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_UDP_INPUT);

    if (IP_UNLIKELY(pkt->end - pkt->start < IPNET_UDP_HDR_SIZE))
    {
        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                          1, 4, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_BADHLEN,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_hdr_trunc++);
        IPCOM_MIB2(udpInErrors++);
        return -IP_ERRNO_EINVAL;
    }

    udp_hdr = (Ipnet_pkt_udp*) &pkt->data[pkt->start];
    ulen_h = ip_ntohs(udp_hdr->ulen);
    src_port = ip_ntohs(udp_hdr->sport);
    dst_port = ip_ntohs(udp_hdr->dport);

    if (IP_UNLIKELY(pkt->end - pkt->start < ulen_h))
    {
        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                          1, 5, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_BADLEN,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_hdr_trunc++);
        IPCOM_MIB2(udpInErrors++);
        return -IP_ERRNO_EINVAL;
    }
    pkt->end = pkt->start + ulen_h;

    if (udp_hdr->sum != 0 && IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_LOOPED_BACK))
    {
        Ip_u16 chksum;
        /* Only check packets that was not created locally */
#ifdef IPCOM_USE_HW_CHECKSUM_RX
        if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_HW_CHECKSUM))
            chksum = 0;
        else if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_TL_CHECKSUM))
            chksum = ipcom_in_checksum_finish(pkt->chk);
        else
#endif /* IPCOM_USE_HW_CHECKSUM_RX */
        {
            pkt->chk += ipcom_in_checksum_update(&pkt->data[pkt->start], (Ip_size_t) ulen_h);
            chksum = ipcom_in_checksum_finish(pkt->chk);
        }

        if (IP_UNLIKELY(chksum != 0))
        {
            /* Wrong checksum */
            IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                              1, 6, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                              ipnet_udp_input, IPCOM_WV_NETD_BADSUM,
                              IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
            IPNET_STATS(udp_input_badchksum++);
            IPCOM_MIB2(udpInErrors++);
            return -IP_ERRNO_EINVAL;
        }
    }

    /* Move the start to beginning of application data */
    pkt->start += IPNET_UDP_HDR_SIZE;

    if (is_multicast)
    {
        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
                          1, 7, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_INFO_RECEIVE,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_multicast++);
        retval = ipnet_raw_input(pkt, IP_FALSE, IP_IPPROTO_UDP,
                                 src_addr, src_port,
                                 dst_addr, dst_port,
                                 socklookup);
        if (retval >= 0)
        {
            /* The packet must be freed unlesss an error occured */
            ipcom_pkt_free(pkt);
            IPCOM_MIB2(udpInDatagrams++);
            IPCOM_MIB2_U64_INC(udpHCInDatagrams);
        }
        else
        {
            IPCOM_MIB2(udpInErrors++);
        }
        return retval;
    }

    sock = socklookup(pkt->vr_index,
                      IP_IPPROTO_UDP,
                      dst_addr,
                      dst_port,
                      src_addr,
                      src_port);
    if (sock == IP_NULL)
        /* No matching socket, try the wildcard group */
        sock = socklookup(IPCOM_VR_ANY,
                          IP_IPPROTO_UDP,
                          dst_addr,
                          dst_port,
                          src_addr,
                          src_port);

    if (sock != IP_NULL)
    {
        Ipcom_socket_eventcb event_cb = sock->ipcom.event_cb;

        /* Verify NAT traversal */
        if (IP_UNLIKELY(ipnet_udp_encapsulation(sock, pkt, nat_t)))
        {
            ipcom_pkt_set_info(pkt, IPNET_PKT_INFO_ENCAP_UDP, sizeof(*udp_hdr), udp_hdr);
            pkt->start -= IPNET_UDP_HDR_SIZE;
            return 0;
        }

        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
                          1, 8, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_INFO_RECEIVE,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_sock_match++);
        pkt->fd = sock->ipcom.fd;
        pkt_no_rx_cache = IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_NO_RX_CACHE);

        retval = ipnet_queue_received_packet(pkt, sock);
        if (retval < 0)
        {
            IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                              1, 9, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                              ipnet_udp_input, IPCOM_WV_NETD_INFO_QUEUE_FULL,
                              IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
            IPNET_STATS(udp_input_queue_pkt_err++);
            IPCOM_MIB2(udpInErrors++);
            return retval;
        }

        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_INFO,
                          1, 10, IPCOM_WV_NETDEVENT_INFO, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_INFO_RECEIVE,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_queue_pkt_ok++);

        if (event_cb != IP_NULL)
            event_cb(&sock->ipcom, pkt, IP_SOEVENT_CB_INPKT);

        IPCOM_MIB2(udpInDatagrams++);
        IPCOM_MIB2_U64_INC(udpHCInDatagrams);

        if (IP_BIT_ISFALSE(sock->flags, IPNET_SOCKET_FLAG_RX_CACHED)
            && !pkt_no_rx_cache)
            ipnet_sock_add_to_rx_cache(sock,
                                       src_addr,
                                       src_port,
                                       dst_addr,
                                       dst_port,
                                       ipnet_udp_fast_deliver_data);

        return 1;
    }
    else
    {
        IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                          1, 13, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                          ipnet_udp_input, IPCOM_WV_NETD_NOPORT,
                          IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
        IPNET_STATS(udp_input_sock_nomatch++);
        IPCOM_MIB2(udpNoPorts++);
    }

    IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                      1, 14, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                      ipnet_udp_input, IPCOM_WV_NETD_NOPORT,
                      IPCOM_WV_IPNET_UDP_MODULE, IPCOM_WV_NETD_UDP4);
    IPNET_STATS(udp_input_econnrefused++);
    return -IP_ERRNO_ECONNREFUSED;
}
/*
 *===========================================================================
 *                    ipnet_fragment_packet
 *===========================================================================
 * Description: Splits a packet that is to big to fit into the MTU of the
 *              network interface.
 * Parameters:  pkt - The packet to split. pkt->start is the offset to the
 *              extension header (or transport layer protocol) with the
 *              id of 'next_hdr'. This packet is released by this function.
 *              ip_hdr_size - The size of the IP header.
 *              other_hdr_size - The size of other headers that will be
 *              added due to fragmentation.
 *              mtu - The MTU of the network interface.
 *              has_more_fragments - function that returns IP_TRUE if the
 *              packet passed as an argument has more fragments. This
 *              parameter might be IP_NULL.
 * Returns:     A list of fragments or IP_NULL if not enough memory could
 *              be allocated to create all fragments.
 */
IP_GLOBAL Ipcom_pkt *
ipnet_fragment_packet(Ipcom_pkt *pkt,
                      int ip_hdr_size,
                      int other_hdr_size,
                      int mtu,
                      Ipnet_more_fragments_func more_fragments)
{
    Ipcom_pkt *frag_pkt_head;
    Ipcom_pkt *frag_pkt_tail;
    Ipcom_pkt *frag_pkt;
    int        frag_size;
    int        total_size;
    int        size_of_this_fragment;
    int        frag_offset;
    Ipnet_pkt_info_sock_snd_buf *ssb;

    total_size = pkt->end - pkt->start;
    frag_size = (mtu - ip_hdr_size - other_hdr_size) & 0xfff8;
    frag_pkt_head = IP_NULL;
    frag_pkt_tail = IP_NULL;
    frag_offset = 0;

    while (frag_offset < total_size)
    {
        if (total_size - frag_offset > frag_size)
            size_of_this_fragment = frag_size;
        else
            size_of_this_fragment = total_size - frag_offset;

        frag_pkt = ipcom_pkt_malloc(mtu, IP_FLAG_FC_STACKCONTEXT);
        if (frag_pkt == IP_NULL)
        {
            IPCOM_WV_MARKER_1 ( IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_CRITICAL, 1, 1, IPCOM_WV_NETD_NOBUFS,
                                ipnet_fragment_packet, IPCOM_WV_IPNET_FRAG_MODULE, IPCOM_WV_NETD_IP_REASSEMBLE);
            IPNET_STATS(pkt_frag_nomem++);
            ipcom_pkt_free(pkt);
            if (frag_pkt_head != IP_NULL)
                ipcom_pkt_free(frag_pkt_head);
            return IP_NULL;
        }

#ifdef IPCOM_USE_MIB2
        if(other_hdr_size == 0)
        {
#ifdef IPCOM_USE_INET
            Ipnet_netif *netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);

            IPCOM_MIB2(ipFragCreates++);
            if (netif != IP_NULL)
            {
                IPCOM_MIB2_SYSWI_U32_ADD(v4, ipSystemStatsOutFragCreates, 1);
                IPCOM_MIB2_PERIF_U32_ADD(v4, ipIfStatsOutFragCreates, 1, netif);
            }
#endif
        }
        else
        {
#ifdef IPCOM_USE_INET6
            Ipnet_netif *netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);

            IPCOM_MIB2_NETIF(ipv6IfStatsOutFragCreates++);
            if (netif != IP_NULL)
            {
                IPCOM_MIB2_SYSWI_U32_ADD(v6, ipSystemStatsOutFragCreates, 1);
                IPCOM_MIB2_PERIF_U32_ADD(v6, ipIfStatsOutFragCreates, 1, netif);
            }
#endif
        }
#endif /* IPCOM_USE_MIB2 */

        if (frag_pkt_head == IP_NULL)
            frag_pkt_head = frag_pkt;
        else
            frag_pkt_tail->next_fragment = frag_pkt;
        frag_pkt_tail = frag_pkt;

        IPCOM_PKT_TRACE(frag_pkt, IPCOM_PKT_ID_FRAG);
        frag_pkt->fd       = pkt->fd;
        frag_pkt->ifindex  = pkt->ifindex;
        frag_pkt->flags    = pkt->flags;
        frag_pkt->vr_index = pkt->vr_index;
        /* Copy IP packet payload */
        frag_pkt->start      = (frag_pkt->maxlen - size_of_this_fragment) & ~0x3;
        frag_pkt->end        = frag_pkt->start + size_of_this_fragment;
        ip_assert(frag_pkt->start < frag_pkt->maxlen);
        ipcom_memcpy(&frag_pkt->data[frag_pkt->start],
                     &pkt->data[pkt->start + frag_offset],
                     (Ip_size_t)size_of_this_fragment);

        frag_pkt->offset     = (Ip_u16) (frag_offset + pkt->offset);
        frag_offset += size_of_this_fragment;
        IP_BIT_SET(frag_pkt->flags, IPCOM_PKT_FLAG_MORE_FRAG);
    }

    if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_FRAGMENT)
        || (more_fragments && IP_FALSE == more_fragments(pkt)))
        /* This is either an IP datagram sourced from this node or the
           last fragment of an IP datagram that has been forwarded by
           this node. */
        IP_BIT_CLR(frag_pkt_tail->flags, IPCOM_PKT_FLAG_MORE_FRAG);

    ip_assert(pkt->start + frag_offset == pkt->end);
    ip_assert(frag_pkt_head != IP_NULL);
    if (IP_NULL != (ssb = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_SOCK_SND_BUF)))
    {
        /* Transfer the socket send buffer allocation from the
           original packet to the last fragment to make sure that the
           send flow control is still driven by TX done events. */
        ipcom_pkt_set_info(frag_pkt_tail,
                           IPNET_PKT_INFO_SOCK_SND_BUF,
                           sizeof(Ipnet_pkt_info_sock_snd_buf),
                           ssb);
        frag_pkt_tail->dtor = pkt->dtor;
        pkt->dtor = IP_NULL;
    }

    /* Do not free original yet, since it might hold the
       callers signal buffer. Put it in the fragment tails
       next original instead and it will be freed when the
       last segment has been transmitted */
    if (frag_pkt_tail != IP_NULL)
        frag_pkt_tail->next_original = pkt;
    else
        ipcom_pkt_free(pkt);

    return frag_pkt_head;
}