/************************************************************************
* NAME: fnet_igmp_leave
*
* DESCRIPTION: Sends a Leave Group message.
*************************************************************************/
void fnet_igmp_leave( fnet_netif_t *netif, fnet_ip4_addr_t  group_addr )
{
#if FNET_CFG_IGMP_VERSION == 2 
    fnet_netbuf_t *nb_header;
    fnet_igmp_header_t *igmp_header;
    fnet_ip4_addr_t dest_ip = FNET_IP4_ADDR_INIT(224, 0, 0, 2); /* All-routers multicast group.*/
    
    /* Construct IGMP header*/
    if((nb_header = fnet_netbuf_new(sizeof(fnet_igmp_header_t), FNET_FALSE)) != 0)
    {
        /*
         * When a host leaves a multicast group, if it was the last host to
         * reply to a Query with a Membership Report for that group, it SHOULD
         * send a Leave Group message to the all-routers multicast group (224.0.0.2).
         */

        igmp_header = nb_header->data_ptr;
        
        igmp_header->type = IGMP_HEADER_TYPE_LEAVE_GROUP; /* Type.*/ 
        igmp_header->max_resp_time = 0;             /* Unused field, zeroed when sent, ignored when received.*/
        igmp_header->group_addr = group_addr ;      /* Group address field.*/   
        
        
        igmp_header->checksum = 0;
        igmp_header->checksum = fnet_checksum(nb_header, (int)nb_header->total_length);

        /* RFC 1112: A Report is sent with an IP destination address equal to the
         * host group address being reported, and with an IP time-to-live of 1.
         */
        
        fnet_ip_output(netif, INADDR_ANY, dest_ip /*dest_addr*/, FNET_IP_PROTOCOL_IGMP, FNET_IGMP_TOS, FNET_IGMP_TTL, nb_header, 0, 0, 0);
    }
#endif /* FNET_CFG_IGMP_VERSION */
}
/************************************************************************
* NAME: fnet_igmp_join
*
* DESCRIPTION: Sends Host Membership Reports.
*************************************************************************/
void fnet_igmp_join( fnet_netif_t *netif, fnet_ip4_addr_t  group_addr )
{
    fnet_netbuf_t *nb_header;
    fnet_igmp_header_t *igmp_header;
    
    /* Construct IGMP header*/
    if((nb_header = fnet_netbuf_new(sizeof(fnet_igmp_header_t), FNET_FALSE)) != 0)
    {
        igmp_header = nb_header->data_ptr;
        /* Type.*/ 
#if FNET_CFG_IGMP_VERSION == 1        
        igmp_header->type = IGMP_HEADER_TYPE_REPORT_V1;
#else /* FNET_CFG_IGMP_VERSION == 2 */
        igmp_header->type = IGMP_HEADER_TYPE_REPORT_V2;
#endif           
        igmp_header->max_resp_time = 0;             /* Unused field, zeroed when sent, ignored when received.*/
        igmp_header->group_addr = group_addr ;      /* Group address field.*/   
        
        
        igmp_header->checksum = 0;
        igmp_header->checksum = fnet_checksum(nb_header, (int)nb_header->total_length);

        /* RFC 1112: A Report is sent with an IP destination address equal to the
         * host group address being reported, and with an IP time-to-live of 1.
         */
        fnet_ip_output(netif, INADDR_ANY, group_addr /*dest_addr*/, FNET_IP_PROTOCOL_IGMP, FNET_IGMP_TOS, FNET_IGMP_TTL, nb_header, 0, 0, 0);
    }
}
/************************************************************************
* NAME: fnet_arp_request
*
* DESCRIPTION: Sends ARP request.
*************************************************************************/
void fnet_arp_request( fnet_netif_t *netif, fnet_ip4_addr_t ipaddr )
{
    fnet_arp_header_t *arp_hdr;
    fnet_mac_addr_t sender_addr;

    fnet_netbuf_t *nb;

    if((nb = fnet_netbuf_new(sizeof(fnet_arp_header_t), FNET_TRUE)) != 0)
    {
        arp_hdr = nb->data_ptr;
        arp_hdr->hard_type = FNET_HTONS(FNET_ARP_HARD_TYPE); /* The type of hardware address (=1 for Ethernet).*/
        arp_hdr->prot_type = FNET_HTONS(FNET_ETH_TYPE_IP4);   /* The type of protocol address (=0x0800 for IP). */
        arp_hdr->hard_size = FNET_ARP_HARD_SIZE; /* The size in bytes of the hardware address (=6). */
        arp_hdr->prot_size = FNET_ARP_PROT_SIZE; /* The size in bytes of the protocol address (=4). */
        arp_hdr->op = FNET_HTONS(FNET_ARP_OP_REQUEST);       /* Opcode. */

        fnet_netif_get_hw_addr(netif, sender_addr, sizeof(fnet_mac_addr_t));

        fnet_memcpy(arp_hdr->target_hard_addr, fnet_eth_null_addr, sizeof(fnet_mac_addr_t));
        fnet_memcpy(arp_hdr->sender_hard_addr, sender_addr, sizeof(fnet_mac_addr_t));

        arp_hdr->targer_prot_addr = ipaddr;              /* Protocol address of target of this packet.*/
        arp_hdr->sender_prot_addr = netif->ip4_addr.address; /* Protocol address of sender of this packet.*/

        fnet_arp_trace("TX", arp_hdr); /* Print ARP header. */        
        
        ((fnet_eth_if_t *)(netif->if_ptr))->output(netif, FNET_ETH_TYPE_ARP, fnet_eth_broadcast, nb);
    }
}
Example #4
0
/************************************************************************
* NAME: fnet_netbuf_from_buf
*
* DESCRIPTION: Creates a new net_buf and fills it by a content of 
*              the external data buffer. 
*************************************************************************/
fnet_netbuf_t *fnet_netbuf_from_buf( void *data_ptr, int len, int drain )
{
    fnet_netbuf_t *nb;
    
    nb = fnet_netbuf_new(len, drain);

    if(nb)
        fnet_memcpy(nb->data_ptr, data_ptr, (unsigned int)len);

    return (nb);
}
Example #5
0
/************************************************************************
* NAME: fnet_netbuf_from_buf
*
* DESCRIPTION: Creates a new net_buf and fills it by a content of 
*              the external data buffer. 
*************************************************************************/
fnet_netbuf_t *fnet_netbuf_from_buf( void *data_ptr, fnet_size_t len, fnet_bool_t drain )
{
    fnet_netbuf_t *nb;
    
    nb = fnet_netbuf_new(len, drain);

    if(nb)
    {
        fnet_memcpy(nb->data_ptr, data_ptr, len);
    }

    return (nb);
}
Example #6
0
/************************************************************************
* NAME: fnet_icmp_error
*
* DESCRIPTION: Sends ICMP error message.
*************************************************************************/
void fnet_icmp_error( fnet_netif_t *netif, fnet_uint8_t type, fnet_uint8_t code, fnet_netbuf_t *nb )
{
    fnet_ip_header_t        *ipheader;
    fnet_netbuf_t           *nb_header;
    fnet_icmp_err_header_t  *icmpheader;
    fnet_ip4_addr_t          source_addr;
    fnet_ip4_addr_t          destination_addr;
    

    if(nb)
    {
        ipheader = (fnet_ip_header_t *)nb->data_ptr;
        
        source_addr = ipheader->source_addr;
        destination_addr = ipheader->desination_addr;

        /* Do not send error if not the first fragment of message (RFC1122)*/
        if((FNET_IP_HEADER_GET_OFFSET(ipheader) != 0u) ||
            /* Do not send error on ICMP error messages*/
            ((ipheader->protocol == FNET_IP_PROTOCOL_ICMP)
              && (!FNET_ICMP_IS_QUERY_TYPE(((fnet_icmp_header_t *)((fnet_uint8_t *)(nb->data_ptr) + (FNET_IP_HEADER_GET_HEADER_LENGTH(ipheader) << 2)))->type))) 
            /* Do not send error on a datagram whose source address does not define a single
             * host -- e.g., a zero address, a loopback address, a
             * broadcast address, a multicast address, or a Class E
             * address.*/
            || (fnet_ip_addr_is_broadcast(source_addr, netif))
            || FNET_IP4_ADDR_IS_MULTICAST(source_addr) 
            || FNET_IP4_CLASS_E(source_addr)
            /* Do not send error on a datagram destined to an IP broadcast or IP multicast address*/
            || (fnet_ip_addr_is_broadcast(destination_addr, netif))
            || FNET_IP4_ADDR_IS_MULTICAST(destination_addr)
             /* Do not send error on datagram sent as a link-layer broadcast or multicast.*/
            ||((nb->flags & FNET_NETBUF_FLAG_BROADCAST) != 0u) || ((nb->flags & FNET_NETBUF_FLAG_MULTICAST) != 0u)
        )
        {
            goto FREE_NB;
        }

        /* Construct ICMP error header*/
        if((nb_header = fnet_netbuf_new((sizeof(fnet_icmp_err_header_t) - sizeof(fnet_ip_header_t)), FNET_FALSE)) == 0)
        {
            goto FREE_NB;
        }

        icmpheader = (fnet_icmp_err_header_t *)nb_header->data_ptr;
        icmpheader->fields.unused = 0u;

        if(type == FNET_ICMP_PARAMPROB)
        {
            icmpheader->fields.ptr = fnet_htons((fnet_uint16_t)code);
            code = 0u;
        }
        else if((type == FNET_ICMP_PARAMPROB) && (code == FNET_ICMP_UNREACHABLE_NEEDFRAG) && netif)
        {    
            icmpheader->fields.mtu = fnet_htons((fnet_uint16_t)netif->mtu);
        }
        else
        {}

        icmpheader->header.type = type;
        icmpheader->header.code = code;

        if((fnet_size_t)((FNET_IP_HEADER_GET_HEADER_LENGTH(ipheader) << 2) + 8u) < nb->total_length)
        {
            fnet_netbuf_trim(&nb, (fnet_int32_t)((fnet_size_t)((FNET_IP_HEADER_GET_HEADER_LENGTH(ipheader) << 2) + 8u) - nb->total_length));
        }

        nb = fnet_netbuf_concat(nb_header, nb);

        fnet_icmp_output(netif, destination_addr, source_addr, nb);

        return;

FREE_NB:
        fnet_netbuf_free_chain(nb);
    }
}
Example #7
0
/************************************************************************
* NAME: fnet_netbuf_cut_center
*
* DESCRIPTION: Cuts len bytes in net_buf queue starting from offset "offset"
*
*************************************************************************/
fnet_netbuf_t *fnet_netbuf_cut_center( fnet_netbuf_t ** nb_ptr, fnet_size_t offset, fnet_size_t len )
{
    fnet_netbuf_t   *head_nb, *tmp_nb, *nb;
    fnet_size_t     tot_len;

    if(len == 0u)
    {
        return (0);
    }

    nb = (fnet_netbuf_t *) *nb_ptr;

    if((nb->total_length < (len + offset)) || (nb == 0))
    {
        return (0);
    }

    if(offset == 0u) /* The first case - when we cut from the begin of buffer.*/
    {
        fnet_netbuf_trim(&nb, (fnet_int32_t)len);
        *nb_ptr = nb;
        return (nb);
    }

    head_nb = nb;

    tmp_nb = nb;

    tot_len = nb->length;

    while((nb != 0) && (offset >= tot_len))
    {
        nb = nb->next;                             /* Run up th the first net_buf, which points */
        tot_len += nb->length;                     /* to the data, which should be erased.*/ 

        if((nb != 0) && (offset >= tot_len))
        {
            tmp_nb = nb;                          /* To store previous pointer. */
        }
    }

    if(tot_len - nb->length == offset)            /* If we start cut from the begin of buffer. */
    {
        nb->total_length = head_nb->total_length; /* For correct fnet_netbuf_trim execution.*/
        fnet_netbuf_trim(&tmp_nb->next, (fnet_int32_t)len);
        head_nb->total_length -= len;
        
        return ((fnet_netbuf_t *) *nb_ptr);
    }

    /* If only the middle of one net_buf should be cut.*/
    if(tot_len - offset > len)
    {
        head_nb->total_length -= len;

        /* Split one net_uf into two.*/
        if(((fnet_uint32_t *)nb->data)[0] == 1u) /* If we can simply erase them (reference_counter == 1).*/
        {
            fnet_memcpy((fnet_uint8_t *)nb->data_ptr + nb->length - tot_len + offset,
                        (fnet_uint8_t *)nb->data_ptr + nb->length - tot_len + offset + len,
                        (fnet_size_t)(tot_len - offset - len));
            nb->length -= len;
        }
        else
        {
            head_nb = fnet_netbuf_new(tot_len - offset - len, FNET_FALSE);

            if(head_nb == 0) /* If no free memory.*/
            {
                nb = (fnet_netbuf_t *) *nb_ptr;
                nb->total_length += len;
                return (0);
            }

            fnet_memcpy(head_nb->data_ptr,
                        (fnet_uint8_t *)nb->data_ptr + nb->length - tot_len + offset + len,
                        (fnet_size_t)(tot_len - offset - len));

            head_nb->next = nb->next;

            nb->next = head_nb;

            nb->length -= tot_len - offset;
        }

        return ((fnet_netbuf_t *) *nb_ptr);
    }

    if(tot_len - offset == len) /* If we cut from the middle of buffer to the end only.*/
    {
        nb->length -= len;

        head_nb->total_length -= len;

        return ((fnet_netbuf_t *) *nb_ptr);
    }
    else                                /* Cut from the middle of net_buf to the end and trim remaining info*/
    {                                   /* (tot_len-offset < len)*/
        nb->length -= tot_len - offset;

        nb->next->total_length = head_nb->total_length; /* For correct fnet_netbuf_trim execution. */
        fnet_netbuf_trim(&nb->next, (fnet_int32_t)(len - (tot_len - offset)));

        head_nb->total_length -= len;

        return ((fnet_netbuf_t *) *nb_ptr);
    }
}
Example #8
0
/************************************************************************
* NAME: fnet_icmp6_error
*
* DESCRIPTION: Sends ICMPv6 error message.
*************************************************************************/
void fnet_icmp6_error( fnet_netif_t *netif, unsigned char type, unsigned char code, unsigned long param, fnet_netbuf_t *origin_nb )
{
    fnet_ip6_header_t       *ip6_header;
    fnet_icmp6_err_header_t *icmp6_err_header;
    fnet_ip6_addr_t         *src_ip; 
    const fnet_ip6_addr_t   *dest_ip; 
    fnet_netbuf_t           *nb_header;   
    
    if(origin_nb)
    {
        /* Limit to FNET_IP6_DEFAULT_MTU. */
        if(origin_nb->total_length > (FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t))  ))
            fnet_netbuf_trim(&origin_nb, (int)(FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t)) - origin_nb->total_length));
        
        ip6_header = origin_nb->data_ptr;
        
        src_ip = &ip6_header->source_addr;
        dest_ip = &ip6_header->destination_addr;
        
        /*******************************************************************
         * RFC 4443:
         * (e) An ICMPv6 error message MUST NOT be originated as a result of
         * receiving the following:
         *******************************************************************/
        /* (e.1) An ICMPv6 error message. */ 
        /* (e.2) An ICMPv6 REDIRECT message [IPv6-DISC].*/
        if (ip6_header->next_header == FNET_IP_PROTOCOL_ICMP6) /* TBD Extension header case.*/
        {
            /* Make sure the packet has at least a 'TYPE' field */
            if (ip6_header->length == 0)
            {
                goto FREE_NB;
            }
            
            icmp6_err_header = (fnet_icmp6_err_header_t *)((unsigned char *)ip6_header + sizeof(fnet_ip6_header_t));
            if (FNET_ICMP6_TYPE_IS_ERROR(icmp6_err_header->icmp6_header.type) || (icmp6_err_header->icmp6_header.type == FNET_ICMP6_TYPE_REDIRECT ) )
            {
                goto FREE_NB;
            }
        }

        /*
         * (e.3) A packet destined to an IPv6 multicast address. (There are
         * two exceptions to this rule: (1) the Packet Too Big Message
         * (Section 3.2) to allow Path MTU discovery to work for IPv6
         * multicast, and (2) the Parameter Problem Message, Code 2
         * (Section 3.4) reporting an unrecognized IPv6 option (see
         * Section 4.2 of [IPv6]) that has the Option Type highestorder
         * two bits set to 10).
         * (e.4) A packet sent as a link-layer multicast (the exceptions
         * from e.3 apply to this case, too).     
         */
         if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip)  
            && !( (type == FNET_ICMP6_TYPE_PACKET_TOOBIG) 
                || ((type == FNET_ICMP6_TYPE_PARAM_PROB) && (code == FNET_ICMP6_CODE_PP_OPTION))) )
         {
            goto FREE_NB;
         }
         else
         {
            if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip))
            {
                /* We may not use multicast address as source. Get real source address. */
                dest_ip = fnet_ip6_select_src_addr(netif, src_ip /*dest*/); 
            }
         }    
             
        /*
         * (e.5) A packet sent as a link-layer broadcast (the exceptions
         *  from e.3 apply to this case, too). TBD
         */
      

        /*
         * (e.6) A packet whose source address does not uniquely identify a
         * single node -- e.g., the IPv6 Unspecified Address, an IPv6
         * multicast address, or an address known by the ICMP message
         * originator to be an IPv6 anycast address.
         */
        if(FNET_IP6_ADDR_IS_MULTICAST(src_ip) || FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_any, src_ip))
        {
            goto FREE_NB;
        }
     
        /* Construct ICMPv6 error header.*/
        if((nb_header = fnet_netbuf_new((sizeof(fnet_icmp6_err_header_t)), FNET_FALSE)) == 0)
            goto FREE_NB;     
        
        icmp6_err_header = nb_header->data_ptr;
        
        icmp6_err_header->icmp6_header.type = type;
        icmp6_err_header->icmp6_header.code = code;        
        icmp6_err_header->data = fnet_htonl(param); 
        
        origin_nb = fnet_netbuf_concat(nb_header, origin_nb);
        
        fnet_icmp6_output( netif, dest_ip/*ipsrc*/, src_ip/*ipdest*/, 0, origin_nb);        
            
        return;

FREE_NB:
        fnet_netbuf_free_chain(origin_nb);        
    
    }

}
Example #9
0
/************************************************************************
* NAME: fnet_mld_send
*
* DESCRIPTION: Sends MLD message defined by type:
*        FNET_ICMP6_TYPE_MULTICAST_LISTENER_REPORT or FNET_ICMP6_TYPE_MULTICAST_LISTENER_DONE
*************************************************************************/
static void fnet_mld_send(fnet_netif_t *netif, fnet_ip6_addr_t *group_addr, fnet_uint8_t type)
{
    fnet_netbuf_t                       *nb;
    fnet_netbuf_t                       *nb_option;
    fnet_mld_header_t                   *mld_header;
    fnet_mld_ra_option_header_t         *ra_option_header;
    FNET_COMP_PACKED_VAR fnet_uint16_t  *checksum_p;
    fnet_ip6_addr_t                     *ip_src;
    fnet_ip6_addr_t                     *ip_dst;

    /* [RFC2710] EXCLUDING the link-scope all-nodes address and any multicast
     * addresses of scope 0 (reserved) or 1(node-local).*/
    if((FNET_IP6_ADDR_MULTICAST_SCOPE(group_addr) > FNET_IP6_ADDR_SCOPE_INTERFACELOCAL)
            && !FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_linklocal_allnodes, group_addr))
    {
        /* Construct Router Alert option + MLD meassage */
        if((nb = fnet_netbuf_new(sizeof(fnet_mld_header_t), FNET_FALSE)) != 0)
        {
            if((nb_option = fnet_netbuf_new(sizeof(fnet_mld_ra_option_header_t), FNET_FALSE)) != 0)
            {
                /* Fill Hop-by_Hop Options header.*/
                ra_option_header = (fnet_mld_ra_option_header_t  *)(nb_option->data_ptr);
                fnet_memcpy ((void *)ra_option_header, (void *)(&mld_ra_option), sizeof(mld_ra_option));

                /* Fill MLD message. */
                mld_header = (fnet_mld_header_t  *)(nb->data_ptr);
                fnet_memset_zero(mld_header, sizeof(fnet_mld_header_t));
                mld_header->icmp6_header.type = type;
                FNET_IP6_ADDR_COPY(group_addr, &mld_header->multicast_addr);

                /* Checksum calculation.*/
                mld_header->icmp6_header.checksum = 0;
                mld_header->icmp6_header.checksum = fnet_checksum_pseudo_start(nb, FNET_HTONS((fnet_uint16_t)FNET_IP_PROTOCOL_ICMP6), (fnet_uint16_t)nb->total_length);
                checksum_p = &mld_header->icmp6_header.checksum;

                /* Concatanate Hop-by_Hop Options with MLD header. */
                nb = fnet_netbuf_concat(nb_option, nb);

                /* Source Address Selection for MLD, by RFC3590.*/

                /* [RFC3590] MLD Report and Done messages are sent with a link-local address as
                 * the IPv6 source address, if a valid address is available on the interface.*/
                ip_src = fnet_netif_get_ip6_addr_valid_link_local(netif);

                /* [RFC3590] If a valid link-local address is not available (e.g., one has not been configured),
                 * the message is sent with the unspecified address. */
                if(ip_src == FNET_NULL)
                {
                    ip_src = (fnet_ip6_addr_t *)&fnet_ip6_addr_any;
                    netif->mld_invalid = FNET_TRUE;
                }
                else
                {
                    netif->mld_invalid = FNET_FALSE;
                }

                /* When a node ceases to listen to a multicast address on an interface,
                 * it SHOULD send a single Done message to the link-scope all-routers
                 * multicast address (FF02::2)*/
                if(type == FNET_ICMP6_TYPE_MULTICAST_LISTENER_DONE)
                {
                    ip_dst =(fnet_ip6_addr_t *)&fnet_ip6_addr_linklocal_allrouters;
                }
                else
                {
                    ip_dst = group_addr;
                }

                /* Send via IPv6*/
                fnet_ip6_output(netif, ip_src, ip_dst, FNET_IP6_TYPE_HOP_BY_HOP_OPTIONS, FNET_MLD_HOP_LIMIT, nb, checksum_p);
            }
            else
            {
                fnet_netbuf_free_chain(nb);
            }
        }
    }
}
Example #10
0
/************************************************************************
* NAME: fnet_udp_output
*
* DESCRIPTION: UDP output function
*************************************************************************/
static fnet_error_t fnet_udp_output(  struct sockaddr *src_addr, const struct sockaddr *dest_addr,
                             fnet_socket_option_t *sockoption, fnet_netbuf_t *nb )                            
{
    fnet_netbuf_t                           *nb_header;
    fnet_udp_header_t                       *udp_header;
    fnet_error_t                            error =  FNET_ERR_OK;
    FNET_COMP_PACKED_VAR fnet_uint16_t      *checksum_p;
    fnet_netif_t                            *netif = FNET_NULL;
    fnet_scope_id_t                         scope_id = 0u;

    /* Check Scope ID.*/
    if(dest_addr->sa_scope_id) /* Take scope id from destination address.*/
    {
        scope_id = dest_addr->sa_scope_id;
    }
    else  /* Take scope id from source address.*/
    {
        scope_id = src_addr->sa_scope_id;
    }
    netif = (fnet_netif_t *)fnet_netif_get_by_scope_id(scope_id); /* It can be FNET_NULL, in case scope_id is 0.*/

    /* Construct UDP header.*/
    if((nb_header = fnet_netbuf_new(sizeof(fnet_udp_header_t), FNET_TRUE)) == 0)
    {
        fnet_netbuf_free_chain(nb); 
        return (FNET_ERR_NOMEM);
    }

    udp_header = (fnet_udp_header_t *)nb_header->data_ptr;

    udp_header->source_port = src_addr->sa_port;             /* Source port number.*/
    udp_header->destination_port = dest_addr->sa_port;       /* Destination port number.*/
    nb = fnet_netbuf_concat(nb_header, nb);
    udp_header->length = fnet_htons((fnet_uint16_t)nb->total_length);  /* Length.*/

    /* Checksum calculation.*/
    udp_header->checksum = 0u;  

#if FNET_CFG_UDP_CHECKSUM

#if FNET_CFG_CPU_ETH_HW_TX_IP_CHECKSUM
    if( 0 
    #if FNET_CFG_IP4
        ||( (dest_addr->sa_family == AF_INET) 
        && ((netif = fnet_ip_route(((struct sockaddr_in *)(dest_addr))->sin_addr.s_addr))!= FNET_NULL)
        && (netif->features & FNET_NETIF_FEATURE_HW_TX_PROTOCOL_CHECKSUM)
        && (fnet_ip_will_fragment(netif, nb->total_length) == FNET_FALSE) /* Fragmented packets are not inspected.*/  ) 
    #endif
    #if FNET_CFG_IP6
        ||( (dest_addr->sa_family == AF_INET6) 
        && (netif || (((netif = fnet_ip6_route(&((struct sockaddr_in6 *)(src_addr))->sin6_addr.s6_addr, &((struct sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr)))!= FNET_NULL) )
        && (netif->features & FNET_NETIF_FEATURE_HW_TX_PROTOCOL_CHECKSUM)
        && (fnet_ip6_will_fragment(netif, nb->total_length) == FNET_FALSE) /* Fragmented packets are not inspected.*/  ) 
    #endif
    )
    {
        nb->flags |= FNET_NETBUF_FLAG_HW_PROTOCOL_CHECKSUM;
        checksum_p = 0;
    }
    else
#endif /* FNET_CFG_CPU_ETH_HW_TX_IP_CHECKSUM */
    {
        udp_header->checksum = fnet_checksum_pseudo_start( nb, FNET_HTONS((fnet_uint16_t)FNET_IP_PROTOCOL_UDP), (fnet_uint16_t)nb->total_length );
        checksum_p = &udp_header->checksum;
    }
#endif /* FNET_CFG_UDP_CHECKSUM */

#if FNET_CFG_IP4
    if(dest_addr->sa_family == AF_INET)
    {
        error = fnet_ip_output(netif, 
                                ((const struct sockaddr_in *)(src_addr))->sin_addr.s_addr, 
                                ((const struct sockaddr_in *)(dest_addr))->sin_addr.s_addr, 
                                FNET_IP_PROTOCOL_UDP, sockoption->ip_opt.tos,
                            #if FNET_CFG_MULTICAST
                                (fnet_uint8_t)((FNET_IP4_ADDR_IS_MULTICAST(((const struct sockaddr_in *)(dest_addr))->sin_addr.s_addr)?sockoption->ip_opt.ttl_multicast:sockoption->ip_opt.ttl)),                               
                            #else
                                sockoption->ip_opt.ttl, 
                            #endif /* FNET_CFG_MULTICAST */                               
                                nb, FNET_UDP_DF, sockoption->so_dontroute,
                                checksum_p
                                );
    }
    else
#endif
#if FNET_CFG_IP6    
    if(dest_addr->sa_family == AF_INET6)
    {
        error = fnet_ip6_output( netif, 
                                fnet_socket_addr_is_unspecified(src_addr)? FNET_NULL : &((struct sockaddr_in6 *)(src_addr))->sin6_addr.s6_addr, 
                                &((const struct sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr, 
                                FNET_IP_PROTOCOL_UDP, 
                                FNET_IP6_ADDR_IS_MULTICAST(&((const struct sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr)?sockoption->ip6_opt.hops_multicast:sockoption->ip6_opt.hops_unicast,
                                nb, 
                                checksum_p
                                );
    }
    else
#endif                               
    {}

    return (error);
}