예제 #1
0
void fnet_eth_output_ip4(fnet_netif_t *netif, fnet_ip4_addr_t dest_ip_addr, fnet_netbuf_t* nb)
{
    fnet_mac_addr_t destination_addr; /* 48-bit destination address */
    fnet_mac_addr_t * dest_ptr;
  
    /* Construct Ethernet header. Start with looking up deciding which
    * MAC address to use as a destination address. Broadcasts and
    * multicasts are special, all other addresses are looked up in the
    * ARP table. */
    if(fnet_ip_addr_is_broadcast (dest_ip_addr, netif))
    {
        fnet_memcpy (destination_addr, fnet_eth_broadcast, sizeof(fnet_mac_addr_t));
    }
    else if(FNET_IP4_ADDR_IS_MULTICAST(dest_ip_addr))
    {
        /* Hash IP multicast address to MAC address. */
        destination_addr[0] = 0x01;
        destination_addr[1] = 0x0;
        destination_addr[2] = 0x5e;
        destination_addr[3] = (unsigned char)(FNET_IP4_ADDR2(dest_ip_addr)& 0x7f);
        destination_addr[4] = (unsigned char)(FNET_IP4_ADDR3(dest_ip_addr));
        destination_addr[5] = (unsigned char)(FNET_IP4_ADDR4(dest_ip_addr));
        //TBD Use macro
    }
    else
    /* Unicast address. */
    {
        if((dest_ptr = fnet_arp_lookup(netif, dest_ip_addr))!=0)
        {
            fnet_memcpy (destination_addr, *dest_ptr, sizeof(fnet_mac_addr_t));
        }
        else
        {
            fnet_arp_resolve(netif, dest_ip_addr, nb);
            goto EXIT;
        }
    }

    /* Send Ethernet frame. */
    ((fnet_eth_if_t *)(netif->if_ptr))->output(netif, FNET_ETH_TYPE_IP4, destination_addr, nb);
EXIT:
    return;    
}
예제 #2
0
/************************************************************************
* NAME: fnet_raw_output
*
* DESCRIPTION: RAW output function
*************************************************************************/
static int fnet_raw_output(  struct sockaddr *src_addr, const struct sockaddr *dest_addr, unsigned char protocol_number,
                             fnet_socket_option_t *sockoption, fnet_netbuf_t *nb )                            
{
    int error =  FNET_OK;
    
    fnet_netif_t *netif = FNET_NULL;


#if FNET_CFG_IP4
    if(dest_addr->sa_family == AF_INET)
    {
        error = fnet_ip_output(netif, ((struct sockaddr_in *)(src_addr))->sin_addr.s_addr, 
                                        ((const struct sockaddr_in *)(dest_addr))->sin_addr.s_addr, 
                                        protocol_number, 
                                        sockoption->ip_opt.tos,
                                #if FNET_CFG_MULTICAST
                                    (unsigned char)((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, 0, ((sockoption->flags & SO_DONTROUTE) > 0),
                                   0
                                   );
    }
#endif
   
#if FNET_CFG_IP6    
    if(dest_addr->sa_family == AF_INET6)
    {
        /* Check Scope ID.*/
        netif = fnet_netif_get_by_scope_id( ((const struct sockaddr_in6 *)dest_addr)->sin6_scope_id );
        
        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, 
                                protocol_number, 
                                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,0);
    }
#endif                               

    return (error);
}
예제 #3
0
void fnet_eth_output_ip4(fnet_netif_t *netif, fnet_ip4_addr_t dest_ip_addr, fnet_netbuf_t *nb)
{
    fnet_mac_addr_t destination_addr; /* 48-bit destination address */

    /* Construct Ethernet header. Start with looking up deciding which
    * MAC address to use as a destination address. Broadcasts and
    * multicasts are special, all other addresses are looked up in the
    * ARP table. */
    if(fnet_ip_addr_is_broadcast (dest_ip_addr, netif))
    {
        fnet_memcpy (destination_addr, fnet_eth_broadcast, sizeof(fnet_mac_addr_t));
    }
    else if(FNET_IP4_ADDR_IS_MULTICAST(dest_ip_addr))
    {
        /* Hash IP multicast address to MAC address. */
        destination_addr[0] = 0x01U;
        destination_addr[1] = 0x0U;
        destination_addr[2] = 0x5eU;
        destination_addr[3] = (fnet_uint8_t)(FNET_IP4_ADDR2(dest_ip_addr) & 0x7fU);
        destination_addr[4] = (fnet_uint8_t)(FNET_IP4_ADDR3(dest_ip_addr));
        destination_addr[5] = (fnet_uint8_t)(FNET_IP4_ADDR4(dest_ip_addr));
        /* TBD Use macro. */
    }
    else
        /* Unicast address. */
    {
        if(fnet_arp_get_mac( (fnet_netif_desc_t) netif, dest_ip_addr, destination_addr) == FNET_FALSE)
        {
            fnet_arp_resolve(netif, dest_ip_addr, nb);
            goto EXIT;
        }

    }

    /* Send Ethernet frame. */
    fnet_eth_output(netif, FNET_ETH_TYPE_IP4, destination_addr, nb);
EXIT:
    return;
}
예제 #4
0
파일: fnet_raw.c 프로젝트: butok/FNET
/************************************************************************
* DESCRIPTION: RAW output function
*************************************************************************/
static fnet_error_t fnet_raw_output(  struct sockaddr *src_addr, const struct sockaddr *dest_addr, fnet_uint8_t protocol_number,
                                      fnet_socket_option_t *sockoption, fnet_netbuf_t *nb )
{
    fnet_error_t error =  FNET_ERR_OK;

    fnet_netif_t *netif =  (fnet_netif_t *)fnet_netif_get_by_scope_id( dest_addr->sa_scope_id );

#if FNET_CFG_IP4
    if(dest_addr->sa_family == AF_INET)
    {
        error = fnet_ip_output(netif, ((struct sockaddr_in *)(src_addr))->sin_addr.s_addr,
                               ((const struct sockaddr_in *)(dest_addr))->sin_addr.s_addr,
                               protocol_number,
                               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_FALSE, sockoption->so_dontroute,
                               0
                              );
    }
#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,
                                 protocol_number,
                                 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, 0);
    }
#endif

    return (error);
}
예제 #5
0
파일: fnet_icmp.c 프로젝트: rschuck/K64f
/************************************************************************
* NAME: fnet_icmp_input
*
* DESCRIPTION: ICMP input function.
*************************************************************************/
static void fnet_icmp_input(fnet_netif_t *netif, struct sockaddr *src_addr,  struct sockaddr *dest_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip4_nb)
{
    fnet_icmp_header_t      *hdr;
    fnet_prot_notify_t      prot_cmd;
    fnet_ip4_addr_t         src_ip;
    fnet_ip4_addr_t         dest_ip;
    
    fnet_netbuf_free_chain(ip4_nb); /* Not used.*/
    
    if((netif != 0) && (nb != 0) )
    {
        if(fnet_netbuf_pullup(&nb, sizeof(fnet_icmp_header_t)) == FNET_ERR) /* The header must reside in contiguous area of memory. */
        {
            goto DISCARD;
        }

        hdr = (fnet_icmp_header_t *)nb->data_ptr;

        src_ip = ((struct sockaddr_in*)(src_addr))->sin_addr.s_addr;
        dest_ip = ((struct sockaddr_in*)(dest_addr))->sin_addr.s_addr;

        if(
        #if FNET_CFG_CPU_ETH_HW_RX_PROTOCOL_CHECKSUM || FNET_CFG_CPU_ETH_HW_TX_PROTOCOL_CHECKSUM
            ((nb->flags & FNET_NETBUF_FLAG_HW_PROTOCOL_CHECKSUM) == 0) &&
        #endif
            (fnet_checksum(nb, nb->total_length))
            || (fnet_ip_addr_is_broadcast(src_ip, netif))
            || FNET_IP4_ADDR_IS_MULTICAST(src_ip))
        {
            goto DISCARD;
        }
        
        fnet_icmp_trace("RX", hdr); 
        
        switch(hdr->type)
        {
            /**************************
             * ICMP Request Processing
             **************************/
            case FNET_ICMP_ECHO:
                if((nb->total_length < sizeof(fnet_icmp_echo_header_t)) ||
                /* An ICMP Echo Request destined to an IP broadcast or IP
                * multicast address MAY be silently discarded.(RFC1122)*/
                (fnet_ip_addr_is_broadcast(dest_ip, netif)) || FNET_IP4_ADDR_IS_MULTICAST(dest_ip))
                {
                    goto DISCARD;
                }

                hdr->type = FNET_ICMP_ECHOREPLY;

                fnet_icmp_output(netif, dest_ip, src_ip, nb);
                break;
#if 0 /* Optional functionality.*/                
            /************************
             * Time Stamp Query 
             ************************/
            case FNET_ICMP_TSTAMP:
 
                /* The header must reside in contiguous area of memory. */
                if(fnet_netbuf_pullup(&nb, sizeof(fnet_icmp_timestamp_header_t)) == FNET_ERR)
                {
                    goto DISCARD;
                }

                hdr = nb->data_ptr;

                hdr->type = FNET_ICMP_TSTAMPREPLY;
                /* All times are in milliseconds since the stack timer start modulo 1 day. 
                * The high-order bit is set as the time value is recorded in nonstandard units. */
                ((fnet_icmp_timestamp_header_t *)hdr)->receive_timestamp
                    = fnet_htonl(((fnet_timer_ticks() * FNET_TIMER_PERIOD_MS) % (24 * 60 * 60 * 1000)) | (0x80000000));

                dest_ip = netif->ip4_addr.address;

                fnet_icmp_output(netif, dest_ip, src_ip, nb);
                break;
            /************************
             * Address Mask Query
             ************************/
            case FNET_ICMP_MASKREQ:
                /* The header must reside in contiguous area of memory*/
                if(fnet_netbuf_pullup(&nb, sizeof(fnet_icmp_mask_header_t)) == FNET_ERR) 
                {
                    goto DISCARD;
                }

                hdr = nb->data_ptr;

                hdr->type = FNET_ICMP_MASKREPLY;

                ((fnet_icmp_mask_header_t *)hdr)->mask = netif->ip4_addr.subnetmask;

                dest_ip = netif->ip4_addr.address;

                fnet_icmp_output(netif, dest_ip, src_ip, nb);
                break;
#endif                
            /**************************
             * ICMP Error Processing
             **************************/
            case FNET_ICMP_UNREACHABLE:
                switch(hdr->code)
                {
                    case FNET_ICMP_UNREACHABLE_NET:           /* net unreachable */
                    case FNET_ICMP_UNREACHABLE_NET_UNKNOWN:   /* unknown net */
                    case FNET_ICMP_UNREACHABLE_NET_PROHIB:    /* prohibited access */
                    case FNET_ICMP_UNREACHABLE_TOSNET:        /* bad tos for net */
                        prot_cmd = FNET_PROT_NOTIFY_UNREACH_NET;
                        break;

                    case FNET_ICMP_UNREACHABLE_HOST:          /* host unreachable */
                    case FNET_ICMP_UNREACHABLE_HOST_UNKNOWN:  /* unknown host */
                    case FNET_ICMP_UNREACHABLE_ISOLATED:      /* src host isolated */
                    case FNET_ICMP_UNREACHABLE_HOST_PROHIB:   /* ditto */
                    case FNET_ICMP_UNREACHABLE_TOSHOST:       /* bad tos for host */
                        prot_cmd = FNET_PROT_NOTIFY_UNREACH_HOST;
                        break;

                    case FNET_ICMP_UNREACHABLE_PROTOCOL:      /* protocol unreachable */
                        prot_cmd = FNET_PROT_NOTIFY_UNREACH_PROTOCOL;
                        break;

                    case FNET_ICMP_UNREACHABLE_PORT:          /* port unreachable */
                        prot_cmd = FNET_PROT_NOTIFY_UNREACH_PORT;
                        break;

                    case FNET_ICMP_UNREACHABLE_SRCFAIL:       /* source route failed */
                        prot_cmd = FNET_PROT_NOTIFY_UNREACH_SRCFAIL;
                        break;

                    case FNET_ICMP_UNREACHABLE_NEEDFRAG:      /* fragmentation needed and DF set*/
                        prot_cmd = FNET_PROT_NOTIFY_MSGSIZE;
                        break;

                    default:
                        goto DISCARD;
                }
                fnet_icmp_notify_protocol(prot_cmd, nb);  /* Protocol notification.*/
                break;
            case FNET_ICMP_TIMXCEED:
                switch(hdr->code)
                {
                    case FNET_ICMP_TIMXCEED_INTRANS:          /* time to live exceeded in transit (ttl==0)*/
                        prot_cmd = FNET_PROT_NOTIFY_TIMXCEED_INTRANS;
                        break;

                    case FNET_ICMP_TIMXCEED_REASS:            /* fragment reassembly time exceeded (ttl==0)*/
                        prot_cmd = FNET_PROT_NOTIFY_TIMXCEED_REASS;
                        break;

                    default:
                        goto DISCARD;
                }

                fnet_icmp_notify_protocol(prot_cmd, nb);  /* Protocol notification.*/
                break;
            case FNET_ICMP_PARAMPROB:                       /* Parameter Problem Message.*/
                if(hdr->code > 1u)
                {
                    goto DISCARD;
                }

                prot_cmd = FNET_PROT_NOTIFY_PARAMPROB;
                fnet_icmp_notify_protocol(prot_cmd, nb);  /* Protocol notification.*/
                break;
            case FNET_ICMP_SOURCEQUENCH:                    /* Source Quench Message; packet lost, slow down.*/
                if(hdr->code)
                {
                    goto DISCARD;
                }

                prot_cmd = FNET_PROT_NOTIFY_QUENCH;
                fnet_icmp_notify_protocol(prot_cmd, nb);  /* Protocol notification.*/
                break;
            /************************
             * Ignore others
             ************************/
            /*
            case FNET_ICMP_REDIRECT:
            case FNET_ICMP_ECHOREPLY:
            case FNET_ICMP_ROUTERADVERT:
            case FNET_ICMP_ROUTERSOLICIT:
            case FNET_ICMP_TSTAMPREPLY:
            case FNET_ICMP_IREQREPLY:
            case FNET_ICMP_MASKREPLY:*/
            default:
                goto DISCARD;
                
        }/* switch(hdr->type) */
    }
    else
    {
DISCARD:
        fnet_netbuf_free_chain(nb);
    }
}
예제 #6
0
파일: fnet_icmp.c 프로젝트: rschuck/K64f
/************************************************************************
* 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);
    }
}
예제 #7
0
/************************************************************************
* NAME: fnet_igmp_input
*
* DESCRIPTION: IGMP input function.
*************************************************************************/
static void fnet_igmp_input( fnet_netif_t *netif, fnet_ip4_addr_t src_ip, fnet_ip4_addr_t dest_ip, fnet_netbuf_t *nb, fnet_netbuf_t *ip4_nb)
{
    fnet_igmp_header_t *hdr;
    fnet_netbuf_t *tmp_nb;
    int i;
    
    FNET_COMP_UNUSED_ARG(dest_ip);
    FNET_COMP_UNUSED_ARG(src_ip);
    
    fnet_netbuf_free_chain(ip4_nb);

    if((netif != 0) && (nb != 0) )
    {
        /* The header must reside in contiguous area of memory. */
        if((tmp_nb = fnet_netbuf_pullup(nb, sizeof(fnet_igmp_header_t))) == 0) 
        {
            goto DISCARD;
        }

        nb = tmp_nb;

        hdr = nb->data_ptr;
        
        /* RFC2236 To be valid, the Query message
         * must be at least 8 octets long, have a correct IGMP
         * checksum.
         */
        if(fnet_checksum(nb, (int)nb->total_length)  )
        {
            goto DISCARD;
        }
        
        fnet_igmp_trace("RX", hdr); 

        /**************************
        * IGMP QUERY Processing
        **************************/     
        if(hdr->type == IGMP_HEADER_TYPE_QUERY)
        {
            /* RFC2236: The group address in the IGMP header must either be zero (a General
             * Query) or a valid multicast group address (a Group-Specific Query).
             * A General Query applies to all memberships on the interface from
             * which the Query is received. A Group-Specific Query applies to
             * membership in a single group on the interface from which the Query
             * is received. Queries are ignored for memberships in the Non-Member
             * state.
             */
            if(hdr->group_addr == 0)
            /* General Query */
            {
                 /* Find all joined-groups for this interface.*/
                for(i=0; i < FNET_CFG_MULTICAST_MAX; i++)
                {
                    if((fnet_ip_multicast_list[i].user_counter > 0) && (fnet_ip_multicast_list[i].netif == netif))
                    {
                        /* Send report.*/
                        fnet_igmp_join(netif, fnet_ip_multicast_list[i].group_addr );
                    }
                }
            }
        #if FNET_CFG_IGMP_VERSION == 2                    
            else if(FNET_IP4_ADDR_IS_MULTICAST(hdr->group_addr))
            /* A Group-Specific Query.*/ 
            {
                /* Find specific group.*/
                for(i=0; i < FNET_CFG_MULTICAST_MAX; i++)
                {
                    if((fnet_ip_multicast_list[i].user_counter > 0) && (fnet_ip_multicast_list[i].netif == netif) && (fnet_ip_multicast_list[i].group_addr == hdr->group_addr))
                    {
                        /* Send report.*/
                        fnet_igmp_join(netif, fnet_ip_multicast_list[i].group_addr );
                        break;
                    }
                }
            }
        #endif /* FNET_CFG_IGMP_VERSION */                
            /* esle wrong */
        }
        /************************
         * Ignore others
         ************************/
    }

DISCARD:
    fnet_netbuf_free_chain(nb);
}
예제 #8
0
파일: fapp_bench.c 프로젝트: ErikZalm/fnet
/************************************************************************
* NAME: fapp_benchrx_cmd
*
* DESCRIPTION: Start RX Benchmark server. 
************************************************************************/
void fapp_benchrx_cmd( fnet_shell_desc_t desc, int argc, char ** argv )
{
    fnet_address_family_t family;

    family = AF_SUPPORTED;            
        
    /* TCP */
    if((argc == 1)||(argc == 2 && fnet_strcasecmp("tcp", argv[1]) == 0)) 
    {
        fapp_bench_tcp_rx(desc, family);
    }
    /* UDP */
    else if(((argc == 2) || (argc == 3)) && fnet_strcasecmp("udp", argv[1]) == 0) 
    {
        fnet_ip4_addr_t multicast_address = 0;
        
        if(argc == 3) /* Multicast group address.*/
        {
            if((fnet_inet_aton(argv[2], (struct in_addr *) &multicast_address) == FNET_ERR) || !FNET_IP4_ADDR_IS_MULTICAST(multicast_address))
            {
                fnet_shell_println(desc, FAPP_PARAM_ERR, argv[2]);
                return;
            }
        }
        
        fapp_bench_udp_rx(desc, family, multicast_address);
    }
    else
    {
        fnet_shell_println(desc, FAPP_PARAM_ERR, argv[1]);
    }
}
예제 #9
0
파일: fnet_udp.c 프로젝트: 8bitgeek/fnet
/************************************************************************
* 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);
}