/** Common code to see if the current input packet matches the pcb * (current input packet is accessed via ip(4/6)_current_* macros) * * @param pcb pcb to check * @param inp network interface on which the datagram was received (only used for IPv4) * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4) * @return 1 on match, 0 otherwise */ static u8_t ESP_IRAM_ATTR udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast) { LWIP_UNUSED_ARG(inp); /* in IPv6 only case */ LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */ /* Dual-stack: PCBs listening to any IP type also listen to any IP address */ if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { #if LWIP_IPV4 && IP_SOF_BROADCAST_RECV if((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) { return 0; } #endif /* LWIP_IPV4 && IP_SOF_BROADCAST_RECV */ return 1; } /* Only need to check PCB if incoming IP version matches PCB IP version */ if(IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) { LWIP_ASSERT("UDP PCB: inconsistent local/remote IP versions", IP_IS_V6_VAL(pcb->local_ip) == IP_IS_V6_VAL(pcb->remote_ip)); #if LWIP_IPV4 /* Special case: IPv4 broadcast: all or broadcasts in my subnet * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */ if(broadcast != 0) { #if IP_SOF_BROADCAST_RECV if(ip_get_option(pcb, SOF_BROADCAST)) #endif /* IP_SOF_BROADCAST_RECV */ { if(ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) || ((ip4_current_dest_addr()->addr == IPADDR_BROADCAST)) || ip4_addr_netcmp(ip_2_ip4(&pcb->local_ip), ip4_current_dest_addr(), netif_ip4_netmask(inp))) { return 1; } } } else #endif /* LWIP_IPV4 */ /* Handle IPv4 and IPv6: all, multicast or exact match */ if(ip_addr_isany(&pcb->local_ip) || #if LWIP_IPV6_MLD (ip_current_is_v6() && ip6_addr_ismulticast(ip6_current_dest_addr())) || #endif /* LWIP_IPV6_MLD */ #if LWIP_IGMP (!ip_current_is_v6() && ip4_addr_ismulticast(ip4_current_dest_addr())) || #endif /* LWIP_IGMP */ ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) { return 1; } } return 0; }
void icmp6_proxy_input(struct pbuf *p, struct netif *inp) { struct icmp6_hdr *icmp6hdr; ICMP6_STATS_INC(icmp6.recv); /* Check that ICMPv6 header fits in payload */ if (p->len < sizeof(struct icmp6_hdr)) { /* drop short packets */ pbuf_free(p); ICMP6_STATS_INC(icmp6.lenerr); ICMP6_STATS_INC(icmp6.drop); return; } icmp6hdr = (struct icmp6_hdr *)p->payload; if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), ip6_current_dest_addr()) != 0) { /* Checksum failed */ pbuf_free(p); ICMP6_STATS_INC(icmp6.chkerr); ICMP6_STATS_INC(icmp6.drop); return; } switch (icmp6hdr->type) { case ICMP6_TYPE_EREQ: if (ping6_proxy_accept_callback != NULL) { (*ping6_proxy_accept_callback)(ping6_proxy_accept_arg, p); return; } ICMP6_STATS_INC(icmp6.drop); break; case ICMP6_TYPE_EREP: /* ignore silently */ ICMP6_STATS_INC(icmp6.drop); break; default: ICMP6_STATS_INC(icmp6.drop); break; } pbuf_free(p); }
/** * This function is called by the network interface device driver when * an IPv6 packet is received. The function does the basic checks of the * IP header such as packet size being at least larger than the header * size etc. If the packet was not destined for us, the packet is * forwarded (using ip6_forward). * * Finally, the packet is sent to the upper layer protocol input function. * * @param p the received IPv6 packet (p->payload points to IPv6 header) * @param inp the netif on which this packet was received * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't * processed, but currently always returns ERR_OK) */ err_t ip6_input(struct pbuf *p, struct netif *inp) { struct ip6_hdr *ip6hdr; struct netif *netif; u8_t nexth; u16_t hlen; /* the current header length */ u8_t i; #if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ @todo int check_ip_src=1; #endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ IP6_STATS_INC(ip6.recv); /* identify the IP header */ ip6hdr = (struct ip6_hdr *)p->payload; if (IP6H_V(ip6hdr) != 6) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", IP6H_V(ip6hdr))); pbuf_free(p); IP6_STATS_INC(ip6.err); IP6_STATS_INC(ip6.drop); return ERR_OK; } #ifdef LWIP_HOOK_IP6_INPUT if (LWIP_HOOK_IP6_INPUT(p, inp)) { /* the packet has been eaten */ return ERR_OK; } #endif /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { if (IP6_HLEN > p->len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", IP6_HLEN, p->len)); } if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); } /* free (drop) packet pbufs */ pbuf_free(p); IP6_STATS_INC(ip6.lenerr); IP6_STATS_INC(ip6.drop); return ERR_OK; } /* Trim pbuf. This should have been done at the netif layer, * but we'll do it anyway just to be sure that its done. */ pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr)); /* copy IP addresses to aligned ip6_addr_t */ ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest); ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src); /* current header pointer. */ ip_data.current_ip6_header = ip6hdr; /* In netif, used in case we need to send ICMPv6 packets back. */ ip_data.current_netif = inp; ip_data.current_input_netif = inp; /* match packet against an interface, i.e. is this packet for us? */ if (ip6_addr_ismulticast(ip6_current_dest_addr())) { /* Always joined to multicast if-local and link-local all-nodes group. */ if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { netif = inp; } #if LWIP_IPV6_MLD else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { netif = inp; } #else /* LWIP_IPV6_MLD */ else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { /* Filter solicited node packets when MLD is not enabled * (for Neighbor discovery). */ netif = NULL; for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { netif = inp; LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", netif->name[0], netif->name[1])); break; } } } #endif /* LWIP_IPV6_MLD */ else { netif = NULL; } } else { /* start trying with inp. if that's not acceptable, start walking the list of configured netifs. 'first' is used as a boolean to mark whether we started walking the list */ int first = 1; netif = inp; do { /* interface is up? */ if (netif_is_up(netif)) { /* unicast to this interface address? address configured? */ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { /* exit outer loop */ goto netif_found; } } } if (ip6_addr_islinklocal(ip6_current_dest_addr())) { /* Do not match link-local addresses to other netifs. */ netif = NULL; break; } if (first) { first = 0; netif = netif_list; } else { netif = netif->next; } if (netif == inp) { netif = netif->next; } } while (netif != NULL); netif_found: LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); } /* "::" packet source address? (used in duplicate address detection) */ if (ip6_addr_isany(ip6_current_src_addr()) && (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { /* packet source is not valid */ /* free (drop) packet pbufs */ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); pbuf_free(p); IP6_STATS_INC(ip6.drop); goto ip6_input_cleanup; } /* packet not for us? */ if (netif == NULL) { /* packet not for us, route or discard */ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); #if LWIP_IPV6_FORWARD /* non-multicast packet? */ if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { /* try to forward IP packet on (other) interfaces */ ip6_forward(p, ip6hdr, inp); } #endif /* LWIP_IPV6_FORWARD */ pbuf_free(p); goto ip6_input_cleanup; } /* current netif pointer. */ ip_data.current_netif = netif; /* Save next header type. */ nexth = IP6H_NEXTH(ip6hdr); /* Init header length. */ hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; /* Move to payload. */ pbuf_header(p, -IP6_HLEN); /* Process known option extension headers, if present. */ while (nexth != IP6_NEXTH_NONE) { switch (nexth) { case IP6_NEXTH_HOPBYHOP: LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); /* Get next header type. */ nexth = *((u8_t *)p->payload); /* Get the header length. */ hlen = 8 * (1 + *((u8_t *)p->payload + 1)); ip_data.current_ip_header_tot_len += hlen; /* Skip over this header. */ if (hlen > p->len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", hlen, p->len)); /* free (drop) packet pbufs */ pbuf_free(p); IP6_STATS_INC(ip6.lenerr); IP6_STATS_INC(ip6.drop); goto ip6_input_cleanup; } pbuf_header(p, -(s16_t)hlen); break; case IP6_NEXTH_DESTOPTS: LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); /* Get next header type. */ nexth = *((u8_t *)p->payload); /* Get the header length. */ hlen = 8 * (1 + *((u8_t *)p->payload + 1)); ip_data.current_ip_header_tot_len += hlen; /* Skip over this header. */ if (hlen > p->len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", hlen, p->len)); /* free (drop) packet pbufs */ pbuf_free(p); IP6_STATS_INC(ip6.lenerr); IP6_STATS_INC(ip6.drop); goto ip6_input_cleanup; } pbuf_header(p, -(s16_t)hlen); break; case IP6_NEXTH_ROUTING: LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); /* Get next header type. */ nexth = *((u8_t *)p->payload); /* Get the header length. */ hlen = 8 * (1 + *((u8_t *)p->payload + 1)); ip_data.current_ip_header_tot_len += hlen; /* Skip over this header. */ if (hlen > p->len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", hlen, p->len)); /* free (drop) packet pbufs */ pbuf_free(p); IP6_STATS_INC(ip6.lenerr); IP6_STATS_INC(ip6.drop); goto ip6_input_cleanup; } pbuf_header(p, -(s16_t)hlen); break; case IP6_NEXTH_FRAGMENT: { struct ip6_frag_hdr * frag_hdr; LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); frag_hdr = (struct ip6_frag_hdr *)p->payload; /* Get next header type. */ nexth = frag_hdr->_nexth; /* Fragment Header length. */ hlen = 8; ip_data.current_ip_header_tot_len += hlen; /* Make sure this header fits in current pbuf. */ if (hlen > p->len) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", hlen, p->len)); /* free (drop) packet pbufs */ pbuf_free(p); IP6_FRAG_STATS_INC(ip6_frag.lenerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto ip6_input_cleanup; } /* Offset == 0 and more_fragments == 0? */ if ((frag_hdr->_fragment_offset & PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) { /* This is a 1-fragment packet, usually a packet that we have * already reassembled. Skip this header anc continue. */ pbuf_header(p, -(s16_t)hlen); } else { #if LWIP_IPV6_REASS /* reassemble the packet */ p = ip6_reass(p); /* packet not fully reassembled yet? */ if (p == NULL) { goto ip6_input_cleanup; } /* Returned p point to IPv6 header. * Update all our variables and pointers and continue. */ ip6hdr = (struct ip6_hdr *)p->payload; nexth = IP6H_NEXTH(ip6hdr); hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; pbuf_header(p, -IP6_HLEN); #else /* LWIP_IPV6_REASS */ /* free (drop) packet pbufs */ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); pbuf_free(p); IP6_STATS_INC(ip6.opterr); IP6_STATS_INC(ip6.drop); goto ip6_input_cleanup; #endif /* LWIP_IPV6_REASS */ } break; } default: goto options_done; break; } } options_done: /* p points to IPv6 header again. */ pbuf_header_force(p, ip_data.current_ip_header_tot_len); /* send to upper layers */ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); ip6_debug_print(p); LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) #endif /* LWIP_RAW */ { switch (nexth) { case IP6_NEXTH_NONE: pbuf_free(p); break; #if LWIP_UDP case IP6_NEXTH_UDP: #if LWIP_UDPLITE case IP6_NEXTH_UDPLITE: #endif /* LWIP_UDPLITE */ /* Point to payload. */ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); udp_input(p, inp); break; #endif /* LWIP_UDP */ #if LWIP_TCP case IP6_NEXTH_TCP: /* Point to payload. */ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); tcp_input(p, inp); break; #endif /* LWIP_TCP */ #if LWIP_ICMP6 case IP6_NEXTH_ICMP6: /* Point to payload. */ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len); icmp6_input(p, inp); break; #endif /* LWIP_ICMP */ default: #if LWIP_ICMP6 /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); } #endif /* LWIP_ICMP */ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); pbuf_free(p); IP6_STATS_INC(ip6.proterr); IP6_STATS_INC(ip6.drop); break; } } ip6_input_cleanup: ip_data.current_netif = NULL; ip_data.current_input_netif = NULL; ip_data.current_ip6_header = NULL; ip_data.current_ip_header_tot_len = 0; ip6_addr_set_zero(ip6_current_src_addr()); ip6_addr_set_zero(ip6_current_dest_addr()); return ERR_OK; }
/** * Forwards an IPv6 packet. It finds an appropriate route for the * packet, decrements the HL value of the packet, and outputs * the packet on the appropriate interface. * * @param p the packet to forward (p->payload points to IP header) * @param iphdr the IPv6 header of the input packet * @param inp the netif on which this packet was received */ static void ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) { struct netif *netif; /* do not forward link-local addresses */ if (ip6_addr_islinklocal(ip6_current_dest_addr())) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return; } /* Find network interface where to forward this IP packet to. */ netif = ip6_route(IP6_ADDR_ANY6, ip6_current_dest_addr()); if (netif == NULL) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(ip6_current_dest_addr()), IP6_ADDR_BLOCK2(ip6_current_dest_addr()), IP6_ADDR_BLOCK3(ip6_current_dest_addr()), IP6_ADDR_BLOCK4(ip6_current_dest_addr()), IP6_ADDR_BLOCK5(ip6_current_dest_addr()), IP6_ADDR_BLOCK6(ip6_current_dest_addr()), IP6_ADDR_BLOCK7(ip6_current_dest_addr()), IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return; } /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return; } /* decrement HL */ IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); /* send ICMP6 if HL == 0 */ if (IP6H_HOPLIM(iphdr) == 0) { #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_time_exceeded(p, ICMP6_TE_HL); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.drop); return; } if (netif->mtu && (p->tot_len > netif->mtu)) { #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_packet_too_big(p, netif->mtu); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.drop); return; } LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(ip6_current_dest_addr()), IP6_ADDR_BLOCK2(ip6_current_dest_addr()), IP6_ADDR_BLOCK3(ip6_current_dest_addr()), IP6_ADDR_BLOCK4(ip6_current_dest_addr()), IP6_ADDR_BLOCK5(ip6_current_dest_addr()), IP6_ADDR_BLOCK6(ip6_current_dest_addr()), IP6_ADDR_BLOCK7(ip6_current_dest_addr()), IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); /* transmit pbuf on chosen interface */ netif->output_ip6(netif, p, ip6_current_dest_addr()); IP6_STATS_INC(ip6.fw); IP6_STATS_INC(ip6.xmit); return; }
/** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function * finds a corresponding UDP PCB and hands over the pbuf to the pcbs * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; u16_t src, dest; u8_t broadcast; u8_t for_us = 0; LWIP_UNUSED_ARG(inp); PERF_START; UDP_STATS_INC(udp.recv); /* Check minimum length (UDP header) */ if (p->len < UDP_HLEN) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } udphdr = (struct udp_hdr *)p->payload; /* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); /* convert src and dest ports to host byte order */ src = lwip_ntohs(udphdr->src); dest = lwip_ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ LWIP_DEBUGF(UDP_DEBUG, ("udp (")); ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest))); ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src))); pcb = NULL; prev = NULL; uncon_pcb = NULL; /* Iterate through the UDP pcb list for a matching pcb. * 'Perfect match' pcbs (connected to the remote port & ip address) are * preferred. If no perfect match is found, the first unconnected pcb that * matches the local port and ip address gets the datagram. */ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { /* print the PCB local and remote address */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); /* compare PCB local addr+port to UDP destination addr+port */ if ((pcb->local_port == dest) && (udp_input_local_match(pcb, inp, broadcast) != 0)) { if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) && ((uncon_pcb == NULL) #if SO_REUSE /* prefer specific IPs over cath-all */ || !ip_addr_isany(&pcb->local_ip) #endif /* SO_REUSE */ )) { /* the first unconnected matching PCB */ uncon_pcb = pcb; } /* compare PCB remote addr+port to UDP source addr+port */ if ((pcb->remote_port == src) && (ip_addr_isany_val(pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } } prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; } /* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL) { for_us = 1; } else { #if LWIP_IPV6 if (ip_current_is_v6()) { for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0; } #endif /* LWIP_IPV6 */ #if LWIP_IPV4 if (!ip_current_is_v6()) { for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr()); } #endif /* LWIP_IPV4 */ } if (for_us) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); #if CHECKSUM_CHECK_UDP IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) { #if LWIP_UDPLITE if (ip_current_header_proto() == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ u16_t chklen = lwip_ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ goto chkerr; } } if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE, p->tot_len, chklen, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } else #endif /* LWIP_UDPLITE */ { if (udphdr->chksum != 0) { if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } } } #endif /* CHECKSUM_CHECK_UDP */ if (pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\n", 0); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } if (pcb != NULL) { MIB2_STATS_INC(mib2.udpindatagrams); #if SO_REUSE && SO_REUSE_RXTOALL if (ip_get_option(pcb, SOF_REUSEADDR) && (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) { /* pass broadcast- or multicast packets to all multicast pcbs if SOF_REUSEADDR is set on the first match */ struct udp_pcb *mpcb; u8_t p_header_changed = 0; s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { if (mpcb != pcb) { /* compare PCB local addr+port to UDP destination addr+port */ if ((mpcb->local_port == dest) && (udp_input_local_match(mpcb, inp, broadcast) != 0)) { /* pass a copy of the packet to all local matches */ if (mpcb->recv != NULL) { struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { pbuf_header_force(p, hdrs_len); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (q != NULL) { err_t err = pbuf_copy(q, p); if (err == ERR_OK) { /* move payload to UDP data */ pbuf_header(q, -hdrs_len); mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); } } } } } } if (p_header_changed) { /* and move payload to UDP data again */ pbuf_header(p, -hdrs_len); } } #endif /* SO_REUSE && SO_REUSE_RXTOALL */ /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); #if LWIP_ICMP || LWIP_ICMP6 /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN); icmp_port_unreach(ip_current_is_v6(), p); } #endif /* LWIP_ICMP || LWIP_ICMP6 */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpnoports); pbuf_free(p); } } else {
/** * Reassembles incoming IPv6 fragments into an IPv6 datagram. * * @param p points to the IPv6 Fragment Header * @return NULL if reassembly is incomplete, pbuf pointing to * IPv6 Header if reassembly is complete */ struct pbuf * ip6_reass(struct pbuf *p) { struct ip6_reassdata *ipr, *ipr_prev; struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; struct ip6_frag_hdr *frag_hdr; u16_t offset, len; u16_t clen; u8_t valid = 1; struct pbuf *q; IP6_FRAG_STATS_INC(ip6_frag.recv); if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) { /* ip6_frag_hdr must be in the first pbuf, not chained */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } frag_hdr = (struct ip6_frag_hdr *) p->payload; clen = pbuf_clen(p); offset = lwip_ntohs(frag_hdr->_fragment_offset); /* Calculate fragment length from IPv6 payload length. * Adjust for headers before Fragment Header. * And finally adjust by Fragment Header length. */ len = lwip_ntohs(ip6_current_header()->_plen); len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN); len -= IP6_FRAG_HLEN; /* Look for the datagram the fragment belongs to in the current datagram queue, * remembering the previous in the queue for later dequeueing. */ for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { /* Check if the incoming fragment matches the one currently present in the reassembly buffer. If so, we proceed with copying the fragment into the buffer. */ if ((frag_hdr->_identification == ipr->identification) && ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) && ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) { IP6_FRAG_STATS_INC(ip6_frag.cachehit); break; } ipr_prev = ipr; } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) { #if IP_REASS_FREE_OLDEST /* Make room and try again. */ ip6_reass_remove_oldest_datagram(ipr, clen); ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr != NULL) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } memset(ipr, 0, sizeof(struct ip6_reassdata)); ipr->timer = IP_REASS_MAXAGE; /* enqueue the new structure to the front of the list */ ipr->next = reassdatagrams; reassdatagrams = ipr; /* Use the current IPv6 header for src/dest address reference. * Eventually, we will replace it when we get the first fragment * (it might be this one, in any case, it is done later). */ #if IPV6_FRAG_COPYHEADER MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); #else /* IPV6_FRAG_COPYHEADER */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #endif /* IPV6_FRAG_COPYHEADER */ /* copy the fragmented packet id. */ ipr->identification = frag_hdr->_identification; /* copy the nexth field */ ipr->nexth = frag_hdr->_nexth; } /* Check if we are allowed to enqueue more datagrams. */ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST ip6_reass_remove_oldest_datagram(ipr, clen); if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { /* @todo: send ICMPv6 time exceeded here? */ /* drop this pbuf */ IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } /* Overwrite Fragment Header with our own helper struct. */ #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4). This cannot fail since we already checked when receiving this fragment. */ u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #else /* IPV6_FRAG_COPYHEADER */ LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1", sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); #endif /* IPV6_FRAG_COPYHEADER */ iprh = (struct ip6_reass_helper *)p->payload; iprh->next_pbuf = NULL; iprh->start = (offset & IP6_FRAG_OFFSET_MASK); iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; /* find the right place to insert this pbuf */ /* Iterate through until we either get to the end of the list (append), * or we find on with a larger offset (insert). */ for (q = ipr->p; q != NULL;) { iprh_tmp = (struct ip6_reass_helper*)q->payload; if (iprh->start < iprh_tmp->start) { #if IP_REASS_CHECK_OVERLAP if (iprh->end > iprh_tmp->start) { /* fragment overlaps with following, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } if (iprh_prev != NULL) { if (iprh->start < iprh_prev->end) { /* fragment overlaps with previous, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } #endif /* IP_REASS_CHECK_OVERLAP */ /* the new pbuf should be inserted before this */ iprh->next_pbuf = q; if (iprh_prev != NULL) { /* not the fragment with the lowest offset */ iprh_prev->next_pbuf = p; } else { /* fragment with the lowest offset */ ipr->p = p; } break; } else if (iprh->start == iprh_tmp->start) { /* received the same datagram twice: no need to keep the datagram */ IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; #if IP_REASS_CHECK_OVERLAP } else if (iprh->start < iprh_tmp->end) { /* overlap: no need to keep the new datagram */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; #endif /* IP_REASS_CHECK_OVERLAP */ } else { /* Check if the fragments received so far have no gaps. */ if (iprh_prev != NULL) { if (iprh_prev->end != iprh_tmp->start) { /* There is a fragment missing between the current * and the previous fragment */ valid = 0; } } } q = iprh_tmp->next_pbuf; iprh_prev = iprh_tmp; } /* If q is NULL, then we made it to the end of the list. Determine what to do now */ if (q == NULL) { if (iprh_prev != NULL) { /* this is (for now), the fragment with the highest offset: * chain it to the last fragment */ #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); #endif /* IP_REASS_CHECK_OVERLAP */ iprh_prev->next_pbuf = p; if (iprh_prev->end != iprh->start) { valid = 0; } } else { #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("no previous fragment, this must be the first fragment!", ipr->p == NULL); #endif /* IP_REASS_CHECK_OVERLAP */ /* this is the first fragment we ever received for this ip datagram */ ipr->p = p; } } /* Track the current number of pbufs current 'in-flight', in order to limit the number of fragments that may be enqueued at any one time */ ip6_reass_pbufcount += clen; /* Remember IPv6 header if this is the first fragment. */ if (iprh->start == 0) { #if IPV6_FRAG_COPYHEADER if (iprh->next_pbuf != NULL) { MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); } #else /* IPV6_FRAG_COPYHEADER */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #endif /* IPV6_FRAG_COPYHEADER */ } /* If this is the last fragment, calculate total packet length. */ if ((offset & IP6_FRAG_MORE_FLAG) == 0) { ipr->datagram_len = iprh->end; } /* Additional validity tests: we have received first and last fragment. */ iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; if (iprh_tmp->start != 0) { valid = 0; } if (ipr->datagram_len == 0) { valid = 0; } /* Final validity test: no gaps between current and last fragment. */ iprh_prev = iprh; q = iprh->next_pbuf; while ((q != NULL) && valid) { iprh = (struct ip6_reass_helper*)q->payload; if (iprh_prev->end != iprh->start) { valid = 0; break; } iprh_prev = iprh; q = iprh->next_pbuf; } if (valid) { /* All fragments have been received */ struct ip6_hdr* iphdr_ptr; /* chain together the pbufs contained within the ip6_reassdata list. */ iprh = (struct ip6_reass_helper*) ipr->p->payload; while (iprh != NULL) { struct pbuf* next_pbuf = iprh->next_pbuf; if (next_pbuf != NULL) { /* Save next helper struct (will be hidden in next step). */ iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload; /* hide the fragment header for every succeeding fragment */ pbuf_header(next_pbuf, -IP6_FRAG_HLEN); #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */ u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM)); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #endif pbuf_cat(ipr->p, next_pbuf); } else { iprh_tmp = NULL; } iprh = iprh_tmp; } #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM)); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN); MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN); #else iphdr_ptr = ipr->iphdr; #endif /* Adjust datagram length by adding header lengths. */ ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr) + IP6_FRAG_HLEN - IP6_HLEN); /* Set payload length in ip header. */ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); /* Get the first pbuf. */ p = ipr->p; /* Restore Fragment Header in first pbuf. Mark as "single fragment" * packet. Restore nexth. */ frag_hdr = (struct ip6_frag_hdr *) p->payload; frag_hdr->_nexth = ipr->nexth; frag_hdr->reserved = 0; frag_hdr->_fragment_offset = 0; frag_hdr->_identification = 0; /* release the sources allocate for the fragment queue entry */ if (reassdatagrams == ipr) { /* it was the first in the list */ reassdatagrams = ipr->next; } else { /* it wasn't the first, so it must have a valid 'prev' */ LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); ipr_prev->next = ipr->next; } memp_free(MEMP_IP6_REASSDATA, ipr); /* adjust the number of pbufs currently queued for reassembly. */ ip6_reass_pbufcount -= pbuf_clen(p); /* Move pbuf back to IPv6 header. This cannot fail since we already checked when receiving this fragment. */ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) { LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); pbuf_free(p); return NULL; } /* Return the pbuf chain */ return p; } /* the datagram is not (yet?) reassembled completely */ return NULL; nullreturn: pbuf_free(p); return NULL; }
/** * Process an input ICMPv6 message. Called by ip6_input. * * Will generate a reply for echo requests. Other messages are forwarded * to nd6_input, or mld6_input. * * @param p the mld packet, p->payload pointing to the icmpv6 header * @param inp the netif on which this packet was received */ void icmp6_input(struct pbuf *p, struct interface *inp) { struct icmp6_hdr *icmp6hdr; struct pbuf * r; ip6_addr_t * reply_src; ICMP6_STATS_INC(icmp6.recv); /* Check that ICMPv6 header fits in payload */ if (p->len < sizeof(struct icmp6_hdr)) { /* drop short packets */ pbuf_free(p); ICMP6_STATS_INC(icmp6.lenerr); ICMP6_STATS_INC(icmp6.drop); return; } icmp6hdr = (struct icmp6_hdr *)p->payload; #if LWIP_ICMP6_CHECKSUM_CHECK if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), ip6_current_dest_addr()) != 0) { /* Checksum failed */ pbuf_free(p); ICMP6_STATS_INC(icmp6.chkerr); ICMP6_STATS_INC(icmp6.drop); return; } #endif /* LWIP_ICMP6_CHECKSUM_CHECK */ switch (icmp6hdr->type) { case ICMP6_TYPE_NA: /* Neighbor advertisement */ case ICMP6_TYPE_NS: /* Neighbor solicitation */ case ICMP6_TYPE_RA: /* Router advertisement */ case ICMP6_TYPE_RD: /* Redirect */ case ICMP6_TYPE_PTB: /* Packet too big */ nd6_input(p, inp); return; break; case ICMP6_TYPE_RS: #if LWIP_IPV6_FORWARD /* TODO implement router functionality */ #endif break; #if LWIP_IPV6_MLD case ICMP6_TYPE_MLQ: case ICMP6_TYPE_MLR: case ICMP6_TYPE_MLD: mld6_input(p, inp); return; break; #endif case ICMP6_TYPE_EREQ: #if !LWIP_MULTICAST_PING /* multicast destination address? */ if (ip6_addr_ismulticast(ip6_current_dest_addr())) { /* drop */ pbuf_free(p); ICMP6_STATS_INC(icmp6.drop); return; } #endif /* LWIP_MULTICAST_PING */ /* Allocate reply. */ r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); if (r == NULL) { /* drop */ pbuf_free(p); ICMP6_STATS_INC(icmp6.memerr); return; } /* Copy echo request. */ if (pbuf_copy(r, p) != ERR_OK) { /* drop */ pbuf_free(p); pbuf_free(r); ICMP6_STATS_INC(icmp6.err); return; } /* Determine reply source IPv6 address. */ #if LWIP_MULTICAST_PING if (ip6_addr_ismulticast(ip6_current_dest_addr())) { reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); if (reply_src == NULL) { /* drop */ pbuf_free(p); pbuf_free(r); ICMP6_STATS_INC(icmp6.rterr); return; } } else #endif /* LWIP_MULTICAST_PING */ { reply_src = ip6_current_dest_addr(); } /* Set fields in reply. */ ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); /* Send reply. */ ICMP6_STATS_INC(icmp6.xmit); ip6_output_if(r, reply_src, ip6_current_src_addr(), LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); pbuf_free(r); break; default: ICMP6_STATS_INC(icmp6.proterr); ICMP6_STATS_INC(icmp6.drop); break; } pbuf_free(p); }
/** * Process an input MLD message. Called by icmp6_input. * * @param p the mld packet, p->payload pointing to the icmpv6 header * @param inp the netif on which this packet was received */ void mld6_input(struct pbuf *p, struct netif *inp) { struct mld_header * mld_hdr; struct mld_group* group; MLD6_STATS_INC(mld6.recv); /* Check that mld header fits in packet. */ if (p->len < sizeof(struct mld_header)) { // TODO debug message pbuf_free(p); MLD6_STATS_INC(mld6.lenerr); MLD6_STATS_INC(mld6.drop); return; } mld_hdr = (struct mld_header *)p->payload; switch (mld_hdr->type) { case ICMP6_TYPE_MLQ: /* Multicast listener query. */ { /* Is it a general query? */ if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && ip6_addr_isany(&(mld_hdr->multicast_address))) { MLD6_STATS_INC(mld6.rx_general); /* Report all groups, except all nodes group, and if-local groups. */ group = mld_group_list; while (group != NULL) { if ((group->netif == inp) && (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { mld6_delayed_report(group, mld_hdr->max_resp_delay); } group = group->next; } } else { /* Have we joined this group? * We use IP6 destination address to have a memory aligned copy. * mld_hdr->multicast_address should be the same. */ MLD6_STATS_INC(mld6.rx_group); group = mld6_lookfor_group(inp, ip6_current_dest_addr()); if (group != NULL) { /* Schedule a report. */ mld6_delayed_report(group, mld_hdr->max_resp_delay); } } break; /* ICMP6_TYPE_MLQ */ } case ICMP6_TYPE_MLR: /* Multicast listener report. */ { /* Have we joined this group? * We use IP6 destination address to have a memory aligned copy. * mld_hdr->multicast_address should be the same. */ MLD6_STATS_INC(mld6.rx_report); group = mld6_lookfor_group(inp, ip6_current_dest_addr()); if (group != NULL) { /* If we are waiting to report, cancel it. */ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { group->timer = 0; /* stopped */ group->group_state = MLD6_GROUP_IDLE_MEMBER; group->last_reporter_flag = 0; } } break; /* ICMP6_TYPE_MLR */ } case ICMP6_TYPE_MLD: /* Multicast listener done. */ { /* Do nothing, router will query us. */ break; /* ICMP6_TYPE_MLD */ } default: MLD6_STATS_INC(mld6.proterr); MLD6_STATS_INC(mld6.drop); break; } pbuf_free(p); }
static void pxping_recv6(void *arg, struct pbuf *p) { struct pxping *pxping = (struct pxping *)arg; struct icmp6_echo_hdr *icmph; size_t bufsize; struct pong6 *pong; int mapped; void *reqdata; size_t reqsize; struct sockaddr_in6 src, dst; int hopl; IP_OPTION_INFORMATION opts; int status; pong = NULL; icmph = (struct icmp6_echo_hdr *)p->payload; memset(&dst, 0, sizeof(dst)); dst.sin6_family = AF_INET6; mapped = pxremap_outbound_ip6((ip6_addr_t *)&dst.sin6_addr, ip6_current_dest_addr()); if (RT_UNLIKELY(mapped == PXREMAP_FAILED)) { goto out; } hopl = IP6H_HOPLIM(ip6_current_header()); if (mapped == PXREMAP_ASIS) { if (RT_UNLIKELY(hopl == 1)) { status = pbuf_header(p, ip_current_header_tot_len()); if (RT_LIKELY(status == 0)) { icmp6_time_exceeded(p, ICMP6_TE_HL); } goto out; } --hopl; } status = pbuf_header(p, -(u16_t)sizeof(*icmph)); /* to ping payload */ if (RT_UNLIKELY(status != 0)) { goto out; } bufsize = sizeof(ICMPV6_ECHO_REPLY) + p->tot_len; pong = (struct pong6 *)malloc(sizeof(*pong) - sizeof(pong->buf) + bufsize); if (RT_UNLIKELY(pong == NULL)) { goto out; } pong->bufsize = bufsize; pong->netif = pxping->netif; ip6_addr_copy(pong->reqsrc, *ip6_current_src_addr()); memcpy(&pong->reqicmph, icmph, sizeof(*icmph)); memset(pong->buf, 0xa5, pong->bufsize); pong->reqsize = reqsize = p->tot_len; if (p->next == NULL) { /* single pbuf can be directly used as request data source */ reqdata = p->payload; } else { /* data from pbuf chain must be concatenated */ pbuf_copy_partial(p, pong->buf, p->tot_len, 0); reqdata = pong->buf; } memset(&src, 0, sizeof(src)); src.sin6_family = AF_INET6; src.sin6_addr = in6addr_any; /* let the OS select host source address */ memset(&opts, 0, sizeof(opts)); opts.Ttl = hopl; status = Icmp6SendEcho2(pxping->hdl6, NULL, pxping->callback6, pong, &src, &dst, reqdata, (WORD)reqsize, &opts, pong->buf, (DWORD)pong->bufsize, 5 * 1000 /* ms */); if (RT_UNLIKELY(status != 0)) { DPRINTF(("Icmp6SendEcho2: unexpected status %d\n", status)); goto out; } else if ((status = GetLastError()) != ERROR_IO_PENDING) { int code; DPRINTF(("Icmp6SendEcho2: error %d\n", status)); switch (status) { case ERROR_NETWORK_UNREACHABLE: case ERROR_HOST_UNREACHABLE: code = ICMP6_DUR_NO_ROUTE; break; default: code = -1; break; } if (code != -1) { /* move payload back to IP header */ status = pbuf_header(p, (u16_t)(sizeof(*icmph) + ip_current_header_tot_len())); if (RT_LIKELY(status == 0)) { icmp6_dest_unreach(p, code); } } goto out; } pong = NULL; /* callback owns it now */ out: if (pong != NULL) { free(pong); } pbuf_free(p); }
/** * Reassembles incoming IPv6 fragments into an IPv6 datagram. * * @param p points to the IPv6 Fragment Header * @param len the length of the payload (after Fragment Header) * @return NULL if reassembly is incomplete, pbuf pointing to * IPv6 Header if reassembly is complete */ struct pbuf * ip6_reass(struct pbuf *p) { struct ip6_reassdata *ipr, **pipr; struct ip6_reass_helper *iprh, *iprh_tmp; struct ip6_reass_helper **pnext; struct ip6_frag_hdr * frag_hdr; size_t unfrag_len; u16_t offset, len, start, end, validlen; u8_t clen; IP6_FRAG_STATS_INC(ip6_frag.recv); frag_hdr = (struct ip6_frag_hdr *) p->payload; clen = pbuf_clen(p); offset = ntohs(frag_hdr->_fragment_offset); /* Calculate fragment length from IPv6 payload length. * Adjust for headers before Fragment Header. * And finally adjust by Fragment Header length. */ len = ntohs(ip6_current_header()->_plen); len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; len -= IP6_FRAG_HLEN; start = (offset & IP6_FRAG_OFFSET_MASK); end = start + len; /* Look for the datagram the fragment belongs to in the current datagram queue, * remembering the previous in the queue for later dequeueing. */ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { /* Check if the incoming fragment matches the one currently present in the reassembly buffer. If so, we proceed with copying the fragment into the buffer. */ if ((frag_hdr->_identification == ipr->identification) && ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr.src)) && ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr.dest))) { IP6_FRAG_STATS_INC(ip6_frag.cachehit); break; } } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) { #if IP_REASS_FREE_OLDEST /* Make room and try again. */ ip6_reass_remove_oldest_datagram(ipr, clen); ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) #endif /* IP_REASS_FREE_OLDEST */ { IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } memset(ipr, 0, sizeof(struct ip6_reassdata)); ipr->timer = IP_REASS_MAXAGE; /* enqueue the new structure to the front of the list */ ipr->next = reassdatagrams; reassdatagrams = ipr; /* Use the current IPv6 header for src/dest address reference. * Eventually, we will replace it when we get the first fragment * (it might be this one, in any case, it is done later). */ SMEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN); if (start == 0) { ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header(); } /* copy the fragmented packet id. */ ipr->identification = frag_hdr->_identification; } /* If this is the last fragment, save total packet length. */ if ((offset & IP6_FRAG_MORE_FLAG) == 0) { #if IP_REASS_CHECK_OVERLAP if (ipr->datagram_len != 0) { IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } #endif /* IP_REASS_CHECK_OVERLAP */ ipr->datagram_len = end; } /* find the place to insert this pbuf */ validlen = 0; for (pnext = &ipr->iprh; *pnext != NULL; pnext = &(*pnext)->next) { iprh_tmp = *pnext; if (start < iprh_tmp->start) { /* the new pbuf should be inserted before this */ #if IP_REASS_CHECK_OVERLAP if (end > iprh_tmp->start) { /* fragment overlaps with following, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } #endif /* IP_REASS_CHECK_OVERLAP */ break; } else if (start == iprh_tmp->start) { /* received the same datagram twice: no need to keep the datagram */ IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } #if IP_REASS_CHECK_OVERLAP else if (start < iprh_tmp->end) { /* overlap: no need to keep the new datagram */ IP6_FRAG_STATS_INC(ip6_frag.proterr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } #endif /* IP_REASS_CHECK_OVERLAP */ else { /* Check if the fragments received so far have no gaps. */ if (validlen == iprh_tmp->start) { validlen = iprh_tmp->end; } else { validlen = 0; } } } /* Check if we are allowed to enqueue more datagrams. */ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST ip6_reass_remove_oldest_datagram(ipr, clen); if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) #endif /* IP_REASS_FREE_OLDEST */ { /* @todo: send ICMPv6 time exceeded here? */ /* drop this pbuf */ IP6_FRAG_STATS_INC(ip6_frag.memerr); IP6_FRAG_STATS_INC(ip6_frag.drop); goto nullreturn; } } if (start == 0 && ipr->iphdr0 == NULL) { /* * We've got the fragment with offset 0 out of order, remember its * IPv6 header location (in the hidden part of the current pbuf) * and update the copy in ip6_reassdata::iphdr. We don't need to * copy complete header since src and dest are the same as in the * first fragment we received. */ ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header(); SMEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN - 2 * sizeof(ip_addr_p_t)); } /* Overwrite IPv6 Header with our own helper struct (aligned). */ iprh = (struct ip6_reass_helper *) (((uintptr_t)(u8_t *)ip6_current_header() + sizeof(void *) - 1) & ~(sizeof(void *) - 1)); iprh->p = p; iprh->start = start; iprh->end = end; /* insert it into the list */ iprh->next = *pnext; *pnext = iprh; /* Track the current number of pbufs current 'in-flight', in order to limit the number of fragments that may be enqueued at any one time */ ip6_reass_pbufcount += clen; if (ipr->datagram_len == 0) { /* We still don't have the last fragment. */ return NULL; } if (validlen == start) { validlen = end; } else { /* There are gaps before this fragment. */ return NULL; } if (validlen != 0) { /* * We know we have all the data up to the end of this fragment and * we know the total length. Check if the reassembly is complete. */ for (iprh_tmp = iprh->next; iprh_tmp != NULL; iprh_tmp = iprh_tmp->next) { if (validlen == iprh_tmp->start) { validlen = iprh_tmp->end; } else { validlen = 0; break; } } if (validlen != ipr->datagram_len) { /* the datagram is not yet reassembled completely */ return NULL; } } /* * All fragments have been received. Reassemble original datagram * and return it to ip6_input() to be processed instead of the final * fragment that completed the reassembly. */ /* chain together the pbufs contained within the ip6_reassdata list. */ p = NULL; for (iprh = ipr->iprh; iprh != NULL; iprh = iprh->next) { if (p == NULL) { p = iprh->p; } else { /* hide the fragment header for every succeeding fragment */ pbuf_header(iprh->p, -IP6_FRAG_HLEN); pbuf_cat(p, iprh->p); } } /* Adjust datagram length by adding preceding header lengths. */ unfrag_len = (u8_t *)p->payload - (u8_t *)ipr->iphdr0; # ifndef VBOX ipr->datagram_len += unfrag_len - IP6_HLEN + IP6_FRAG_HLEN; # else LWIP_ASSERT("overflow", (s16_t)unfrag_len == (ssize_t)unfrag_len); /* s16_t because of pbuf_header call */ ipr->datagram_len += (u16_t)(unfrag_len - IP6_HLEN + IP6_FRAG_HLEN); # endif /* Set payload length in ip header. */ ipr->iphdr._plen = htons(ipr->datagram_len); /* restore IPv6 header (overwritten with ip6_reass_helper) */ SMEMCPY(ipr->iphdr0, &ipr->iphdr, IP6_HLEN); /* Mark as "single fragment" packet (see caller). */ frag_hdr = (struct ip6_frag_hdr *) p->payload; frag_hdr->_fragment_offset = 0; /* Unlink from the reassdatagrams list */ for (pipr = &reassdatagrams; *pipr != NULL; pipr = &(*pipr)->next) { if (*pipr == ipr) { (*pipr) = ipr->next; break; } } memp_free(MEMP_IP6_REASSDATA, ipr); /* adjust the number of pbufs currently queued for reassembly. */ ip6_reass_pbufcount -= pbuf_clen(p); /* Move pbuf back to IPv6 header. */ # ifndef VBOX if (pbuf_header(p, unfrag_len) != 0) { # else if (pbuf_header(p, (s16_t)unfrag_len) != 0) { # endif LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); goto nullreturn; } /* Return the pbuf chain */ return p; nullreturn: pbuf_free(p); return NULL; } #endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ #if LWIP_IPV6 && LWIP_IPV6_FRAG /** Allocate a new struct pbuf_custom_ref */ static struct pbuf_custom_ref* ip6_frag_alloc_pbuf_custom_ref(void) { return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); } /** Free a struct pbuf_custom_ref */ static void ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) { LWIP_ASSERT("p != NULL", p != NULL); memp_free(MEMP_FRAG_PBUF, p); }
/** * Reassembles incoming IPv6 fragments into an IPv6 datagram. * * @param p points to the IPv6 Fragment Header * @return NULL if reassembly is incomplete, pbuf pointing to * IPv6 Header if reassembly is complete */ struct pbuf * ip6_reass(struct pbuf *p) { struct ip6_reassdata *ipr, *ipr_prev; struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; struct ip6_frag_hdr *frag_hdr; u16_t offset, len, start, end; ptrdiff_t hdrdiff; u16_t clen; u8_t valid = 1; struct pbuf *q, *next_pbuf; IP6_FRAG_STATS_INC(ip6_frag.recv); /* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */ LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf", p->len >= sizeof(struct ip6_frag_hdr)); frag_hdr = (struct ip6_frag_hdr *) p->payload; clen = pbuf_clen(p); offset = lwip_ntohs(frag_hdr->_fragment_offset); /* Calculate fragment length from IPv6 payload length. * Adjust for headers before Fragment Header. * And finally adjust by Fragment Header length. */ len = lwip_ntohs(ip6_current_header()->_plen); hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header(); LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF); LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN); hdrdiff -= IP6_HLEN; hdrdiff += IP6_FRAG_HLEN; if (hdrdiff > len) { IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } len = (u16_t)(len - hdrdiff); start = (offset & IP6_FRAG_OFFSET_MASK); if (start > (0xFFFF - len)) { /* u16_t overflow, cannot handle this */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } /* Look for the datagram the fragment belongs to in the current datagram queue, * remembering the previous in the queue for later dequeueing. */ for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) { /* Check if the incoming fragment matches the one currently present in the reassembly buffer. If so, we proceed with copying the fragment into the buffer. */ if ((frag_hdr->_identification == ipr->identification) && ip6_addr_cmp_packed(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) && ip6_addr_cmp_packed(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) { IP6_FRAG_STATS_INC(ip6_frag.cachehit); break; } ipr_prev = ipr; } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr == NULL) { #if IP_REASS_FREE_OLDEST /* Make room and try again. */ ip6_reass_remove_oldest_datagram(ipr, clen); ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); if (ipr != NULL) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { IP6_FRAG_STATS_INC(ip6_frag.memerr); goto nullreturn; } } memset(ipr, 0, sizeof(struct ip6_reassdata)); ipr->timer = IPV6_REASS_MAXAGE; /* enqueue the new structure to the front of the list */ ipr->next = reassdatagrams; reassdatagrams = ipr; /* Use the current IPv6 header for src/dest address reference. * Eventually, we will replace it when we get the first fragment * (it might be this one, in any case, it is done later). */ /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; #if IPV6_FRAG_COPYHEADER MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src)); MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest)); #endif /* IPV6_FRAG_COPYHEADER */ #if LWIP_IPV6_SCOPES /* Also store the address zone information. * @todo It is possible that due to netif destruction and recreation, the * stored zones end up resolving to a different interface. In that case, we * risk sending a "time exceeded" ICMP response over the wrong link. * Ideally, netif destruction would clean up matching pending reassembly * structures, but custom zone mappings would make that non-trivial. */ ipr->src_zone = ip6_addr_zone(ip6_current_src_addr()); ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr()); #endif /* LWIP_IPV6_SCOPES */ /* copy the fragmented packet id. */ ipr->identification = frag_hdr->_identification; /* copy the nexth field */ ipr->nexth = frag_hdr->_nexth; } /* Check if we are allowed to enqueue more datagrams. */ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST ip6_reass_remove_oldest_datagram(ipr, clen); if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { /* re-search ipr_prev since it might have been removed */ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { if (ipr_prev->next == ipr) { break; } } } else #endif /* IP_REASS_FREE_OLDEST */ { /* @todo: send ICMPv6 time exceeded here? */ /* drop this pbuf */ IP6_FRAG_STATS_INC(ip6_frag.memerr); goto nullreturn; } } /* Overwrite Fragment Header with our own helper struct. */ #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4). This cannot fail since we already checked when receiving this fragment. */ u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #else /* IPV6_FRAG_COPYHEADER */ LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1", sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); #endif /* IPV6_FRAG_COPYHEADER */ /* Prepare the pointer to the helper structure, and its initial values. * Do not yet write to the structure itself, as we still have to make a * backup of the original data, and we should not do that until we know for * sure that we are going to add this packet to the list. */ iprh = (struct ip6_reass_helper *)p->payload; next_pbuf = NULL; end = (u16_t)(start + len); /* find the right place to insert this pbuf */ /* Iterate through until we either get to the end of the list (append), * or we find on with a larger offset (insert). */ for (q = ipr->p; q != NULL;) { iprh_tmp = (struct ip6_reass_helper*)q->payload; if (start < iprh_tmp->start) { #if IP_REASS_CHECK_OVERLAP if (end > iprh_tmp->start) { /* fragment overlaps with following, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } if (iprh_prev != NULL) { if (start < iprh_prev->end) { /* fragment overlaps with previous, throw away */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; } } #endif /* IP_REASS_CHECK_OVERLAP */ /* the new pbuf should be inserted before this */ next_pbuf = q; if (iprh_prev != NULL) { /* not the fragment with the lowest offset */ iprh_prev->next_pbuf = p; } else { /* fragment with the lowest offset */ ipr->p = p; } break; } else if (start == iprh_tmp->start) { /* received the same datagram twice: no need to keep the datagram */ goto nullreturn; #if IP_REASS_CHECK_OVERLAP } else if (start < iprh_tmp->end) { /* overlap: no need to keep the new datagram */ IP6_FRAG_STATS_INC(ip6_frag.proterr); goto nullreturn; #endif /* IP_REASS_CHECK_OVERLAP */ } else { /* Check if the fragments received so far have no gaps. */ if (iprh_prev != NULL) { if (iprh_prev->end != iprh_tmp->start) { /* There is a fragment missing between the current * and the previous fragment */ valid = 0; } } } q = iprh_tmp->next_pbuf; iprh_prev = iprh_tmp; } /* If q is NULL, then we made it to the end of the list. Determine what to do now */ if (q == NULL) { if (iprh_prev != NULL) { /* this is (for now), the fragment with the highest offset: * chain it to the last fragment */ #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= start); #endif /* IP_REASS_CHECK_OVERLAP */ iprh_prev->next_pbuf = p; if (iprh_prev->end != start) { valid = 0; } } else { #if IP_REASS_CHECK_OVERLAP LWIP_ASSERT("no previous fragment, this must be the first fragment!", ipr->p == NULL); #endif /* IP_REASS_CHECK_OVERLAP */ /* this is the first fragment we ever received for this ip datagram */ ipr->p = p; } } /* Track the current number of pbufs current 'in-flight', in order to limit the number of fragments that may be enqueued at any one time */ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount + clen); /* Remember IPv6 header if this is the first fragment. */ if (start == 0) { /* need to use the none-const pointer here: */ ipr->iphdr = ip_data.current_ip6_header; /* Make a backup of the part of the packet data that we are about to * overwrite, so that we can restore the original later. */ MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh)); /* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they * will be the same as they were. With LWIP_IPV6_SCOPES, the same applies * to the source/destination zones. */ } /* Only after the backup do we get to fill in the actual helper structure. */ iprh->next_pbuf = next_pbuf; iprh->start = start; iprh->end = end; /* If this is the last fragment, calculate total packet length. */ if ((offset & IP6_FRAG_MORE_FLAG) == 0) { ipr->datagram_len = iprh->end; } /* Additional validity tests: we have received first and last fragment. */ iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; if (iprh_tmp->start != 0) { valid = 0; } if (ipr->datagram_len == 0) { valid = 0; } /* Final validity test: no gaps between current and last fragment. */ iprh_prev = iprh; q = iprh->next_pbuf; while ((q != NULL) && valid) { iprh = (struct ip6_reass_helper*)q->payload; if (iprh_prev->end != iprh->start) { valid = 0; break; } iprh_prev = iprh; q = iprh->next_pbuf; } if (valid) { /* All fragments have been received */ struct ip6_hdr* iphdr_ptr; /* chain together the pbufs contained within the ip6_reassdata list. */ iprh = (struct ip6_reass_helper*) ipr->p->payload; while (iprh != NULL) { next_pbuf = iprh->next_pbuf; if (next_pbuf != NULL) { /* Save next helper struct (will be hidden in next step). */ iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload; /* hide the fragment header for every succeeding fragment */ pbuf_remove_header(next_pbuf, IP6_FRAG_HLEN); #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */ u8_t hdrerr = pbuf_remove_header(next_pbuf, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #endif pbuf_cat(ipr->p, next_pbuf); } else { iprh_tmp = NULL; } iprh = iprh_tmp; } /* Get the first pbuf. */ p = ipr->p; #if IPV6_FRAG_COPYHEADER if (IPV6_FRAG_REQROOM > 0) { u8_t hdrerr; /* Restore (only) the bytes that we overwrote beyond the fragment header. * Those bytes may belong to either the IPv6 header or an extension * header placed before the fragment header. */ MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM); /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */ hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM); LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0); } #endif /* We need to get rid of the fragment header itself, which is somewhere in * the middle of the packet (but still in the first pbuf of the chain). * Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary * in order to be able to reassemble packets that are close to full size * (i.e., around 65535 bytes). We simply move up all the headers before the * fragment header, including the IPv6 header, and adjust the payload start * accordingly. This works because all these headers are in the first pbuf * of the chain, and because the caller adjusts all its pointers on * successful reassembly. */ MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr, (size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr)); /* This is where the IPv6 header is now. */ iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr)); /* Adjust datagram length by adding header lengths. */ ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr) - IP6_HLEN); /* Set payload length in ip header. */ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len); /* With the fragment header gone, we now need to adjust the next-header * field of whatever header was originally before it. Since the packet made * it through the original header processing routines at least up to the * fragment header, we do not need any further sanity checks here. */ if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) { iphdr_ptr->_nexth = ipr->nexth; } else { u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN; while (*ptr != IP6_NEXTH_FRAGMENT) { ptr += 8 * (1 + ptr[1]); } *ptr = ipr->nexth; } /* release the resources allocated for the fragment queue entry */ if (reassdatagrams == ipr) { /* it was the first in the list */ reassdatagrams = ipr->next; } else { /* it wasn't the first, so it must have a valid 'prev' */ LWIP_ASSERT("sanity check linked list", ipr_prev != NULL); ipr_prev->next = ipr->next; } memp_free(MEMP_IP6_REASSDATA, ipr); /* adjust the number of pbufs currently queued for reassembly. */ clen = pbuf_clen(p); LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen); ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen); /* Move pbuf back to IPv6 header. This should never fail. */ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) { LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); pbuf_free(p); return NULL; } /* Return the pbuf chain */ return p; } /* the datagram is not (yet?) reassembled completely */ return NULL; nullreturn: IP6_FRAG_STATS_INC(ip6_frag.drop); pbuf_free(p); return NULL; }
/** * Forwards an IPv6 packet. It finds an appropriate route for the * packet, decrements the HL value of the packet, and outputs * the packet on the appropriate interface. * * @param p the packet to forward (p->payload points to IP header) * @param iphdr the IPv6 header of the input packet * @param inp the netif on which this packet was received * @return -1 if packet is to be dropped, 0 if there's no route, 1 if forwarded */ static int ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) { enum { FWD_DROP = -1, FWD_PROXY = 0, FWD_FORWARDED = 1 }; struct netif *netif; /* do not forward link-local addresses */ if (ip6_addr_islinklocal(ip6_current_dest_addr())) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return FWD_DROP; } /* Find network interface where to forward this IP packet to. */ netif = ip6_route_fwd(ip6_current_dest_addr()); if (netif == NULL) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(ip6_current_dest_addr()), IP6_ADDR_BLOCK2(ip6_current_dest_addr()), IP6_ADDR_BLOCK3(ip6_current_dest_addr()), IP6_ADDR_BLOCK4(ip6_current_dest_addr()), IP6_ADDR_BLOCK5(ip6_current_dest_addr()), IP6_ADDR_BLOCK6(ip6_current_dest_addr()), IP6_ADDR_BLOCK7(ip6_current_dest_addr()), IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); #if LWIP_CONNECTION_PROXY return FWD_PROXY; #else /* !LWIP_CONNECTION_PROXY */ #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return FWD_DROP; #endif /* !LWIP_CONNECTION_PROXY */ } #if LWIP_CONNECTION_PROXY /* The packet is for a destination on a directly connected network. * Check for addresses in that address space that proxy wants to * remap (e.g. to host loopback address/es) and hand it off to * proxy */ if (netif != netif_default && proxy_ip6_divert_hook != NULL && (*proxy_ip6_divert_hook)(netif, ip6_current_dest_addr())) { return FWD_PROXY; } #endif /* LWIP_CONNECTION_PROXY */ /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); IP6_STATS_INC(ip6.rterr); IP6_STATS_INC(ip6.drop); return FWD_DROP; } /* decrement HL */ IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); /* send ICMP6 if HL == 0 */ if (IP6H_HOPLIM(iphdr) == 0) { #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_time_exceeded(p, ICMP6_TE_HL); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.drop); return FWD_DROP; } if (netif->mtu && (p->tot_len > netif->mtu)) { #if LWIP_ICMP6 /* Don't send ICMP messages in response to ICMP messages */ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { icmp6_packet_too_big(p, netif->mtu); } #endif /* LWIP_ICMP6 */ IP6_STATS_INC(ip6.drop); return FWD_DROP; } LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(ip6_current_dest_addr()), IP6_ADDR_BLOCK2(ip6_current_dest_addr()), IP6_ADDR_BLOCK3(ip6_current_dest_addr()), IP6_ADDR_BLOCK4(ip6_current_dest_addr()), IP6_ADDR_BLOCK5(ip6_current_dest_addr()), IP6_ADDR_BLOCK6(ip6_current_dest_addr()), IP6_ADDR_BLOCK7(ip6_current_dest_addr()), IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); /* transmit pbuf on chosen interface */ netif->output_ip6(netif, p, ip6_current_dest_addr()); IP6_STATS_INC(ip6.fw); IP6_STATS_INC(ip6.xmit); return FWD_FORWARDED; }