/************************************************************************ * NAME: fnet_mld_query_receive * * DESCRIPTION: Handles received Multicast Listener Query message. *************************************************************************/ void fnet_mld_query_receive(fnet_netif_t *netif, fnet_ip6_addr_t *src_ip, fnet_ip6_addr_t *dest_ip, fnet_netbuf_t *nb, fnet_netbuf_t *ip6_nb) { fnet_mld_header_t *mld_packet = nb->data_ptr; fnet_size_t mld_packet_size = nb->total_length; fnet_ip6_header_t *ip6_packet = (fnet_ip6_header_t *)ip6_nb->data_ptr; FNET_COMP_UNUSED_ARG(dest_ip); /************************************************************ * Validation of MLD Query Message. *************************************************************/ if( (mld_packet_size < sizeof(fnet_mld_header_t)) /* Validation RFC2710.*/ ||(ip6_packet->hop_limit != FNET_MLD_HOP_LIMIT) /* The IP Hop Limit field has a value of 1.*/ || !FNET_IP6_ADDR_IS_LINKLOCAL(src_ip) /* MUST be the link-local address.*/ ) { goto DROP; } /* [RFC2710] The Multicast Address field in the MLD * message must contain either zero (a General Query) or a valid * multicast address (a Multicast- Address-Specific Query).*/ if(FNET_IP6_ADDR_IS_MULTICAST(&mld_packet->multicast_addr)) { /* [RFC2710] A Multicast-Address-Specific * Query applies to a single multicast address on the interface from * which the Query is received. */ if(fnet_ip6_multicast_find_entry(netif, &mld_packet->multicast_addr)) fnet_mld_join(netif, &mld_packet->multicast_addr); } else if(FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_any, &mld_packet->multicast_addr)) { /* [RFC2710] General Query applies to all multicast addresses on the interface * from which the Query is received. */ fnet_mld_report_all(netif); } else {} DROP: fnet_netbuf_free_chain(ip6_nb); fnet_netbuf_free_chain(nb); }
/************************************************************************ * 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); }
/************************************************************************ * 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); }
/************************************************************************ * NAME: fnet_icmp6_input * * DESCRIPTION: ICMPv6 input function. *************************************************************************/ static void fnet_icmp6_input(fnet_netif_t *netif, struct sockaddr *src_addr, struct sockaddr *dest_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip6_nb) { fnet_icmp6_header_t *hdr; fnet_netbuf_t *tmp_nb; unsigned short sum; fnet_ip6_addr_t *src_ip; fnet_ip6_addr_t *dest_ip; fnet_prot_notify_t prot_cmd; if((netif != 0) && (nb != 0)) { /* The header must reside in contiguous area of memory. */ if((tmp_nb = fnet_netbuf_pullup(nb, sizeof(fnet_icmp6_header_t))) == 0) { goto DISCARD; } nb = tmp_nb; hdr = nb->data_ptr; dest_ip = &((struct sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr; src_ip = &((struct sockaddr_in6 *)(src_addr))->sin6_addr.s6_addr; /* Drop Multicast loopback.*/ #if FNET_CFG_LOOPBACK if ((netif == FNET_LOOP_IF) && FNET_IP6_ADDR_IS_MULTICAST(dest_ip)) { goto DISCARD; } #endif /* FNET_CFG_LOOPBACK */ /* Verify the checksum. */ sum = fnet_checksum_pseudo_start( nb, FNET_HTONS((unsigned short)FNET_IP_PROTOCOL_ICMP6), (unsigned short)nb->total_length ); sum = fnet_checksum_pseudo_end( sum, (char *)src_ip, (char *)dest_ip, sizeof(fnet_ip6_addr_t) ); if(sum) goto DISCARD; /************************************************************ * Process incoming ICMPv6 packets. *************************************************************/ switch (hdr->type) { /************************** * Neighbor Solicitation. **************************/ case FNET_ICMP6_TYPE_NEIGHBOR_SOLICITATION: fnet_nd6_neighbor_solicitation_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Neighbor Advertisemnt. **************************/ case FNET_ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: fnet_nd6_neighbor_advertisement_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Router Advertisemnt. **************************/ case FNET_ICMP6_TYPE_ROUTER_ADVERTISEMENT: fnet_nd6_router_advertisement_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Router Advertisemnt. **************************/ case FNET_ICMP6_TYPE_REDIRECT: fnet_nd6_redirect_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; #if FNET_CFG_MLD /************************** * Multicast Listener Query. **************************/ case FNET_ICMP6_TYPE_MULTICAST_LISTENER_QUERY: fnet_mld_query_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; #endif /************************** * Echo Request. * RFC4443 4.1: Every node MUST implement an ICMPv6 Echo responder function that * receives Echo Requests and originates corresponding Echo Replies. **************************/ case FNET_ICMP6_TYPE_ECHO_REQ: hdr->type = FNET_ICMP6_TYPE_ECHO_REPLY; /* RFC4443: the source address of the reply MUST be a unicast * address belonging to the interface on which * the Echo Request message was received.*/ if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip)) dest_ip = FNET_NULL; fnet_icmp6_output(netif, dest_ip/*ipsrc*/, src_ip/*ipdest*/, 0, nb); fnet_netbuf_free_chain(ip6_nb); break; #if FNET_CFG_IP6_PMTU_DISCOVERY /************************** * Packet Too Big Message. **************************/ case FNET_ICMP6_TYPE_PACKET_TOOBIG: if(netif->pmtu) /* If PMTU is enabled for the interface.*/ { unsigned long pmtu; fnet_icmp6_err_header_t *icmp6_err = nb->data_ptr; /* The header must reside in contiguous area of memory. */ if((tmp_nb = fnet_netbuf_pullup(nb, sizeof(fnet_icmp6_err_header_t))) == 0) { goto DISCARD; } nb = tmp_nb; /* RFC 1981.Upon receipt of such a * message, the source node reduces its assumed PMTU for the path based * on the MTU of the constricting hop as reported in the Packet Too Big * message.*/ pmtu = fnet_ntohl(icmp6_err->data); /* A node MUST NOT increase its estimate of the Path MTU in response to * the contents of a Packet Too Big message. */ if(netif->pmtu > pmtu) { fnet_netif_set_pmtu(netif, pmtu); } } goto DISCARD; #endif /************************** * Destination Unreachable. **************************/ case FNET_ICMP6_TYPE_DEST_UNREACH: switch(hdr->code) { case FNET_ICMP6_CODE_DU_NO_ROUTE: /* No route to destination. */ case FNET_ICMP6_CODE_DU_BEYOND_SCOPE: /* Beyond scope of source address.*/ prot_cmd = FNET_PROT_NOTIFY_UNREACH_NET; break; case FNET_ICMP6_CODE_DU_ADMIN_PROHIBITED: /* Communication with destination administratively prohibited. */ case FNET_ICMP6_CODE_DU_ADDR_UNREACH: /* Address unreachable.*/ prot_cmd = FNET_PROT_NOTIFY_UNREACH_HOST; break; case FNET_ICMP6_CODE_DU_PORT_UNREACH: /* Port unreachable.*/ prot_cmd = FNET_PROT_NOTIFY_UNREACH_PORT; break; default: goto DISCARD; } /* Protocol notification.*/ { fnet_ip6_header_t *ip_header; fnet_prot_if_t *protocol; unsigned int hdr_err_length = sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t); unsigned int hdr_err_data_length = hdr_err_length + 8; /* 8 bytes is enough for transport protocol (port numbers).*/ if(nb->total_length < hdr_err_data_length) { goto DISCARD; } if(nb->total_length > hdr_err_data_length) { fnet_netbuf_trim(&nb, (int)(hdr_err_data_length - nb->total_length)); } if((tmp_nb = fnet_netbuf_pullup(nb, (int)nb->total_length)) == 0) /* The header must reside in contiguous area of memory.*/ { goto DISCARD; } nb = tmp_nb; ip_header = (fnet_ip6_header_t *)((char *)nb->data_ptr + sizeof(fnet_icmp6_err_header_t)); if((protocol = fnet_prot_find(AF_INET6, SOCK_UNSPEC, ip_header->next_header)) != 0) { if(protocol->prot_control_input) { struct sockaddr err_src_addr; struct sockaddr err_dest_addr; /* Prepare addreses for upper protocol.*/ fnet_ip6_set_socket_addr(netif, ip_header, &err_src_addr, &err_dest_addr ); fnet_netbuf_trim(&nb, (int)(hdr_err_length)); /* Cut the ICMP error header.*/ protocol->prot_control_input(prot_cmd, &err_src_addr, &err_dest_addr, nb); } } } goto DISCARD; default: goto DISCARD; } } else { DISCARD: fnet_netbuf_free_chain(ip6_nb); fnet_netbuf_free_chain(nb); } }
/************************************************************************ * 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); } }
/************************************************************************ * NAME: fnet_eth_output_ip6 * * DESCRIPTION: Ethernet IPv6 output function. *************************************************************************/ void fnet_eth_output_ip6(fnet_netif_t *netif, const fnet_ip6_addr_t *src_ip_addr, const fnet_ip6_addr_t *dest_ip_addr, fnet_netbuf_t *nb) { fnet_mac_addr_t dest_mac_addr; /* 48-bit destination address */ fnet_uint8_t *dest_mac_addr_ptr; /******************************************** * Multicast. ********************************************/ if (FNET_IP6_ADDR_IS_MULTICAST(dest_ip_addr)) { FNET_ETH_MULTICAST_IP6_TO_MAC(dest_ip_addr, dest_mac_addr); dest_mac_addr_ptr = (fnet_uint8_t *)dest_mac_addr; } /******************************************** * Unicast. ********************************************/ else { fnet_nd6_neighbor_entry_t *neighbor; /* Possible redirection.*/ fnet_nd6_redirect_addr(netif, &dest_ip_addr); /* Check Neigbor cache.*/ neighbor = fnet_nd6_neighbor_cache_get(netif, dest_ip_addr); /* RFC4861 7.2.2: When a node has a unicast packet to send to a neighbor, but does not * know the neighbor’s link-layer address, it performs address resolution. * For multicast-capable interfaces, this entails creating a * Neighbor Cache entry in the INCOMPLETE state and transmitting a * Neighbor Solicitation message targeted at the neighbor. The * solicitation is sent to the solicited-node multicast address * corresponding to the target address. */ if(neighbor == FNET_NULL) { /* RFC4861 7.2.Address resolution is performed only on addresses that are determined to be * on-link and for which the sender does not know the corresponding link-layer address. * Address resolution is never performed on multicast addresses.*/ if(fnet_nd6_addr_is_onlink(netif, dest_ip_addr) == FNET_TRUE) /* Destimnation is ON local-link.*/ { /* Creating a Neighbor Cache entry in the INCOMPLETE state. */ neighbor = fnet_nd6_neighbor_cache_add(netif, dest_ip_addr, FNET_NULL, FNET_ND6_NEIGHBOR_STATE_INCOMPLETE); neighbor->state_time = fnet_timer_ms(); neighbor->solicitation_send_counter = 0u; FNET_IP6_ADDR_COPY(src_ip_addr, &neighbor->solicitation_src_ip_addr); /* Save src address for later usage.*/ /* AR: Transmitting a Neighbor Solicitation message targeted at the neighbor.*/ fnet_nd6_neighbor_solicitation_send(netif, src_ip_addr, FNET_NULL /* NULL for AR */, dest_ip_addr); } /* Destination is OFF local-link.*/ else { /* Try to use the router, if exists.*/ neighbor = fnet_nd6_default_router_get(netif); if(neighbor == FNET_NULL) /* No Router exists.*/ { fnet_netbuf_free_chain(nb); /* Discard datagram */ goto EXIT; } dest_ip_addr = &neighbor->ip_addr; /* Chage destination address to the router one */ } } /* Link -layer address is not initialized.*/ if((neighbor->state != FNET_ND6_NEIGHBOR_STATE_INCOMPLETE) && (neighbor->ll_addr[0] == 0U) && (neighbor->ll_addr[1] == 0U) && (neighbor->ll_addr[2] == 0U) && (neighbor->ll_addr[3] == 0U) && (neighbor->ll_addr[4] == 0U) && (neighbor->ll_addr[5] == 0U) ) { neighbor->state = FNET_ND6_NEIGHBOR_STATE_INCOMPLETE; neighbor->state_time = fnet_timer_ms(); neighbor->solicitation_send_counter = 0u; FNET_IP6_ADDR_COPY(src_ip_addr, &neighbor->solicitation_src_ip_addr); /* Save src address for later usage.*/ /* AR: Transmitting a Neighbor Solicitation message targeted at the neighbor.*/ fnet_nd6_neighbor_solicitation_send(netif, src_ip_addr, FNET_NULL /* NULL for AR */, dest_ip_addr); } if(neighbor->state == FNET_ND6_NEIGHBOR_STATE_INCOMPLETE) /* Queue packet for later transmit. */ { fnet_nd6_neighbor_enqueue_waiting_netbuf(neighbor, nb); goto EXIT; } if(neighbor->state == FNET_ND6_NEIGHBOR_STATE_STALE) /* RFC4861 7.3.3: The first time a node sends a packet to a neighbor whose entry is * STALE, the sender changes the state to DELAY and sets a timer to * expire in DELAY_FIRST_PROBE_TIME seconds. */ { neighbor->state = FNET_ND6_NEIGHBOR_STATE_DELAY; neighbor->state_time = fnet_timer_ms(); } /* Get destination MAC/HW address.*/ dest_mac_addr_ptr = (fnet_uint8_t *)(&neighbor->ll_addr[0]); } /* Send Ethernet frame. */ fnet_eth_output(netif, FNET_ETH_TYPE_IP6, dest_mac_addr_ptr, nb); EXIT: return; }
/************************************************************************ * NAME: fnet_icmp6_input * * DESCRIPTION: ICMPv6 input function. *************************************************************************/ static void fnet_icmp6_input(fnet_netif_t *netif, fnet_ip6_addr_t *src_ip, fnet_ip6_addr_t *dest_ip, fnet_netbuf_t *nb, fnet_netbuf_t *ip6_nb) { fnet_icmp6_header_t *hdr; fnet_netbuf_t *tmp_nb; unsigned short sum; if((netif != 0) && (nb != 0)) { /* The header must reside in contiguous area of memory. */ if((tmp_nb = fnet_netbuf_pullup(nb, sizeof(fnet_icmp6_header_t))) == 0) { goto DISCARD; } nb = tmp_nb; hdr = nb->data_ptr; /* Drop Multicast loopback.*/ #if FNET_CFG_LOOPBACK if ((netif == FNET_LOOP_IF) && FNET_IP6_ADDR_IS_MULTICAST(dest_ip)) { goto DISCARD; } #endif /* FNET_CFG_LOOPBACK */ /* Verify the checksum. */ sum = fnet_checksum_pseudo_start( nb, FNET_HTONS((unsigned short)FNET_IP_PROTOCOL_ICMP6), (unsigned short)nb->total_length ); sum = fnet_checksum_pseudo_end( sum, (char *)src_ip, (char *)dest_ip, sizeof(fnet_ip6_addr_t) ); if(sum) goto DISCARD; /************************************************************ * Process incoming ICMPv6 packets. *************************************************************/ switch (hdr->type) { /************************** * Neighbor Solicitation. **************************/ case FNET_ICMP6_TYPE_NEIGHBOR_SOLICITATION: fnet_nd6_neighbor_solicitation_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Neighbor Advertisemnt. **************************/ case FNET_ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT: fnet_nd6_neighbor_advertisement_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Router Advertisemnt. **************************/ case FNET_ICMP6_TYPE_ROUTER_ADVERTISEMENT: fnet_nd6_router_advertisement_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; /************************** * Router Advertisemnt. **************************/ case FNET_ICMP6_TYPE_REDIRECT: fnet_nd6_redirect_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; #if FNET_CFG_MLD /************************** * Multicast Listener Query. **************************/ case FNET_ICMP6_TYPE_MULTICAST_LISTENER_QUERY: fnet_mld_query_receive(netif, src_ip, dest_ip, nb, ip6_nb); break; #endif /************************** * Echo Request. * RFC4443 4.1: Every node MUST implement an ICMPv6 Echo responder function that * receives Echo Requests and originates corresponding Echo Replies. **************************/ case FNET_ICMP6_TYPE_ECHO_REQ: hdr->type = FNET_ICMP6_TYPE_ECHO_REPLY; /* RFC4443: the source address of the reply MUST be a unicast * address belonging to the interface on which * the Echo Request message was received.*/ if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip)) dest_ip = FNET_NULL; fnet_icmp6_output(netif, dest_ip/*ipsrc*/, src_ip/*ipdest*/, 0, nb); //MD fnet_netbuf_free_chain(ip6_nb); break; #if FNET_CFG_IP6_PMTU_DISCOVERY /************************** * Packet Too Big Message. **************************/ case FNET_ICMP6_TYPE_PACKET_TOOBIG: if(netif->pmtu) /* If PMTU is enabled for the interface.*/ { unsigned long pmtu; fnet_icmp6_err_header_t *icmp6_err = nb->data_ptr; /* The header must reside in contiguous area of memory. */ if((tmp_nb = fnet_netbuf_pullup(nb, sizeof(fnet_icmp6_err_header_t))) == 0) { goto DISCARD; } nb = tmp_nb; /* RFC 1981.Upon receipt of such a * message, the source node reduces its assumed PMTU for the path based * on the MTU of the constricting hop as reported in the Packet Too Big * message.*/ pmtu = fnet_ntohl(icmp6_err->data); /* A node MUST NOT increase its estimate of the Path MTU in response to * the contents of a Packet Too Big message. */ if(netif->pmtu > pmtu) { fnet_netif_set_pmtu(netif, pmtu); } } goto DISCARD; #endif default: goto DISCARD; } } else { DISCARD: fnet_netbuf_free_chain(ip6_nb); fnet_netbuf_free_chain(nb); } }
/************************************************************************ * NAME: fnet_netif_bind_ip6_addr_prv * * DESCRIPTION: This function binds the IPv6 address to a hardware interface. *************************************************************************/ int fnet_netif_bind_ip6_addr_prv(fnet_netif_t *netif, fnet_ip6_addr_t *addr, fnet_netif_ip6_addr_type_t addr_type, unsigned long lifetime /*in seconds*/, unsigned long prefix_length /* bits */ ) { int result = FNET_ERR; fnet_netif_ip6_addr_t *if_addr_ptr = FNET_NULL; int i; fnet_os_mutex_lock(); /* Check input parameters. */ if(netif && addr && !FNET_IP6_ADDR_IS_MULTICAST(addr)) { /* Find free address entry. */ for(i = 0; i < FNET_NETIF_IP6_ADDR_MAX; i++) { if(netif->ip6_addr[i].state == FNET_NETIF_IP6_ADDR_STATE_NOT_USED) { if_addr_ptr = &netif->ip6_addr[i]; break; /* Found free entry.*/ } } if(if_addr_ptr) { /* Copying address. */ FNET_IP6_ADDR_COPY(addr, &if_addr_ptr->address); /* If the address is zero => make it link-local.*/ if(FNET_IP6_ADDR_EQUAL(&if_addr_ptr->address, &fnet_ip6_addr_any)) { /* Set link-local address. */ if_addr_ptr->address.addr[0] = 0xFE; if_addr_ptr->address.addr[1] = 0x80; } if_addr_ptr->type = addr_type; /* Set type.*/ /* If we are doing Autoconfiguration, the ip_addr points to prefix.*/ if(addr_type == FNET_NETIF_IP6_ADDR_TYPE_AUTOCONFIGURABLE) { /* Construct address from prefix and interface id. */ if((prefix_length != FNET_ND6_PREFIX_LENGTH_DEFAULT) || fnet_netif_set_ip6_addr_autoconf(netif, &if_addr_ptr->address) == FNET_ERR) { goto COMPLETE; } } /* Check if addr already exists. Do it here to cover Autoconfiguration case.*/ if(fnet_netif_get_ip6_addr_info(netif, &if_addr_ptr->address) != FNET_NULL) { /* The address is already bound.*/ result = FNET_OK; goto COMPLETE; } /* Save creation time, in seconds.*/ if_addr_ptr->creation_time = fnet_timer_seconds(); /* Set lifetime, in seconds.*/ if_addr_ptr->lifetime = lifetime; /* If supports ND6. */ if(netif->nd6_if_ptr) { /* An address on which the Duplicate Address Detection procedure is * applied is said to be tentative until the procedure has completed * successfully. */ if_addr_ptr->state = FNET_NETIF_IP6_ADDR_STATE_TENTATIVE; /* Get&Set the solicited-node multicast group-address for assigned ip_addr. */ fnet_ip6_get_solicited_multicast_addr(&if_addr_ptr->address, &if_addr_ptr->solicited_multicast_addr); /************************************************************************* * Join Multicast ADDRESSES. * When a multicast-capable interface becomes enabled, the node MUST * join the all-nodes multicast address on that interface, as well as * the solicited-node multicast address corresponding to each of the IP * addresses assigned to the interface. **************************************************************************/ /* Join solicited multicast address group.*/ fnet_netif_join_ip6_multicast ( (fnet_netif_desc_t)netif, &if_addr_ptr->solicited_multicast_addr ); /* Start Duplicate Address Detection (DAD). * RFC4862: The Duplicate Address Detection algorithm is performed on all addresses, * independently of whether they are obtained via stateless * autoconfiguration or DHCPv6. */ fnet_nd6_dad_start(netif , if_addr_ptr); } else { if_addr_ptr->state = FNET_NETIF_IP6_ADDR_STATE_PREFERRED; } //Check by type //TBD if(netif->api->set_addr_notify) // netif->api->set_addr_notify(netif); } } COMPLETE: fnet_os_mutex_unlock(); return result; }
/************************************************************************ * 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); }