/************************************************************************ * NAME: fnet_netif_pmtu_init * RETURS: None. * DESCRIPTION: Initialize PMTU Discovery for the interface. *************************************************************************/ void fnet_netif_pmtu_init(fnet_netif_t *netif) { /* Path MTU for the link. */ fnet_netif_set_pmtu(netif, netif->mtu); /* Register timer, to detect increase of PMTU.*/ netif->pmtu_timer = fnet_timer_new((FNET_NETIF_PMTU_PERIOD/FNET_TIMER_PERIOD_MS), fnet_netif_pmtu_timer, netif); }
/************************************************************************ * NAME: fnet_nd6_dad_timer * RETURS: None. * DESCRIPTION: Timer routine used to detect increase of PMTU *************************************************************************/ static void fnet_netif_pmtu_timer( void *cookie ) { fnet_netif_t *netif = (fnet_netif_t *)cookie; if( fnet_timer_get_interval(netif->pmtu_timestamp, fnet_timer_ms()) > FNET_NETIF_PMTU_TIMEOUT) { fnet_netif_set_pmtu(netif, netif->mtu); } }
/************************************************************************ * 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_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); } }