/* * XXX: ip6_route is too eager to return single/default netif, and * forwarding logic is different from the logic to select outgoing * interface for our own packets anyway. */ static struct netif * ip6_route_fwd(struct ip6_addr *dest) { struct netif *netif; s8_t i; /* Link-local addresses are not routable. */ if (ip6_addr_islinklocal(dest)) { return NULL; } /* See if the destination subnet matches a configured address. */ for(netif = netif_list; netif != NULL; netif = netif->next) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif; } } } /* Get the netif for a suitable router. */ i = nd6_select_router(dest, NULL); if (i >= 0) { if (default_router_list[i].neighbor_entry != NULL) { if (default_router_list[i].neighbor_entry->netif != NULL) { return default_router_list[i].neighbor_entry->netif; } } } /* no matching netif found */ return NULL; }
const ip_addr_t *LWIP::get_ipv6_addr(const struct netif *netif) { if (!netif_is_up(netif)) { return NULL; } for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && !ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { return netif_ip_addr6(netif, i); } } return NULL; }
/** * Finds the appropriate network interface for a given IPv6 address. It tries to select * a netif following a sequence of heuristics: * 1) if there is only 1 netif, return it * 2) if the destination is a link-local address, try to match the src address to a netif. * this is a tricky case because with multiple netifs, link-local addresses only have * meaning within a particular subnet/link. * 3) tries to match the destination subnet to a configured address * 4) tries to find a router * 5) tries to match the source address to the netif * 6) returns the default netif, if configured * * @param src the source IPv6 address, if known * @param dest the destination IPv6 address for which to find the route * @return the netif on which to send to reach dest */ struct netif * ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) { struct netif *netif; s8_t i; #ifdef LWIP_HOOK_IP6_ROUTE netif = LWIP_HOOK_IP6_ROUTE(src, dest); if (netif != NULL) { return netif; } #endif /* If single netif configuration, fast return. */ if ((netif_list != NULL) && (netif_list->next == NULL)) { if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list)) { return NULL; } return netif_list; } /* Special processing for link-local addresses. */ if (ip6_addr_islinklocal(dest)) { if (ip6_addr_isany(src)) { /* Use default netif, if Up. */ if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) { return NULL; } return netif_default; } /* Try to find the netif for the source address, checking that link is up. */ for (netif = netif_list; netif != NULL; netif = netif->next) { if (!netif_is_up(netif) || !netif_is_link_up(netif)) { continue; } for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { return netif; } } } /* netif not found, use default netif, if up */ if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) { return NULL; } return netif_default; } /* See if the destination subnet matches a configured address. */ for (netif = netif_list; netif != NULL; netif = netif->next) { if (!netif_is_up(netif) || !netif_is_link_up(netif)) { continue; } for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif; } } } /* Get the netif for a suitable router. */ i = nd6_select_router(dest, NULL); if (i >= 0) { if (default_router_list[i].neighbor_entry != NULL) { if (default_router_list[i].neighbor_entry->netif != NULL) { if (netif_is_up(default_router_list[i].neighbor_entry->netif) && netif_is_link_up(default_router_list[i].neighbor_entry->netif)) { return default_router_list[i].neighbor_entry->netif; } } } } /* try with the netif that matches the source address. */ if (!ip6_addr_isany(src)) { for (netif = netif_list; netif != NULL; netif = netif->next) { if (!netif_is_up(netif) || !netif_is_link_up(netif)) { continue; } for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { return netif; } } } } #if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF /* loopif is disabled, loopback traffic is passed through any netif */ if (ip6_addr_isloopback(dest)) { /* don't check for link on loopback traffic */ if (netif_is_up(netif_default)) { return netif_default; } /* default netif is not up, just use any netif for loopback traffic */ for (netif = netif_list; netif != NULL; netif = netif->next) { if (netif_is_up(netif)) { return netif; } } return NULL; } #endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */ /* no matching netif found, use default netif, if up */ if (!netif_is_up(netif_default) || !netif_is_link_up(netif_default)) { return NULL; } return netif_default; }
/** * 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; }
/** * Select the best IPv6 source address for a given destination * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior * is assumed. * * @param netif the netif on which to send a packet * @param dest the destination we are trying to reach * @return the most suitable source address to use, or NULL if no suitable * source address is found */ const ip_addr_t * ip6_select_source_address(struct netif *netif, const ip6_addr_t * dest) { const ip_addr_t *src = NULL; u8_t i; /* If dest is link-local, choose a link-local source. */ if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { return netif_ip_addr6(netif, i); } } } /* Choose a site-local with matching prefix. */ if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif_ip_addr6(netif, i); } } } /* Choose a unique-local with matching prefix. */ if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif_ip_addr6(netif, i); } } } /* Choose a global with best matching prefix. */ if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_isglobal(netif_ip6_addr(netif, i))) { if (src == NULL) { src = netif_ip_addr6(netif, i); } else { /* Replace src only if we find a prefix match. */ /* TODO find longest matching prefix. */ if ((!(ip6_addr_netcmp(ip_2_ip6(src), dest))) && ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { src = netif_ip_addr6(netif, i); } } } } if (src != NULL) { return src; } } /* Last resort: see if arbitrary prefix matches. */ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif_ip_addr6(netif, i); } } return NULL; }
/** * Finds the appropriate network interface for a given IPv6 address. It tries to select * a netif following a sequence of heuristics: * 1) if there is only 1 netif, return it * 2) if the destination is a link-local address, try to match the src address to a netif. * this is a tricky case because with multiple netifs, link-local addresses only have * meaning within a particular subnet/link. * 3) tries to match the destination subnet to a configured address * 4) tries to find a router * 5) tries to match the source address to the netif * 6) returns the default netif, if configured * * @param src the source IPv6 address, if known * @param dest the destination IPv6 address for which to find the route * @return the netif on which to send to reach dest */ struct netif * ip6_route(struct ip6_addr *src, struct ip6_addr *dest) { struct netif *netif; s8_t i; /* If single netif configuration, fast return. */ if ((netif_list != NULL) && (netif_list->next == NULL)) { return netif_list; } /* Special processing for link-local addresses. */ if (ip6_addr_islinklocal(dest)) { if (ip6_addr_isany(src)) { /* Use default netif. */ return netif_default; } /* Try to find the netif for the source address. */ for(netif = netif_list; netif != NULL; netif = netif->next) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { return netif; } } } /* netif not found, use default netif */ return netif_default; } /* See if the destination subnet matches a configured address. */ for(netif = netif_list; netif != NULL; netif = netif->next) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { return netif; } } } /* Get the netif for a suitable router. */ i = nd6_select_router(dest, NULL); if (i >= 0) { if (default_router_list[i].neighbor_entry != NULL) { if (default_router_list[i].neighbor_entry->netif != NULL) { return default_router_list[i].neighbor_entry->netif; } } } /* try with the netif that matches the source address. */ if (!ip6_addr_isany(src)) { for(netif = netif_list; netif != NULL; netif = netif->next) { for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { return netif; } } } } /* no matching netif found, use default netif */ return netif_default; }
/** * Initialize DHCP6 server. * * Join DHCP6 multicast groups. * Create and bind server pcb. * Prebuild fixed parts of reply. */ err_t dhcp6ds_init(struct netif *proxy_netif) { ip6_addr_t *pxaddr, *pxaddr_nonlocal; int i; err_t error; LWIP_ASSERT1(proxy_netif != NULL); LWIP_ASSERT1(proxy_netif->hwaddr_len == 6); /* ethernet */ pxaddr = netif_ip6_addr(proxy_netif, 0); /* link local */ /* * XXX: TODO: This is a leftover from testing with IPv6 mapped * loopback with a special IPv6->IPv4 mapping hack in pxudp.c */ /* advertise ourself as DNS resolver - will be proxied to host */ pxaddr_nonlocal = NULL; for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { if (ip6_addr_ispreferred(netif_ip6_addr_state(proxy_netif, i)) && !ip6_addr_islinklocal(netif_ip6_addr(proxy_netif, i))) { pxaddr_nonlocal = netif_ip6_addr(proxy_netif, i); break; } } LWIP_ASSERT1(pxaddr_nonlocal != NULL); /* must be configured on the netif */ error = mld6_joingroup(pxaddr, &all_dhcp_relays_and_servers); if (error != ERR_OK) { DPRINTF0(("%s: failed to join All_DHCP_Relay_Agents_and_Servers: %s\n", __func__, proxy_lwip_strerr(error))); goto err; } error = mld6_joingroup(pxaddr, &all_dhcp_servers); if (error != ERR_OK) { DPRINTF0(("%s: failed to join All_DHCP_Servers: %s\n", __func__, proxy_lwip_strerr(error))); goto err1; } dhcp6ds_pcb = udp_new_ip6(); if (dhcp6ds_pcb == NULL) { DPRINTF0(("%s: failed to allocate PCB\n", __func__)); error = ERR_MEM; goto err2; } udp_recv_ip6(dhcp6ds_pcb, dhcp6ds_recv, NULL); error = udp_bind_ip6(dhcp6ds_pcb, pxaddr, DHCP6_SERVER_PORT); if (error != ERR_OK) { DPRINTF0(("%s: failed to bind PCB\n", __func__)); goto err3; } #define OPT_SET(buf, off, c) do { \ u16_t _s = PP_HTONS(c); \ memcpy(&(buf)[off], &_s, sizeof(u16_t)); \ } while (0) #define SERVERID_SET(off, c) OPT_SET(dhcp6ds_serverid, (off), (c)) #define DNSSRV_SET(off, c) OPT_SET(dhcp6ds_dns, (off), (c)) SERVERID_SET(0, DHCP6_OPTION_SERVERID); SERVERID_SET(2, DUID_LL_LEN); SERVERID_SET(4, DHCP6_DUID_LL); SERVERID_SET(6, ARES_HRD_ETHERNET); memcpy(&dhcp6ds_serverid[8], proxy_netif->hwaddr, 6); DNSSRV_SET(0, DHCP6_OPTION_DNS_SERVERS); DNSSRV_SET(2, 16); /* one IPv6 address */ /* * XXX: TODO: This is a leftover from testing with IPv6 mapped * loopback with a special IPv6->IPv4 mapping hack in pxudp.c */ memcpy(&dhcp6ds_dns[4], pxaddr_nonlocal, sizeof(ip6_addr_t)); #undef SERVERID_SET #undef DNSSRV_SET return ERR_OK; err3: udp_remove(dhcp6ds_pcb); dhcp6ds_pcb = NULL; err2: mld6_leavegroup(pxaddr, &all_dhcp_servers); err1: mld6_leavegroup(pxaddr, &all_dhcp_relays_and_servers); err: return error; }
/** * 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; }
/********************************************************************************************************* ** 函数名称: __netIfShow ** 功能描述: 显示指定的网络接口信息 (ip v4) ** 输 入 : pcIfName 网络接口名 ** netifShow 网络接口结构 ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static VOID __netIfShow (CPCHAR pcIfName, const struct netif *netifShow) { struct netif *netif; CHAR cSpeed[32]; ip_addr_t ipaddrBroadcast; INT i; if ((pcIfName == LW_NULL) && (netifShow == LW_NULL)) { return; } if (netifShow) { netif = (struct netif *)netifShow; } else { netif = netif_find((PCHAR)pcIfName); } if (netif == LW_NULL) { return; } /* * 打印网口基本信息 */ printf("%c%c%d ", netif->name[0], netif->name[1], netif->num); printf("enable: %s ", (netif_is_up(netif) > 0) ? "true" : "false"); printf("linkup: %s ", (netif_is_link_up(netif) > 0) ? "true" : "false"); printf("MTU: %d ", netif->mtu); printf("multicast: %s\n", (netif->flags & NETIF_FLAG_IGMP) ? "true" : "false"); /* * 打印路由信息 */ if (netif == netif_default) { /* route interface */ printf(" metric: 1 "); } else { printf(" metric: 0 "); } /* * 打印网口硬件地址信息 */ if (netif->flags & NETIF_FLAG_ETHARP) { printf("type: Ethernet-Cap HWaddr: "); /* 以太网络 */ for (i = 0; i < netif->hwaddr_len - 1; i++) { printf("%02X:", netif->hwaddr[i]); } printf("%02X\n", netif->hwaddr[netif->hwaddr_len - 1]); } else if (netif->flags & NETIF_FLAG_POINTTOPOINT) { printf("type: WAN(PPP/SLIP)\n"); /* 点对点网络接口 */ } else { printf("type: General\n"); /* 通用网络接口 */ } __netIfSpeed(netif, cSpeed, sizeof(cSpeed)); #if LWIP_DHCP printf(" DHCP: %s(%s) speed: %s\n", (netif->flags2 & NETIF_FLAG2_DHCP) ? "Enable" : "Disable", (netif->dhcp) ? "On" : "Off", cSpeed); #else printf(" speed: %s\n", cSpeed); /* 打印链接速度 */ #endif /* LWIP_DHCP */ /* * 打印网口协议地址信息 */ printf(" inet addr: %d.%d.%d.%d ", ip4_addr1(&netif->ip_addr), ip4_addr2(&netif->ip_addr), ip4_addr3(&netif->ip_addr), ip4_addr4(&netif->ip_addr)); printf("netmask: %d.%d.%d.%d\n", ip4_addr1(&netif->netmask), ip4_addr2(&netif->netmask), ip4_addr3(&netif->netmask), ip4_addr4(&netif->netmask)); if (netif->flags & NETIF_FLAG_POINTTOPOINT) { printf(" P-to-P: %d.%d.%d.%d ", ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), ip4_addr3(&netif->gw), ip4_addr4(&netif->gw)); } else { printf(" gateway: %d.%d.%d.%d ", ip4_addr1(&netif->gw), ip4_addr2(&netif->gw), ip4_addr3(&netif->gw), ip4_addr4(&netif->gw)); } if (netif->flags & NETIF_FLAG_BROADCAST) { /* 打印广播地址信息 */ ipaddrBroadcast.addr = (netif->ip_addr.addr | (~netif->netmask.addr)); printf("broadcast: %d.%d.%d.%d\n", ip4_addr1(&ipaddrBroadcast), ip4_addr2(&ipaddrBroadcast), ip4_addr3(&ipaddrBroadcast), ip4_addr4(&ipaddrBroadcast)); } else { printf("broadcast: Non\n"); } /* * 打印 ipv6 信息 */ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { PCHAR pcAddrStat; PCHAR pcAddrType; CHAR cBuffer[64]; if (ip6_addr_istentative(netif->ip6_addr_state[i])) { pcAddrStat = "tentative"; } else if (ip6_addr_isvalid(netif->ip6_addr_state[i])) { pcAddrStat = "valid"; } else if (ip6_addr_ispreferred(netif->ip6_addr_state[i])) { pcAddrStat = "preferred"; } else { continue; } if (ip6_addr_isglobal(&netif->ip6_addr[i])) { pcAddrType = "global"; } else if (ip6_addr_islinklocal(&netif->ip6_addr[i])) { pcAddrType = "link"; } else if (ip6_addr_issitelocal(&netif->ip6_addr[i])) { pcAddrType = "site"; } else if (ip6_addr_isuniquelocal(&netif->ip6_addr[i])) { pcAddrType = "uniquelocal"; } else if (ip6_addr_isloopback(&netif->ip6_addr[i])) { pcAddrType = "loopback"; } else { pcAddrType = "unknown"; } printf(" inet6 addr: %s Scope:%s <%s>\n", ip6addr_ntoa_r(&netif->ip6_addr[i], cBuffer, sizeof(cBuffer)), pcAddrType, pcAddrStat); } /* * 打印网口收发数据信息 */ printf(" RX ucast packets:%u nucast packets:%u dropped:%u\n", netif->ifinucastpkts, netif->ifinnucastpkts, netif->ifindiscards); printf(" TX ucast packets:%u nucast packets:%u dropped:%u\n", netif->ifoutucastpkts, netif->ifoutnucastpkts, netif->ifoutdiscards); printf(" RX bytes:%u TX bytes:%u\n", netif->ifinoctets, netif->ifoutoctets); printf("\n"); }