/** This function allows for the easy addition of a new IPv6 address to an interface. * It takes care of finding an empty slot and then sets the address tentative * (to make sure that all the subsequent processing happens). * * @param netif netif to add the address on * @param ip6addr address to add * @param chosen_idx if != NULL, the chosen IPv6 address index will be stored here */ s8_t netif_add_ip6_address(struct netif *netif, ip6_addr_t *ip6addr, s8_t *chosen_idx) { s8_t i; i = netif_get_ip6_addr_match(netif, ip6addr); if (i >= 0) { /* Address already added */ if (chosen_idx != NULL) { *chosen_idx = i; } return ERR_OK; } /* Find a free slot -- musn't be the first one (reserved for link local) */ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (!ip6_addr_isvalid(netif->ip6_addr_state[i])) { ip6_addr_copy(netif->ip6_addr[i], *ip6addr); netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE); if (chosen_idx != NULL) { *chosen_idx = i; } return ERR_OK; } } if (chosen_idx != NULL) { *chosen_idx = -1; } return ERR_VAL; }
/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint * before calling ip6_output_if. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IPv6 header and p->payload points to that IPv6 header) * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an * IP address of the netif is selected and used as source address. * if src == NULL, IP6_ADDR_ANY is used as source) * @param dest the destination IPv6 address to send the packet to * @param hl the Hop Limit value to be set in the IPv6 header * @param tc the Traffic Class value to be set in the IPv6 header * @param nexth the Next Header to be set in the IPv6 header * @param addr_hint address hint pointer set to netif->addr_hint before * calling ip_output_if() * * @return ERR_RTE if no route is found * see ip_output_if() for more return values */ err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) { struct netif *netif; struct ip6_hdr *ip6hdr; ip6_addr_t src_addr, dest_addr; err_t err; /* pbufs passed to IP must have a ref-count of 1 as their payload pointer gets altered as the packet is passed down the stack */ LWIP_ASSERT("p->ref == 1", p->ref == 1); if (dest != IP_HDRINCL) { netif = ip6_route(src, dest); } else { /* IP header included in p, read addresses. */ ip6hdr = (struct ip6_hdr *)p->payload; ip6_addr_copy(src_addr, ip6hdr->src); ip6_addr_copy(dest_addr, ip6hdr->dest); netif = ip6_route(&src_addr, &dest_addr); } if (netif == NULL) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(dest), IP6_ADDR_BLOCK2(dest), IP6_ADDR_BLOCK3(dest), IP6_ADDR_BLOCK4(dest), IP6_ADDR_BLOCK5(dest), IP6_ADDR_BLOCK6(dest), IP6_ADDR_BLOCK7(dest), IP6_ADDR_BLOCK8(dest))); IP6_STATS_INC(ip6.rterr); return ERR_RTE; } NETIF_SET_HWADDRHINT(netif, addr_hint); err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); NETIF_SET_HWADDRHINT(netif, NULL); return err; }
/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint * before calling ip6_output_if. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IPv6 header and p->payload points to that IPv6 header) * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an * IP address of the netif is selected and used as source address. * if src == NULL, IP6_ADDR_ANY is used as source) * @param dest the destination IPv6 address to send the packet to * @param hl the Hop Limit value to be set in the IPv6 header * @param tc the Traffic Class value to be set in the IPv6 header * @param nexth the Next Header to be set in the IPv6 header * @param addr_hint address hint pointer set to netif->addr_hint before * calling ip_output_if() * * @return ERR_RTE if no route is found * see ip_output_if() for more return values */ err_t ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) { struct netif *netif; struct ip6_hdr *ip6hdr; ip6_addr_t src_addr, dest_addr; err_t err; LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); if (dest != IP_HDRINCL) { netif = ip6_route(src, dest); } else { /* IP header included in p, read addresses. */ ip6hdr = (struct ip6_hdr *)p->payload; ip6_addr_copy(src_addr, ip6hdr->src); ip6_addr_copy(dest_addr, ip6hdr->dest); netif = ip6_route(&src_addr, &dest_addr); } if (netif == NULL) { LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", IP6_ADDR_BLOCK1(dest), IP6_ADDR_BLOCK2(dest), IP6_ADDR_BLOCK3(dest), IP6_ADDR_BLOCK4(dest), IP6_ADDR_BLOCK5(dest), IP6_ADDR_BLOCK6(dest), IP6_ADDR_BLOCK7(dest), IP6_ADDR_BLOCK8(dest))); IP6_STATS_INC(ip6.rterr); return ERR_RTE; } NETIF_SET_HWADDRHINT(netif, addr_hint); err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); NETIF_SET_HWADDRHINT(netif, NULL); return err; }
/** * Same as ip6_output_if() but 'src' address is not replaced by netif address * when it is 'any'. */ err_t ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, u8_t hl, u8_t tc, u8_t nexth, struct netif *netif) { struct ip6_hdr *ip6hdr; ip6_addr_t dest_addr; LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); /* Should the IPv6 header be generated or is it already included in p? */ if (dest != IP_HDRINCL) { /* generate IPv6 header */ if (pbuf_header(p, IP6_HLEN)) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); IP6_STATS_INC(ip6.err); return ERR_BUF; } ip6hdr = (struct ip6_hdr *)p->payload; LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", (p->len >= sizeof(struct ip6_hdr))); IP6H_HOPLIM_SET(ip6hdr, hl); IP6H_NEXTH_SET(ip6hdr, nexth); /* dest cannot be NULL here */ ip6_addr_copy(ip6hdr->dest, *dest); IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); if (src == NULL) { src = IP6_ADDR_ANY6; } /* src cannot be NULL here */ ip6_addr_copy(ip6hdr->src, *src); } else { /* IP header already included in p */ ip6hdr = (struct ip6_hdr *)p->payload; ip6_addr_copy(dest_addr, ip6hdr->dest); dest = &dest_addr; } IP6_STATS_INC(ip6.xmit); LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); ip6_debug_print(p); #if ENABLE_LOOPBACK { int i; #if !LWIP_HAVE_LOOPIF if (ip6_addr_isloopback(dest)) { return netif_loop_output(netif, p); } #endif /* !LWIP_HAVE_LOOPIF */ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) { /* Packet to self, enqueue it for loopback */ LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n")); return netif_loop_output(netif, p); } } } #endif /* ENABLE_LOOPBACK */ #if LWIP_IPV6_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { return ip6_frag(p, netif, dest); } #endif /* LWIP_IPV6_FRAG */ LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n")); return netif->output_ip6(netif, p, dest); }
/** * Sends an IPv6 packet on a network interface. This function constructs * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is * used as source (usually during network startup). If the source IPv6 address it * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network * interface is filled in as source address. If the destination IPv6 address is * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points * to it instead of the data. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IPv6 header and p->payload points to that IPv6 header) * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an * IP address of the netif is selected and used as source address. * if src == NULL, IP6_ADDR_ANY is used as source) * @param dest the destination IPv6 address to send the packet to * @param hl the Hop Limit value to be set in the IPv6 header * @param tc the Traffic Class value to be set in the IPv6 header * @param nexth the Next Header to be set in the IPv6 header * @param netif the netif on which to send this packet * @return ERR_OK if the packet was sent OK * ERR_BUF if p doesn't have enough space for IPv6/LINK headers * returns errors returned by netif->output */ err_t ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, u8_t hl, u8_t tc, u8_t nexth, struct netif *netif) { struct ip6_hdr *ip6hdr; ip6_addr_t dest_addr; /* pbufs passed to IP must have a ref-count of 1 as their payload pointer gets altered as the packet is passed down the stack */ LWIP_ASSERT("p->ref == 1", p->ref == 1); /* Should the IPv6 header be generated or is it already included in p? */ if (dest != IP_HDRINCL) { /* generate IPv6 header */ if (pbuf_header(p, IP6_HLEN)) { LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); IP6_STATS_INC(ip6.err); return ERR_BUF; } ip6hdr = (struct ip6_hdr *)p->payload; LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", (p->len >= sizeof(struct ip6_hdr))); IP6H_HOPLIM_SET(ip6hdr, hl); IP6H_NEXTH_SET(ip6hdr, nexth); /* dest cannot be NULL here */ ip6_addr_copy(ip6hdr->dest, *dest); IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); if (src == NULL) { src = IP6_ADDR_ANY; } else if (ip6_addr_isany(src)) { src = ip6_select_source_address(netif, dest); if ((src == NULL) || ip6_addr_isany(src)) { /* No appropriate source address was found for this packet. */ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); IP6_STATS_INC(ip6.rterr); return ERR_RTE; } } /* src cannot be NULL here */ ip6_addr_copy(ip6hdr->src, *src); } else { /* IP header already included in p */ ip6hdr = (struct ip6_hdr *)p->payload; ip6_addr_copy(dest_addr, ip6hdr->dest); dest = &dest_addr; } IP6_STATS_INC(ip6.xmit); LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); ip6_debug_print(p); #if ENABLE_LOOPBACK /* TODO implement loopback for v6 if (ip6_addr_cmp(dest, netif_ip6_addr(0))) { return netif_loop_output(netif, p, dest); }*/ #endif /* ENABLE_LOOPBACK */ #if LWIP_IPV6_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { return ip6_frag(p, netif, dest); } #endif /* LWIP_IPV6_FRAG */ LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()")); return netif->output_ip6(netif, p, dest); }
/** * 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 IP_ACCEPT_LINK_LAYER_ADDRESSING 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 %"U16_F"\n", IP6H_V(ip6hdr))); pbuf_free(p); IP6_STATS_INC(ip6.err); IP6_STATS_INC(ip6.drop); return ERR_OK; } /* 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 */ ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); /* current header pointer. */ ip_data.current_ip6_header = ip6hdr; /* 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())) { /* Accept all solicited node packets when MLD is not enabled * (for Neighbor discovery). */ netif = inp; } #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 (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->name[0], netif->name[1])); } /* "::" 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 = inp; /* 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, -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, -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, -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 & IP6_FRAG_OFFSET_MASK) == 0) && ((frag_hdr->_fragment_offset & 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, -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(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, -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, -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, -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_ip6_header = NULL; ip_data.current_ip_header_tot_len = 0; ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); return ERR_OK; }
/** * Send an ICMPv6 packet in response to an incoming packet. * * @param p the input packet for which the response should be sent, * p->payload pointing to the IPv6 header * @param code Code of the ICMPv6 header * @param data Additional 32-bit parameter in the ICMPv6 header * @param type Type of the ICMPv6 header */ static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) { struct pbuf *q; struct icmp6_hdr *icmp6hdr; ip6_addr_t *reply_src, *reply_dest; ip6_addr_t reply_src_local, reply_dest_local; struct ip6_hdr *ip6hdr; struct interface *netif; /* ICMPv6 header + IPv6 header + data */ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, PBUF_RAM); if (q == NULL) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); ICMP6_STATS_INC(icmp6.memerr); return; } LWIP_ASSERT("check that first pbuf can hold icmp 6message", (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); icmp6hdr = (struct icmp6_hdr *)q->payload; icmp6hdr->type = type; icmp6hdr->code = code; icmp6hdr->data = data; /* copy fields from original packet */ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, IP6_HLEN + LWIP_ICMP6_DATASIZE); /* Get the destination address and netif for this ICMP message. */ if ((ip_current_netif() == NULL) || ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { /* Special case, as ip6_current_xxx is either NULL, or points * to a different packet than the one that expired. * We must use the addresses that are stored in the expired packet. */ ip6hdr = (struct ip6_hdr *)p->payload; /* copy from packed address to aligned address */ ip6_addr_copy(reply_dest_local, ip6hdr->src); ip6_addr_copy(reply_src_local, ip6hdr->dest); reply_dest = &reply_dest_local; reply_src = &reply_src_local; netif = ip6_route(reply_src, reply_dest); if (netif == NULL) { /* drop */ pbuf_free(q); ICMP6_STATS_INC(icmp6.rterr); return; } } else { netif = ip_current_netif(); reply_dest = ip6_current_src_addr(); /* Select an address to use as source. */ reply_src = ip6_select_source_address(netif, reply_dest); if (reply_src == NULL) { /* drop */ pbuf_free(q); ICMP6_STATS_INC(icmp6.rterr); return; } } /* calculate checksum */ icmp6hdr->chksum = 0; icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, reply_src, reply_dest); ICMP6_STATS_INC(icmp6.xmit); ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); pbuf_free(q); }
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); }
/* lwIP UDP receive callback function */ static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) { struct snmp_msg_pstat *msg_ps; u8_t req_idx; err_t err_ret; u16_t payload_len = p->tot_len; u16_t payload_ofs = 0; u16_t varbind_ofs = 0; /* suppress unused argument warning */ LWIP_UNUSED_ARG(arg); /* traverse input message process list, look for SNMP_MSG_EMPTY */ msg_ps = &msg_input_list[0]; req_idx = 0; while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY)) { req_idx++; msg_ps++; } if (req_idx == SNMP_CONCURRENT_REQUESTS) { /* exceeding number of concurrent requests */ pbuf_free(p); return; } /* accepting request */ snmp_inc_snmpinpkts(); /* record used 'protocol control block' */ msg_ps->pcb = pcb; /* source address (network order) */ #if LWIP_IPV6 ip6_addr_copy(msg_ps->sip.ip6, *(ip6_addr_t*)addr); #else msg_ps->sip.ip4 = *addr; #endif /* source port (host order (lwIP oddity)) */ msg_ps->sp = port; /* check total length, version, community, pdu type */ err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); /* Only accept requests and requests without error (be robust) */ /* Reject response and trap headers or error requests as input! */ if ((err_ret != ERR_OK) || ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) && (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) && (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) || ((msg_ps->error_status != SNMP_ES_NOERROR) || (msg_ps->error_index != 0)) ) { /* header check failed drop request silently, do not return error! */ pbuf_free(p); LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); return; } LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); /* Builds a list of variable bindings. Copy the varbinds from the pbuf chain to glue them when these are divided over two or more pbuf's. */ err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); /* we've decoded the incoming message, release input msg now */ pbuf_free(p); if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0)) { /* varbind-list decode failed, or varbind list empty. drop request silently, do not return error! (errors are only returned for a specific varbind failure) */ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); return; } msg_ps->error_status = SNMP_ES_NOERROR; msg_ps->error_index = 0; /* find object for each variable binding */ msg_ps->state = SNMP_MSG_SEARCH_OBJ; /* first variable binding from list to inspect */ msg_ps->vb_idx = 0; LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); /* handle input event and as much objects as possible in one go */ snmp_msg_event(req_idx); }