/************************************************************************ * NAME: fnet_netbuf_del_chain * * DESCRIPTION: Deletes chain nb_chain, which is in the queue pointed by * nb_ptr * *************************************************************************/ void fnet_netbuf_del_chain( fnet_netbuf_t ** nb_ptr, fnet_netbuf_t *nb_chain ) { fnet_netbuf_t *nb_current, *nb; if(!((nb_ptr == 0) || ((fnet_netbuf_t *) *nb_ptr == 0))) { nb_current = (fnet_netbuf_t *) *nb_ptr; if(nb_current == nb_chain) { nb = nb_current->next_chain; fnet_netbuf_free_chain(nb_current); *nb_ptr = nb; return; } while((nb_current->next_chain != nb_chain) && (nb_current != 0)) { nb_current = nb_current->next_chain; } nb = nb_current->next_chain->next_chain; fnet_netbuf_free_chain(nb_current->next_chain); nb_current->next_chain = nb; } }
/************************************************************************ * DESCRIPTION: Deletes chain nb_chain, which is in the queue pointed by nb_ptr *************************************************************************/ void fnet_netbuf_del_chain( fnet_netbuf_t **nb_ptr, fnet_netbuf_t *nb_chain ) { fnet_netbuf_t *nb_current, *nb; if(!((nb_ptr == 0) || ((fnet_netbuf_t *) *nb_ptr == 0))) { nb_current = (fnet_netbuf_t *) *nb_ptr; if(nb_current == nb_chain) { nb = nb_current->next_chain; fnet_netbuf_free_chain(nb_current); *nb_ptr = nb; } else { while(nb_current && (nb_current->next_chain != nb_chain)) { nb_current = nb_current->next_chain; } if(nb_current) { nb = nb_current->next_chain->next_chain; fnet_netbuf_free_chain(nb_current->next_chain); nb_current->next_chain = nb; } /* else not found -> assert?*/ } } }
/************************************************************************ * 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); }
void fnet_loop_output_ip4(fnet_netif_t *netif, fnet_ip4_addr_t dest_ip_addr, fnet_netbuf_t* nb) { FNET_COMP_UNUSED_ARG(dest_ip_addr); /* MTU check */ if (nb->total_length <= netif->mtu) fnet_ip_input(netif, nb); else fnet_netbuf_free_chain(nb); }
void fnet_loop_output_ip6(struct fnet_netif *netif, fnet_ip6_addr_t *src_ip_addr, fnet_ip6_addr_t *dest_ip_addr, fnet_netbuf_t* nb) { FNET_COMP_UNUSED_ARG(dest_ip_addr); FNET_COMP_UNUSED_ARG(src_ip_addr); fnet_isr_lock(); /* MTU check */ if (nb->total_length <= netif->mtu) fnet_ip6_input(netif, nb); else fnet_netbuf_free_chain(nb); fnet_isr_unlock(); }
/************************************************************************ * NAME: fnet_icmp_notify_protocol * * DESCRIPTION: Upper protocol notification.. *************************************************************************/ static void fnet_icmp_notify_protocol(fnet_prot_notify_t prot_cmd, fnet_netbuf_t *nb) { fnet_icmp_err_header_t *hdr_err = (fnet_icmp_err_header_t *)nb->data_ptr; fnet_ip_header_t *ip_header = &hdr_err->ip; fnet_size_t hdr_err_length = sizeof(fnet_icmp_err_header_t) /*+ ((FNET_IP_HEADER_GET_HEADER_LENGTH(ip_header) << 2) - sizeof(fnet_ip_header_t))*/; fnet_size_t hdr_err_data_length = hdr_err_length+8u; /* 8 bytes is enough for transport protocol (port numbers).*/ fnet_prot_if_t *protocol; if(nb->total_length < hdr_err_data_length) { goto DISCARD; } if(nb->total_length > hdr_err_data_length) { fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_data_length - nb->total_length)); } if(fnet_netbuf_pullup(&nb, nb->total_length) == FNET_ERR) /* The header must reside in contiguous area of memory.*/ { goto DISCARD; } hdr_err = (fnet_icmp_err_header_t *)nb->data_ptr; ip_header = &hdr_err->ip; if((protocol = fnet_prot_find(AF_INET, SOCK_UNSPEC, (fnet_uint32_t)hdr_err->ip.protocol)) != 0) { if(protocol->prot_control_input) { struct sockaddr err_src_addr; struct sockaddr err_dest_addr; /* Prepare addreses for upper protocol.*/ fnet_ip_set_socket_addr(ip_header, &err_src_addr, &err_dest_addr ); fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_length)); /* Cut the ICMP error header.*/ protocol->prot_control_input(prot_cmd, &err_src_addr, &err_dest_addr, nb); } } DISCARD: fnet_netbuf_free_chain(nb); }
static void fnet_arp_timer(fnet_uint32_t cookie) { fnet_arp_if_t *arpif = (fnet_arp_if_t *)cookie; fnet_index_t i; for (i = 0U; i < FNET_CFG_ARP_TABLE_SIZE; i++) { if ((arpif->arp_table[i].prot_addr) && (((fnet_timer_ticks() - arpif->arp_table[i].cr_time)) > (fnet_time_t)((FNET_CFG_ARP_EXPIRE_TIMEOUT * 1000U) / FNET_TIMER_PERIOD_MS))) { if (arpif->arp_table[i].hold) { fnet_netbuf_free_chain(arpif->arp_table[i].hold); } fnet_memset_zero(&(arpif->arp_table[i]), sizeof(fnet_arp_entry_t)); } } }
/************************************************************************ * NAME: fnet_arp_timer * * DESCRIPTION: ARP timer. *************************************************************************/ static void fnet_arp_timer( void *cookie ) { fnet_arp_if_t *arpif = (fnet_arp_if_t *)cookie; int i; for (i = 0; i < FNET_ARP_TABLE_SIZE; i++) { if((arpif->arp_table[i].prot_addr) && ((fnet_timer_ticks() - arpif->arp_table[i].cr_time)) > (unsigned long)(FNET_ARP_TIMEOUT / FNET_TIMER_PERIOD_MS)) { if(arpif->arp_table[i].hold) fnet_netbuf_free_chain(arpif->arp_table[i].hold); fnet_memset_zero(&(arpif->arp_table[i]), sizeof(fnet_arp_entry_t)); } } }
/************************************************************************ * NAME: fnet_arp_resolve * * DESCRIPTION: This function finds the first unused or the oldest * ARP table entry and makes a new entry * to prepare it for an ARP reply. *************************************************************************/ void fnet_arp_resolve(fnet_netif_t *netif, fnet_ip4_addr_t ipaddr, fnet_netbuf_t *nb) { fnet_arp_if_t *arpif = netif->arp_if_ptr; fnet_index_t i; fnet_arp_entry_t *entry; fnet_isr_lock(); for (i = 0U; i < FNET_CFG_ARP_TABLE_SIZE; i++) { if (ipaddr == arpif->arp_table[i].prot_addr) { break; } } /* If no unused entry is found, create it. */ if (i == FNET_CFG_ARP_TABLE_SIZE) { entry = fnet_arp_add_entry(netif, ipaddr, fnet_eth_null_addr); } else { entry = &arpif->arp_table[i]; } if (entry->hold) { fnet_netbuf_free_chain(entry->hold); entry->hold = NULL; } if ((i == FNET_CFG_ARP_TABLE_SIZE) || ((entry->hold) && (((fnet_timer_ticks() - entry->hold_time) * FNET_TIMER_PERIOD_MS) > 1000U)) || (!entry->hold)) { entry->hold_time = fnet_timer_ticks(); entry->hold = nb; fnet_arp_request(netif, ipaddr); } else { entry->hold = nb; } fnet_isr_unlock(); }
/************************************************************************ * NAME: fnet_arp_drain * * DESCRIPTION: This function tries to free not critical parts * of memory used by ARP protocol. *************************************************************************/ void fnet_arp_drain(fnet_netif_t *netif) { fnet_index_t i; fnet_arp_if_t *arpif = netif->arp_if_ptr; fnet_isr_lock(); /* ARP table drain.*/ for (i = 0U; i < FNET_CFG_ARP_TABLE_SIZE; i++) { if (arpif->arp_table[i].hold) { fnet_netbuf_free_chain(arpif->arp_table[i].hold); arpif->arp_table[i].hold = 0; arpif->arp_table[i].hold_time = 0U; } } fnet_isr_unlock(); }
/************************************************************************ * NAME: fnet_arp_drain * * DESCRIPTION: This function tries to free not critical parts * of memory used by ARP protocol. *************************************************************************/ void fnet_arp_drain(fnet_netif_t *netif) { int i; fnet_arp_if_t * arpif = &(((fnet_eth_if_t *)(netif->if_ptr))->arp_if); //PFI fnet_isr_lock(); /* ARP table drain.*/ for(i=0;i<FNET_ARP_TABLE_SIZE;i++) { if(arpif->arp_table[i].hold) { fnet_netbuf_free_chain(arpif->arp_table[i].hold); arpif->arp_table[i].hold=0; arpif->arp_table[i].hold_time=0; } } fnet_isr_unlock(); }
/************************************************************************ * NAME: fnet_eth_prot_input * * DESCRIPTION: Eth. network-layer input function. *************************************************************************/ void fnet_eth_prot_input( fnet_netif_t *netif, fnet_netbuf_t *nb, fnet_uint16_t protocol ) { fnet_index_t i; if(netif && nb) { /* Find Network-layer protocol.*/ for(i = 0U; i < FNET_ETH_PROT_IF_LIST_SIZE; i++) { if( protocol == fnet_eth_prot_if_list[i].protocol) { /* Call the protocol-input function.*/ fnet_eth_prot_if_list[i].input(netif, nb); break; } } if(i == FNET_ETH_PROT_IF_LIST_SIZE) { /* No procol found */ fnet_netbuf_free_chain(nb); } } }
/************************************************************************ * NAME: fnet_arp_resolve * * DESCRIPTION: This function finds the first unused or the oldest * ARP table entry and makes a new entry * to prepare it for an ARP reply. *************************************************************************/ void fnet_arp_resolve( fnet_netif_t *netif, fnet_ip4_addr_t ipaddr, fnet_netbuf_t *nb ) { fnet_arp_if_t *arpif = &(((fnet_eth_if_t *)(netif->if_ptr))->arp_if); //PFI int i; fnet_arp_entry_t *entry; for (i = 0; i < FNET_ARP_TABLE_SIZE; i++) { if(ipaddr == arpif->arp_table[i].prot_addr) { break; } } /* If no unused entry is found, create it. */ if(i == FNET_ARP_TABLE_SIZE) entry = fnet_arp_add_entry(netif, ipaddr, fnet_eth_null_addr); else entry = &arpif->arp_table[i]; if(entry->hold) { fnet_netbuf_free_chain(entry->hold); } if((i == FNET_ARP_TABLE_SIZE)|| ((entry->hold)&&(((fnet_timer_ticks()-entry->hold_time)*FNET_TIMER_PERIOD_MS)>1000))|| (!entry->hold)) { entry->hold_time = fnet_timer_ticks(); entry->hold = nb; fnet_arp_request(netif, ipaddr); } else entry->hold = nb; }
/************************************************************************ * NAME: fnet_udp_input * * DESCRIPTION: UDP input function. *************************************************************************/ static void fnet_udp_input(fnet_netif_t *netif, struct sockaddr *foreign_addr, struct sockaddr *local_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip_nb) { fnet_udp_header_t *udp_header = (fnet_udp_header_t *)nb->data_ptr; fnet_socket_if_t *sock; fnet_socket_if_t *last; fnet_size_t udp_length; fnet_netbuf_t *nb_tmp; if((netif != 0) && (nb != 0)) { /* The header must reside in contiguous area of memory.*/ if(fnet_netbuf_pullup(&nb, sizeof(fnet_udp_header_t)) == FNET_ERR) { goto BAD; } udp_length = fnet_ntohs(udp_header->length); if(nb->total_length >= udp_length) /* Check the amount of data.*/ { if(nb->total_length > udp_length) { /* Logical size and the physical size of the packet should be the same.*/ fnet_netbuf_trim(&nb, (fnet_int32_t)(udp_length - nb->total_length)); } #if FNET_CFG_UDP_CHECKSUM if((udp_header->checksum != 0u) #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_uint16_t sum; sum = fnet_checksum_pseudo_start( nb, FNET_HTONS((fnet_uint16_t)FNET_IP_PROTOCOL_UDP), (fnet_uint16_t)udp_length ); sum = fnet_checksum_pseudo_end( sum, &foreign_addr->sa_data[0], &local_addr->sa_data[0], (fnet_size_t)((local_addr->sa_family == AF_INET) ? sizeof(fnet_ip4_addr_t) : sizeof(fnet_ip6_addr_t))); if(sum) { goto BAD; } } #endif fnet_udp_trace("RX", udp_header); /* Trace UDP header.*/ local_addr->sa_port = udp_header->destination_port; foreign_addr->sa_port = udp_header->source_port; fnet_netbuf_trim(&nb, (fnet_int32_t)sizeof(fnet_udp_header_t)); /* Demultiplex broadcast & multicast datagrams.*/ if((fnet_socket_addr_is_broadcast(local_addr, netif)) || (fnet_socket_addr_is_multicast(local_addr))) { last = 0; for (sock = fnet_udp_prot_if.head; sock != 0; sock = sock->next) { /* Compare local port number.*/ if(sock->local_addr.sa_port != local_addr->sa_port) { continue; /* => ignore.*/ } #if FNET_CFG_MULTICAST if(fnet_socket_addr_is_multicast(local_addr)) { fnet_index_t m; fnet_bool_t for_us = FNET_FALSE; #if FNET_CFG_IP4 if(local_addr->sa_family == AF_INET) { for(m = 0u; m < FNET_CFG_MULTICAST_SOCKET_MAX; m++) { if(sock->ip4_multicast_entry[m]) { if((sock->ip4_multicast_entry[m]->group_addr == ((struct sockaddr_in *)(local_addr))->sin_addr.s_addr) && (sock->ip4_multicast_entry[m]->netif == netif )) { for_us = FNET_TRUE; } } } } else #endif #if FNET_CFG_IP6 if(local_addr->sa_family == AF_INET6) { for(m=0u; m < FNET_CFG_MULTICAST_SOCKET_MAX; m++) { if(sock->ip6_multicast_entry[m]) { if(FNET_IP6_ADDR_EQUAL(&sock->ip6_multicast_entry[m]->group_addr, &((struct sockaddr_in6 *)(local_addr))->sin6_addr.s6_addr) && (sock->ip6_multicast_entry[m]->netif == netif)) { for_us = FNET_TRUE; } } } } else #endif {} if(for_us == FNET_FALSE) { continue; } } else #endif /* FNET_CFG_MULTICAST */ { /* Compare local address.*/ if(!fnet_socket_addr_is_unspecified(&sock->local_addr)) { if(!fnet_socket_addr_are_equal(&sock->local_addr, local_addr)) { continue; } } /* Compare foreign address and port number.*/ if(!fnet_socket_addr_is_unspecified(&sock->foreign_addr)) { if((!fnet_socket_addr_are_equal(&sock->foreign_addr, foreign_addr)) || (sock->foreign_addr.sa_port != foreign_addr->sa_port)) { continue; } } } if((last != 0) && (last->receive_buffer.is_shutdown == FNET_FALSE)) { if((nb_tmp = fnet_netbuf_copy(nb, 0u, FNET_NETBUF_COPYALL, FNET_FALSE)) != 0) { if(fnet_socket_buffer_append_address(&(last->receive_buffer), nb_tmp, foreign_addr) == FNET_ERR) { fnet_netbuf_free_chain(nb_tmp); } } } last = sock; } if(last == 0) { goto BAD; } if(last->receive_buffer.is_shutdown) /* Is shutdown.*/ { goto BAD; } if(fnet_socket_buffer_append_address(&(last->receive_buffer), nb, foreign_addr) == FNET_ERR) { goto BAD; } fnet_netbuf_free_chain(ip_nb); } else /* For unicast datagram.*/ { sock = fnet_socket_lookup(fnet_udp_prot_if.head, local_addr, foreign_addr, FNET_IP_PROTOCOL_UDP); if(sock) { if(sock->receive_buffer.is_shutdown) /* Is shutdown.*/ { goto BAD; } if(fnet_socket_buffer_append_address(&(sock->receive_buffer), nb, foreign_addr) == FNET_ERR) { goto BAD; } fnet_netbuf_free_chain(ip_nb); } else { fnet_netbuf_free_chain(nb); /* No match was found, send ICMP destination port unreachable.*/ #if FNET_CFG_IP4 if(local_addr->sa_family == AF_INET) { fnet_icmp_error(netif, FNET_ICMP_UNREACHABLE, FNET_ICMP_UNREACHABLE_PORT, ip_nb); }else #endif #if FNET_CFG_IP6 if(local_addr->sa_family == AF_INET6) { fnet_icmp6_error(netif, FNET_ICMP6_TYPE_DEST_UNREACH, FNET_ICMP6_CODE_DU_PORT_UNREACH, 0u, ip_nb ); }else #endif {} } } } else { goto BAD; } } else { BAD: fnet_netbuf_free_chain(ip_nb); fnet_netbuf_free_chain(nb); } }
/************************************************************************ * 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); } } } }
/************************************************************************ * 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_raw_input * * DESCRIPTION: RAW input function. *************************************************************************/ void fnet_raw_input(fnet_netif_t *netif, struct sockaddr *foreign_addr, struct sockaddr *local_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip_nb) { fnet_socket_t *sock; fnet_socket_t *last; fnet_netbuf_t *nb_tmp; int protocol_number; if(netif && nb && nb->total_length) { #if FNET_CFG_IP4 if(foreign_addr->sa_family == AF_INET) { protocol_number = ((fnet_ip_header_t *)(ip_nb->data_ptr))->protocol; } else #endif #if FNET_CFG_IP6 if(foreign_addr->sa_family == AF_INET6) { protocol_number = ((fnet_ip6_header_t *)(ip_nb->data_ptr))->next_header; } else #endif {protocol_number = 0;} /* Demultiplex broadcast & multicast datagrams.*/ if(fnet_socket_addr_is_broadcast(local_addr, netif) || fnet_socket_addr_is_multicast(local_addr)) { last = 0; for (sock = fnet_raw_prot_if.head; sock != 0; sock = sock->next) { /* Ignore local port number.*/ /* Check protocol number.*/ if(sock->protocol_number != protocol_number) continue; /* => ignore.*/ #if FNET_CFG_MULTICAST if(fnet_socket_addr_is_multicast(local_addr)) { int m; int for_us = FNET_FALSE; #if FNET_CFG_IP4 if(local_addr->sa_family == AF_INET) { for(m=0; m < FNET_CFG_MULTICAST_SOCKET_MAX; m++) { if(sock->ip4_multicast_entry[m]) { if((sock->ip4_multicast_entry[m]->group_addr == ((struct sockaddr_in *)(local_addr))->sin_addr.s_addr) && (sock->ip4_multicast_entry[m]->netif == netif )) for_us = FNET_TRUE; } } } #endif #if FNET_CFG_IP6 if(local_addr->sa_family == AF_INET6) { for(m=0; m < FNET_CFG_MULTICAST_SOCKET_MAX; m++) { if(sock->ip6_multicast_entry[m]) { if(FNET_IP6_ADDR_EQUAL(&sock->ip6_multicast_entry[m]->group_addr, &((struct sockaddr_in6 *)(local_addr))->sin6_addr.s6_addr) && (sock->ip6_multicast_entry[m]->netif == netif)) for_us = FNET_TRUE; } } } #endif if(for_us == FNET_FALSE) continue; } else #endif /* FNET_CFG_MULTICAST */ { /* Compare local address.*/ if(!fnet_socket_addr_is_unspecified(&sock->local_addr)) { if(!fnet_socket_addr_are_equal(&sock->local_addr, local_addr)) continue; } /* Compare foreign address and port number.*/ if(!fnet_socket_addr_is_unspecified(&sock->foreign_addr)) { if((!fnet_socket_addr_are_equal(&sock->foreign_addr, foreign_addr)) ) continue; } } if((last != 0) && (last->receive_buffer.is_shutdown == 0)) { if((nb_tmp = fnet_netbuf_copy(nb, 0, FNET_NETBUF_COPYALL, 0)) != 0) { if(fnet_socket_buffer_append_address(&(last->receive_buffer), nb_tmp, foreign_addr) == FNET_ERR) { fnet_netbuf_free_chain(nb_tmp); } } } last = sock; } if(last == 0) goto BAD; if(last->receive_buffer.is_shutdown) /* Is shutdown.*/ goto BAD; /* Copy buffer.*/ if((nb_tmp = fnet_netbuf_copy(nb, 0, FNET_NETBUF_COPYALL, 0)) != 0) { if(fnet_socket_buffer_append_address(&(last->receive_buffer), nb_tmp, foreign_addr) == FNET_ERR) { fnet_netbuf_free_chain(nb_tmp); goto BAD; } } else goto BAD; } else /* For unicast datagram.*/ { sock = fnet_socket_lookup(fnet_raw_prot_if.head, local_addr, foreign_addr, protocol_number); if(sock) { if(sock->receive_buffer.is_shutdown) /* Is shutdown.*/ goto BAD; /* Copy buffer.*/ if((nb_tmp = fnet_netbuf_copy(nb, 0, FNET_NETBUF_COPYALL, 0)) != 0) { if(fnet_socket_buffer_append_address(&(sock->receive_buffer), nb_tmp, foreign_addr) == FNET_ERR) { fnet_netbuf_free_chain(nb_tmp); goto BAD; } } else goto BAD; } } } BAD: return; }
/************************************************************************ * 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_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_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); }
/************************************************************************ * 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_arp_add_entry * * DESCRIPTION: Adds entry to the ARP table. *************************************************************************/ static fnet_arp_entry_t *fnet_arp_add_entry( fnet_netif_t *netif, fnet_ip4_addr_t ipaddr, const fnet_mac_addr_t ethaddr ) { fnet_arp_if_t *arpif = &(((fnet_eth_if_t *)(netif->if_ptr))->arp_if); int i, j; unsigned long max_time; /* Find an entry to update. */ for (i = 0; i < FNET_ARP_TABLE_SIZE; ++i) { /* Check if the source IP address of the incoming packet matches * the IP address in this ARP table entry.*/ if(ipaddr == arpif->arp_table[i].prot_addr) { /* Update this and return. */ fnet_memcpy(arpif->arp_table[i].hard_addr, ethaddr, sizeof(fnet_mac_addr_t)); arpif->arp_table[i].cr_time = fnet_timer_ticks(); goto ADDED; } } /* If we get here, no existing ARP table entry was found. */ /* Find an unused entry in the ARP table. */ for (i = 0; i < FNET_ARP_TABLE_SIZE; ++i) { if(arpif->arp_table[i].prot_addr == 0) { break; } } /* If no unused entry is found, we try to find the oldest entry and throw it away.*/ if(i == FNET_ARP_TABLE_SIZE) { max_time = 0; j = 0; for (i = 0; i < FNET_ARP_TABLE_SIZE; ++i) { if((fnet_timer_ticks() - arpif->arp_table[i].cr_time) > max_time) { max_time = fnet_timer_ticks() - arpif->arp_table[i].cr_time; j = i; } } i = j; } /* Now, it is the ARP table entry which we will fill with the new information. */ if(arpif->arp_table[i].hold) { fnet_netbuf_free_chain(arpif->arp_table[i].hold); arpif->arp_table[i].hold = 0; arpif->arp_table[i].hold_time = 0; } arpif->arp_table[i].prot_addr = ipaddr; fnet_memcpy(arpif->arp_table[i].hard_addr, ethaddr, sizeof(fnet_mac_addr_t)); arpif->arp_table[i].cr_time = fnet_timer_ticks(); ADDED: return ( &arpif->arp_table[i]); }
/************************************************************************ * 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); } }
/************************************************************************ * 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); }
/************************************************************************ * 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); } }
/************************************************************************ * NAME: fnet_arp_input * * DESCRIPTION: ARP input function. *************************************************************************/ void fnet_arp_input( fnet_netif_t *netif, fnet_netbuf_t *nb ) { fnet_arp_if_t *arpif = &(((fnet_eth_if_t *)(netif->if_ptr))->arp_if); fnet_arp_header_t *arp_hdr = nb->data_ptr; fnet_mac_addr_t local_addr; fnet_arp_entry_t *entry; if(!((nb == 0) /* The packet is wrong. */ || (nb->total_length < sizeof(fnet_arp_header_t)) || (arp_hdr->hard_type != FNET_HTONS(FNET_ARP_HARD_TYPE)) || (arp_hdr->hard_size != FNET_ARP_HARD_SIZE) || (arp_hdr->prot_type != FNET_HTONS(FNET_ETH_TYPE_IP4)) || (arp_hdr->prot_size != FNET_ARP_PROT_SIZE))) { if(nb->total_length > sizeof(fnet_arp_header_t)) { /* Logical size and the physical size of the packet should be the same.*/ fnet_netbuf_trim(&nb, (int)(sizeof(fnet_arp_header_t) - nb->total_length)); } fnet_arp_trace("RX", arp_hdr); /* Print ARP header. */ fnet_netif_get_hw_addr(netif, local_addr, sizeof(fnet_mac_addr_t)); if(!(!fnet_memcmp(arp_hdr->sender_hard_addr, local_addr, sizeof(fnet_mac_addr_t)) /* It's from me => ignore it.*/ || !fnet_memcmp(arp_hdr->sender_hard_addr, fnet_eth_broadcast, sizeof(fnet_mac_addr_t))) /* It's broadcast=> error. */ ) { fnet_ip4_addr_t sender_prot_addr = arp_hdr->sender_prot_addr; fnet_ip4_addr_t targer_prot_addr = arp_hdr->targer_prot_addr; if(sender_prot_addr != netif->ip4_addr.address) /* Check Duplicate IP address.*/ { if(targer_prot_addr == netif->ip4_addr.address) /* It's for me.*/ { entry = fnet_arp_add_entry(netif, sender_prot_addr, arp_hdr->sender_hard_addr); } else { entry = fnet_arp_update_entry(netif, sender_prot_addr, arp_hdr->sender_hard_addr); } if(entry && entry->hold) { /* Send waiting data.*/ ((fnet_eth_if_t *)(netif->if_ptr))->output(netif, FNET_ETH_TYPE_IP4, entry->hard_addr, entry->hold); entry->hold = 0; entry->hold_time = 0; } } else { /* IP is duplicated. */ fnet_event_raise(arpif->arp_event); } /* ARP request. If it asked for our address, we send out a reply.*/ if((arp_hdr->op == FNET_HTONS(FNET_ARP_OP_REQUEST)) && (targer_prot_addr == netif->ip4_addr.address)) { arp_hdr->op = FNET_HTONS(FNET_ARP_OP_REPLY); /* Opcode */ fnet_memcpy(arp_hdr->target_hard_addr, arp_hdr->sender_hard_addr, sizeof(fnet_mac_addr_t)); fnet_memcpy(arp_hdr->sender_hard_addr, local_addr, sizeof(fnet_mac_addr_t)); arp_hdr->targer_prot_addr = arp_hdr->sender_prot_addr; arp_hdr->sender_prot_addr = netif->ip4_addr.address; fnet_arp_trace("TX Reply", arp_hdr); /* Print ARP header. */ ((fnet_eth_if_t *)(netif->if_ptr))->output(netif, FNET_ETH_TYPE_ARP, fnet_eth_broadcast, nb); return; } } } fnet_netbuf_free_chain(nb); }