diff --git a/UPGRADING b/UPGRADING index 3a984c92..3188efc6 100644 --- a/UPGRADING +++ b/UPGRADING @@ -8,6 +8,19 @@ with newer versions. * [Enter new changes just after this line - do not remove this line] + ++ Application changes: + + * The IPv6 implementation now supports address scopes. All addresses that have a scope according + to the default policy (link-local unicast addresses, interface-local and link-local multicast + addresses) should now have a zone set on them before being passed to the core API, although + lwIP will currently attempt to select a zone on the caller's behalf when necessary. + Applications that directly assign IPv6 addresses to interfaces (which is NOT recommended) + must now ensure that link-local addresses carry the netif's zone. See the new ip6_zone.h header + file for more information and relevant macros. For now it is still possible to turn off scopes + support through the new LWIP_IPV6_SCOPES option. When upgrading an implementation that uses the + core API directly, it is highly recommended to enable LWIP_IPV6_SCOPES_DEBUG at least for + a while, to ensure e.g. proper address initialization. + (2.0.1) ++ Application changes: diff --git a/src/api/netdb.c b/src/api/netdb.c index f551412c..e2353d82 100644 --- a/src/api/netdb.c +++ b/src/api/netdb.c @@ -376,6 +376,7 @@ lwip_getaddrinfo(const char *nodename, const char *servname, sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_port = lwip_htons((u16_t)port_nr); + sa6->sin6_scope_id = ip6_addr_zone(&addr); ai->ai_family = AF_INET6; #endif /* LWIP_IPV6 */ } else { diff --git a/src/api/sockets.c b/src/api/sockets.c index d4ddb76a..9368e5b4 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -96,9 +96,12 @@ (sin6)->sin6_port = lwip_htons((port)); \ (sin6)->sin6_flowinfo = 0; \ inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \ - (sin6)->sin6_scope_id = 0; }while(0) + (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0) #define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \ inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \ + if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \ + ip6_addr_set_zone(ip_2_ip6(ipaddr), (sin6)->sin6_scope_id); \ + } \ (port) = lwip_ntohs((sin6)->sin6_port); }while(0) #endif /* LWIP_IPV6 */ diff --git a/src/core/ipv6/ethip6.c b/src/core/ipv6/ethip6.c index 8f9a91b5..904005c4 100644 --- a/src/core/ipv6/ethip6.c +++ b/src/core/ipv6/ethip6.c @@ -82,6 +82,9 @@ ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) const u8_t *hwaddr; err_t result; + /* The destination IP address must be properly zoned from here on down. */ + IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif); + /* multicast destination IP address? */ if (ip6_addr_ismulticast(ip6addr)) { /* Hash IP multicast address to MAC address.*/ diff --git a/src/core/ipv6/icmp6.c b/src/core/ipv6/icmp6.c index 323b69a2..c4fd3771 100644 --- a/src/core/ipv6/icmp6.c +++ b/src/core/ipv6/icmp6.c @@ -65,7 +65,8 @@ #endif /* Forward declarations */ -static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, + u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr); /** @@ -209,6 +210,9 @@ icmp6_input(struct pbuf *p, struct netif *inp) /** * Send an icmpv6 'destination unreachable' packet. * + * This function must be used only in direct response to a packet that is being + * received right now. Otherwise, address zones would be lost. + * * @param p the input packet for which the 'unreachable' should be sent, * p->payload pointing to the IPv6 header * @param c ICMPv6 code for the unreachable type @@ -216,12 +220,15 @@ icmp6_input(struct pbuf *p, struct netif *inp) void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) { - icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR, NULL, NULL); } /** * Send an icmpv6 'packet too big' packet. * + * This function must be used only in direct response to a packet that is being + * received right now. Otherwise, address zones would be lost. + * * @param p the input packet for which the 'packet too big' should be sent, * p->payload pointing to the IPv6 header * @param mtu the maximum mtu that we can accept @@ -229,25 +236,53 @@ icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) void icmp6_packet_too_big(struct pbuf *p, u32_t mtu) { - icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB, NULL, NULL); } /** * Send an icmpv6 'time exceeded' packet. * - * @param p the input packet for which the 'unreachable' should be sent, + * This function must be used only in direct response to a packet that is being + * received right now. Otherwise, address zones would be lost. + * + * @param p the input packet for which the 'time exceeded' should be sent, * p->payload pointing to the IPv6 header * @param c ICMPv6 code for the time exceeded type */ void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) { - icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE, NULL, NULL); +} + +/** + * Send an icmpv6 'time exceeded' packet, with explicit source and destination + * addresses. + * + * This function may be used to send a response sometime after receiving the + * packet for which this response is meant. The provided source and destination + * addresses are used primarily to retain their zone information. + * + * @param p the input packet for which the 'time exceeded' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + * @param src_addr source address of the original packet, with zone information + * @param dest_addr destination address of the original packet, with zone + * information + */ +void +icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, + const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr); } /** * Send an icmpv6 'parameter problem' packet. * + * This function must be used only in direct response to a packet that is being + * received right now. Otherwise, address zones would be lost. + * * @param p the input packet for which the 'param problem' should be sent, * p->payload pointing to the IP header * @param c ICMPv6 code for the param problem type @@ -256,27 +291,38 @@ icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) { - icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP, NULL, NULL); } /** * Send an ICMPv6 packet in response to an incoming packet. * + * If this packet is sent as a direct response of an incoming packet being + * processed right now, the information of that incoming packet will be used to + * determine where to reply. In that case, the src_addr and dest_addr parameters + * must be set to NULL. If this packet is NOT sent as a direct response to an + * incoming packet, but rather sometime later (e.g. for a fragment reassembly + * timeout), the caller must provide the zoned source and destination addresses + * from the original packet with the src_addr and dest_addr parameters. The + * reason for this approach is that while the addresses themselves are part of + * the original packet, their zone information is not, thus possibly resulting + * in a link-local response being sent over the wrong link. + * * @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 + * @param src_addr original source address or NULL (see description) + * @param dest_addr original destination address or NULL (see description) */ static void -icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type, + const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr) { struct pbuf *q; struct icmp6_hdr *icmp6hdr; - const ip6_addr_t *reply_src; - ip6_addr_t *reply_dest; - ip6_addr_t reply_src_local, reply_dest_local; - struct ip6_hdr *ip6hdr; + const ip6_addr_t *reply_src, *reply_dest; struct netif *netif; /* ICMPv6 header + IPv6 header + data */ @@ -300,17 +346,15 @@ icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) 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))) { + if (src_addr != NULL) { + LWIP_ASSERT("must provide both source and destination", dest_addr != NULL); /* 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; + * to a different packet than the one that expired. */ + IP6_ADDR_ZONECHECK(src_addr); + IP6_ADDR_ZONECHECK(dest_addr); + /* Swap source and destination for the reply. */ + reply_dest = src_addr; + reply_src = dest_addr; netif = ip6_route(reply_src, reply_dest); if (netif == NULL) { /* drop */ @@ -321,6 +365,7 @@ icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) } else { netif = ip_current_netif(); + LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL); reply_dest = ip6_current_src_addr(); /* Select an address to use as source. */ diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c index 01a40e63..f871b780 100644 --- a/src/core/ipv6/ip6.c +++ b/src/core/ipv6/ip6.c @@ -64,13 +64,15 @@ * 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-announced route - * 5) tries to match the source address to the netif - * 6) returns the default netif, if configured + * 2) if the destination is a zoned address, match its zone to a netif + * 3) if the either the source or destination address is a scoped address, + * match the source address's zone (if set) or address (if not) to a netif + * 4) tries to match the destination subnet to a configured address + * 5) tries to find a router-announced route + * 6) tries to match the (unscoped) source address to the netif + * 7) returns the default netif, if configured + * + * Note that each of the two given addresses may or may not be properly zoned. * * @param src the source IPv6 address, if known * @param dest the destination IPv6 address for which to find the route @@ -84,45 +86,87 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) /* 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)) { + if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list) || + (ip6_addr_has_zone(dest) && !ip6_addr_test_zone(dest, 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_default == NULL || !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. */ +#if LWIP_IPV6_SCOPES + /* Special processing for zoned destination addresses. This includes link- + * local unicast addresses and interface/link-local multicast addresses. Use + * the zone to find a matching netif. If the address is not zoned, then there + * is technically no "wrong" netif to choose, and we leave routing to other + * rules; in most cases this should be the scoped-source rule below. */ + if (ip6_addr_has_zone(dest)) { + IP6_ADDR_ZONECHECK(dest); + /* Find a netif based on the zone. For custom mappings, one zone may map + * to multiple netifs, so find one that can actually send a packet. */ for (netif = netif_list; netif != NULL; netif = netif->next) { - if (!netif_is_up(netif) || !netif_is_link_up(netif)) { - continue; + if (ip6_addr_test_zone(dest, netif) && + netif_is_up(netif) && netif_is_link_up(netif)) { + return netif; } - 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))) { + } + /* No matching netif found. Do no try to route to a different netif, + * as that would be a zone violation, resulting in any packets sent to + * that netif being dropped on output. */ + return NULL; + } +#endif /* LWIP_IPV6_SCOPES */ + + /* Special processing for scoped source and destination addresses. If we get + * here, the destination address does not have a zone, so either way we need + * to look at the source address, which may or may not have a zone. If it + * does, the zone is restrictive: there is (typically) only one matching + * netif for it, and we should avoid routing to any other netif as that would + * result in guaranteed zone violations. For scoped source addresses that do + * not have a zone, use (only) a netif that has that source address locally + * assigned. This case also applies to the loopback source address, which has + * an implied link-local scope. If only the destination address is scoped + * (but, again, not zoned), we still want to use only the source address to + * determine its zone because that's most likely what the user/application + * wants, regardless of whether the source address is scoped. Finally, some + * of this story also applies if scoping is disabled altogether. */ +#if LWIP_IPV6_SCOPES + if (ip6_addr_has_scope(dest, IP6_UNKNOWN) || + ip6_addr_has_scope(src, IP6_UNICAST) || +#else /* LWIP_IPV6_SCOPES */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_iflocal(dest) || + ip6_addr_ismulticast_linklocal(dest) || ip6_addr_islinklocal(src) || +#endif /* LWIP_IPV6_SCOPES */ + ip6_addr_isloopback(src)) { + if (ip6_addr_has_zone(src)) { + /* Find a netif matching the source zone (relatively cheap). */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (netif_is_up(netif) && netif_is_link_up(netif) && + ip6_addr_test_zone(src, netif)) { return netif; } } + } else { + /* Find a netif matching the source address (relatively expensive). */ + 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_zoneless(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } } - - /* netif not found, use default netif, if up */ - if (netif_default == NULL || !netif_is_up(netif_default) || - !netif_is_link_up(netif_default)) { - return NULL; - } - return netif_default; + /* Again, do not use any other netif in this case, as that could result in + * zone boundary violations. */ + return NULL; } - /* we come here for non-link-local addresses */ + /* We come here only if neither source nor destination is scoped. */ + IP6_ADDR_ZONECHECK(src); + #ifdef LWIP_HOOK_IP6_ROUTE netif = LWIP_HOOK_IP6_ROUTE(src, dest); if (netif != NULL) { @@ -156,7 +200,8 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) return netif; } - /* try with the netif that matches the source address. */ + /* Try with the netif that matches the source address. Given the earlier rule + * for scoped source addresses, this applies to unscoped addresses only. */ if (!ip6_addr_isany(src)) { for (netif = netif_list; netif != NULL; netif = netif->next) { if (!netif_is_up(netif) || !netif_is_link_up(netif)) { @@ -215,7 +260,8 @@ ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest) * proper implementation of Rule 8 would obviate the need to implement Rule 6. * * @param netif the netif on which to send a packet - * @param dest the destination we are trying to reach + * @param dest the destination we are trying to reach (possibly not properly + * zoned) * @return the most suitable source address to use, or NULL if no suitable * source address is found */ @@ -270,7 +316,9 @@ ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest) } cand_pref = ip6_addr_ispreferred(netif_ip6_addr_state(netif, i)); /* @todo compute the actual common bits, for longest matching prefix. */ - cand_bits = ip6_addr_netcmp(cand_addr, dest); /* just 1 or 0 for now */ + /* We cannot count on the destination address having a proper zone + * assignment, so do not compare zones in this case. */ + cand_bits = ip6_addr_netcmp_zoneless(cand_addr, dest); /* just 1 or 0 for now */ if (cand_bits && ip6_addr_nethostcmp(cand_addr, dest)) { return netif_ip_addr6(netif, i); /* Rule 1 */ } @@ -336,6 +384,20 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) IP6_STATS_INC(ip6.drop); return; } +#if LWIP_IPV6_SCOPES + /* Do not forward packets with a zoned (e.g., link-local) source address + * outside of their zone. We determined the zone a bit earlier, so we know + * that the address is properly zoned here, so we can safely use has_zone. + * Also skip packets with a loopback source address (link-local implied). */ + if ((ip6_addr_has_zone(ip6_current_src_addr()) && + !ip6_addr_test_zone(ip6_current_src_addr(), netif)) || + ip6_addr_isloopback(ip6_current_src_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding packet beyond its source address zone.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } +#endif /* LWIP_IPV6_SCOPES */ /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { @@ -459,17 +521,23 @@ ip6_input(struct pbuf *p, struct netif *inp) 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); + ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_dest, ip6hdr->dest); + ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_src, ip6hdr->src); - /* Don't accept virtual IPv6 mapped IPv4 addresses */ + /* Don't accept virtual IPv6 mapped IPv4 addresses. + * Don't accept multicast source addresses. */ if (ip6_addr_isipv6mappedipv4(ip_2_ip6(&ip_data.current_iphdr_dest)) || - ip6_addr_isipv6mappedipv4(ip_2_ip6(&ip_data.current_iphdr_src)) ) { + ip6_addr_isipv6mappedipv4(ip_2_ip6(&ip_data.current_iphdr_src)) || + ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_src))) { IP6_STATS_INC(ip6.err); IP6_STATS_INC(ip6.drop); return ERR_OK; } + /* Set the appropriate zone identifier on the addresses. */ + ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, inp); + ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNICAST, inp); + /* current header pointer. */ ip_data.current_ip6_header = ip6hdr; @@ -517,29 +585,46 @@ ip6_input(struct pbuf *p, struct netif *inp) /* interface is up? */ if (netif_is_up(netif)) { /* unicast to this interface address? address configured? */ + /* If custom scopes are used, the destination zone will be tested as + * part of the local-address comparison, but we need to test the source + * scope as well (e.g., is this interface on the same link?). */ 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))) { + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i)) +#if IPV6_CUSTOM_SCOPES + && (!ip6_addr_has_zone(ip6_current_src_addr()) || + ip6_addr_test_zone(ip6_current_src_addr(), netif)) +#endif /* IPV6_CUSTOM_SCOPES */ + ) { /* exit outer loop */ goto netif_found; } } } if (first) { - if (ip6_addr_islinklocal(ip6_current_dest_addr()) -#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF - || ip6_addr_isloopback(ip6_current_dest_addr()) -#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */ - ) { - /* Do not match link-local addresses to other netifs. The loopback - * address is to be considered link-local and packets to it should be - * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This - * requirement cannot be implemented in the case that loopback - * traffic is sent across a non-loopback interface, however. - */ +#if !IPV6_CUSTOM_SCOPES + /* Shortcut: stop looking for other interfaces if either the source or + * the destination has a scope constrained to this interface. Custom + * scopes may break the 1:1 link/interface mapping, however. */ + if (ip6_addr_islinklocal(ip6_current_dest_addr()) || + ip6_addr_islinklocal(ip6_current_src_addr())) { netif = NULL; break; } +#endif /* !IPV6_CUSTOM_SCOPES */ +#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF + /* The loopback address is to be considered link-local. Packets to it + * should be dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. + * Its implied scope means packets *from* the loopback address should + * not be accepted on other interfaces, either. These requirements + * cannot be implemented in the case that loopback traffic is sent + * across a non-loopback interface, however. */ + if (ip6_addr_isloopback(ip6_current_dest_addr()) || + ip6_addr_isloopback(ip6_current_src_addr())) { + netif = NULL; + break; + } +#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */ first = 0; netif = netif_list; } else { @@ -818,8 +903,10 @@ ip6_input_cleanup: 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 + * if src == NULL, IP6_ADDR_ANY is used as source) (src is possibly not + * properly zoned) + * @param dest the destination IPv6 address to send the packet to (possibly not + * properly zoned) * @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 @@ -864,6 +951,20 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, /* Should the IPv6 header be generated or is it already included in p? */ if (dest != LWIP_IP_HDRINCL) { +#if LWIP_IPV6_SCOPES + /* If the destination address is scoped but lacks a zone, add a zone now, + * based on the outgoing interface. The lower layers (e.g., nd6) absolutely + * require addresses to be properly zoned for correctness. In some cases, + * earlier attempts will have been made to add a zone to the destination, + * but this function is the only one that is called in all (other) cases, + * so we must do this here. */ + if (ip6_addr_lacks_zone(dest, IP6_UNKNOWN)) { + ip6_addr_copy(dest_addr, *dest); + ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif); + dest = &dest_addr; + } +#endif /* LWIP_IPV6_SCOPES */ + /* 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")); @@ -879,7 +980,7 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, IP6H_NEXTH_SET(ip6hdr, nexth); /* dest cannot be NULL here */ - ip6_addr_copy(ip6hdr->dest, *dest); + ip6_addr_copy_to_packed(ip6hdr->dest, *dest); IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); @@ -888,12 +989,13 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, src = IP6_ADDR_ANY6; } /* src cannot be NULL here */ - ip6_addr_copy(ip6hdr->src, *src); + ip6_addr_copy_to_packed(ip6hdr->src, *src); } else { /* IP header already included in p */ ip6hdr = (struct ip6_hdr *)p->payload; - ip6_addr_copy(dest_addr, ip6hdr->dest); + ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest); + ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif); dest = &dest_addr; } @@ -964,8 +1066,8 @@ ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *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); + ip6_addr_copy_from_packed(src_addr, ip6hdr->src); + ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest); netif = ip6_route(&src_addr, &dest_addr); } @@ -1023,8 +1125,8 @@ ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *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); + ip6_addr_copy_from_packed(src_addr, ip6hdr->src); + ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest); netif = ip6_route(&src_addr, &dest_addr); } diff --git a/src/core/ipv6/ip6_addr.c b/src/core/ipv6/ip6_addr.c index aa06659a..61a2442a 100644 --- a/src/core/ipv6/ip6_addr.c +++ b/src/core/ipv6/ip6_addr.c @@ -160,6 +160,8 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr) return 0; } + ip6_addr_clear_zone(addr); + return 1; } diff --git a/src/core/ipv6/ip6_frag.c b/src/core/ipv6/ip6_frag.c index 50d73473..e97e9524 100644 --- a/src/core/ipv6/ip6_frag.c +++ b/src/core/ipv6/ip6_frag.c @@ -169,7 +169,15 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); } else { - icmp6_time_exceeded(p, ICMP6_TE_FRAG); + /* Reconstruct the zoned source and destination addresses, so that we do + * not end up sending the ICMP response over the wrong link. */ + ip6_addr_t src_addr, dest_addr; + ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr)); + ip6_addr_set_zone(&src_addr, ipr->src_zone); + ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr)); + ip6_addr_set_zone(&dest_addr, ipr->dest_zone); + /* Send the actual ICMP response. */ + icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr); } clen = pbuf_clen(p); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); @@ -297,8 +305,8 @@ ip6_reass(struct pbuf *p) 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_SRC(ipr))) && - ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)))) { + 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; } @@ -345,7 +353,16 @@ ip6_reass(struct pbuf *p) 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; @@ -489,7 +506,8 @@ ip6_reass(struct pbuf *p) * 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. */ + * 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; diff --git a/src/core/ipv6/mld6.c b/src/core/ipv6/mld6.c index 0cd0078b..1db59af1 100644 --- a/src/core/ipv6/mld6.c +++ b/src/core/ipv6/mld6.c @@ -293,11 +293,17 @@ mld6_input(struct pbuf *p, struct netif *inp) /** * @ingroup mld6 - * Join a group on a network interface. + * Join a group on one or all network interfaces. * - * @param srcaddr ipv6 address of the network interface which should - * join a new group. If IP6_ADDR_ANY, join on all netifs - * @param groupaddr the ipv6 address of the group to join + * If the group is to be joined on all interfaces, the given group address must + * not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE). + * If the group is to be joined on one particular interface, the given group + * address may or may not have a zone set. + * + * @param srcaddr ipv6 address (zoned) of the network interface which should + * join a new group. If IP6_ADDR_ANY6, join on all netifs + * @param groupaddr the ipv6 address of the group to join (possibly but not + * necessarily zoned) * @return ERR_OK if group was joined on the netif(s), an err_t otherwise */ err_t @@ -330,13 +336,26 @@ mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) * Join a group on a network interface. * * @param netif the network interface which should join a new group. - * @param groupaddr the ipv6 address of the group to join + * @param groupaddr the ipv6 address of the group to join (possibly but not + * necessarily zoned) * @return ERR_OK if group was joined on the netif, an err_t otherwise */ err_t mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) { struct mld_group *group; +#if LWIP_IPV6_SCOPES + ip6_addr_t ip6addr; + + /* If the address has a particular scope but no zone set, use the netif to + * set one now. Within the mld6 module, all addresses are properly zoned. */ + if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) { + ip6_addr_set(&ip6addr, groupaddr); + ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif); + groupaddr = &ip6addr; + } + IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif); +#endif /* LWIP_IPV6_SCOPES */ /* find group or create a new one if not found */ group = mld6_lookfor_group(netif, groupaddr); @@ -368,9 +387,12 @@ mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) * @ingroup mld6 * Leave a group on a network interface. * - * @param srcaddr ipv6 address of the network interface which should - * leave the group. If IP6_ISANY, leave on all netifs - * @param groupaddr the ipv6 address of the group to leave + * Zoning of address follows the same rules as @ref mld6_joingroup. + * + * @param srcaddr ipv6 address (zoned) of the network interface which should + * leave the group. If IP6_ADDR_ANY6, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave (possibly, but not + * necessarily zoned) * @return ERR_OK if group was left on the netif(s), an err_t otherwise */ err_t @@ -403,13 +425,24 @@ mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr) * Leave a group on a network interface. * * @param netif the network interface which should leave the group. - * @param groupaddr the ipv6 address of the group to leave + * @param groupaddr the ipv6 address of the group to leave (possibly, but not + * necessarily zoned) * @return ERR_OK if group was left on the netif, an err_t otherwise */ err_t mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) { struct mld_group *group; +#if LWIP_IPV6_SCOPES + ip6_addr_t ip6addr; + + if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) { + ip6_addr_set(&ip6addr, groupaddr); + ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif); + groupaddr = &ip6addr; + } + IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif); +#endif /* LWIP_IPV6_SCOPES */ /* find group */ group = mld6_lookfor_group(netif, groupaddr); @@ -561,7 +594,7 @@ mld6_send(struct netif *netif, struct mld_group *group, u8_t type) mld_hdr->chksum = 0; mld_hdr->max_resp_delay = 0; mld_hdr->reserved = 0; - ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address); #if CHECKSUM_GEN_ICMP6 IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c index c6c12df8..23a1208b 100644 --- a/src/core/ipv6/nd6.c +++ b/src/core/ipv6/nd6.c @@ -238,6 +238,7 @@ nd6_process_autoconfig_prefix(struct netif *netif, * already did exist, resulting in that address being given lifetimes. */ IP6_ADDR(&ip6addr, prefix_addr->addr[0], prefix_addr->addr[1], netif_ip6_addr(netif, 0)->addr[2], netif_ip6_addr(netif, 0)->addr[3]); + ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif); free_idx = 0; for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { @@ -294,8 +295,9 @@ nd6_input(struct pbuf *p, struct netif *inp) na_hdr = (struct na_header *)p->payload; - /* Create an aligned copy of the target address. */ - ip6_addr_set(&target_address, &(na_hdr->target_address)); + /* Create an aligned, zoned copy of the target address. */ + ip6_addr_copy_from_packed(target_address, na_hdr->target_address); + ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); /* Check a subset of the other RFC 4861 Sec. 7.1.2 requirements. */ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || na_hdr->code != 0 || @@ -424,8 +426,9 @@ nd6_input(struct pbuf *p, struct netif *inp) ns_hdr = (struct ns_header *)p->payload; - /* Create an aligned copy of the target address. */ - ip6_addr_set(&target_address, &(ns_hdr->target_address)); + /* Create an aligned, zoned copy of the target address. */ + ip6_addr_copy_from_packed(target_address, ns_hdr->target_address); + ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); /* Check a subset of the other RFC 4861 Sec. 7.1.1 requirements. */ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ns_hdr->code != 0 || @@ -538,7 +541,7 @@ nd6_input(struct pbuf *p, struct netif *inp) u8_t *buffer; /* Used to copy options. */ u16_t offset; #if LWIP_ND6_RDNSS_MAX_DNS_SERVERS - /* There can by multiple RDNSS options per RA */ + /* There can be multiple RDNSS options per RA */ u8_t rdnss_server_idx = 0; #endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */ @@ -662,7 +665,8 @@ nd6_input(struct pbuf *p, struct netif *inp) prefix_opt = (struct prefix_option *)buffer; /* Get a memory-aligned copy of the prefix. */ - ip6_addr_set(&prefix_addr, &(prefix_opt->prefix)); + ip6_addr_copy_from_packed(prefix_addr, prefix_opt->prefix); + ip6_addr_assign_zone(&prefix_addr, IP6_UNICAST, inp); if (!ip6_addr_islinklocal(&prefix_addr)) { if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) && @@ -710,8 +714,9 @@ nd6_input(struct pbuf *p, struct netif *inp) for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) { ip_addr_t rdnss_address; - /* Get a memory-aligned copy of the prefix. */ - ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]); + /* Get a memory-aligned, zoned copy of the prefix. */ + ip_addr_copy_from_ip6_packed(rdnss_address, rdnss_opt->rdnss_address[n]); + ip6_addr_assign_zone(ip_2_ip6(&rdnss_address), IP6_UNKNOWN, inp); if (htonl(rdnss_opt->lifetime) > 0) { /* TODO implement Lifetime > 0 */ @@ -758,8 +763,9 @@ nd6_input(struct pbuf *p, struct netif *inp) redir_hdr = (struct redirect_header *)p->payload; - /* Copy original destination address, to have an aligned copy. */ - ip6_addr_set(&destination_address, &(redir_hdr->destination_address)); + /* Create an aligned, zoned copy of the destination address. */ + ip6_addr_copy_from_packed(destination_address, redir_hdr->destination_address); + ip6_addr_assign_zone(&destination_address, IP6_UNICAST, inp); /* Check a subset of the other RFC 4861 Sec. 8.1 requirements. */ if (!ip6_addr_islinklocal(ip6_current_src_addr()) || @@ -792,22 +798,23 @@ nd6_input(struct pbuf *p, struct netif *inp) return; } + /* Create an aligned, zoned copy of the target address. */ + ip6_addr_copy_from_packed(target_address, redir_hdr->target_address); + ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp); + /* Set the new target address. */ - ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + ip6_addr_copy(destination_cache[i].next_hop_addr, target_address); /* If Link-layer address of other router is given, try to add to neighbor cache. */ if (lladdr_opt != NULL) { if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { - /* Copy target address to current source address, to have an aligned copy. */ - ip6_addr_set(&target_address, &(redir_hdr->target_address)); - i = nd6_find_neighbor_cache_entry(&target_address); if (i < 0) { i = nd6_new_neighbor_cache_entry(); if (i >= 0) { neighbor_cache[i].netif = inp; MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); - ip6_addr_set(&(neighbor_cache[i].next_hop_address), &target_address); + ip6_addr_copy(neighbor_cache[i].next_hop_address, target_address); /* Receiving a message does not prove reachability: only in one direction. * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ @@ -833,7 +840,7 @@ nd6_input(struct pbuf *p, struct netif *inp) struct icmp6_hdr *icmp6hdr; /* Packet too big message */ struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */ u32_t pmtu; - ip6_addr_t tmp; + ip6_addr_t destination_address; /* Check that ICMPv6 header + IPv6 header fit in payload */ if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { @@ -847,11 +854,12 @@ nd6_input(struct pbuf *p, struct netif *inp) icmp6hdr = (struct icmp6_hdr *)p->payload; ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); - /* Copy original destination address to current source address, to have an aligned copy. */ - ip6_addr_set(&tmp, &(ip6hdr->dest)); + /* Create an aligned, zoned copy of the destination address. */ + ip6_addr_copy_from_packed(destination_address, ip6hdr->dest); + ip6_addr_assign_zone(&destination_address, IP6_UNKNOWN, inp); /* Look for entry in destination cache. */ - i = nd6_find_destination_cache_entry(&tmp); + i = nd6_find_destination_cache_entry(&destination_address); if (i < 0) { /* Destination not in cache, drop packet. */ pbuf_free(p); @@ -1110,6 +1118,8 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) const ip6_addr_t *src_addr; u16_t lladdr_opt_len; + LWIP_ASSERT("target address is required", target_addr != NULL); + if (!(flags & ND6_SEND_FLAG_ANY_SRC) && ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { /* Use link-local address as source address. */ @@ -1136,7 +1146,7 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) ns_hdr->code = 0; ns_hdr->chksum = 0; ns_hdr->reserved = 0; - ip6_addr_set(&(ns_hdr->target_address), target_addr); + ip6_addr_copy_to_packed(ns_hdr->target_address, *target_addr); if (lladdr_opt_len != 0) { struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); @@ -1148,6 +1158,7 @@ nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) /* Generate the solicited node address for the target address. */ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); target_addr = &multicast_address; } @@ -1182,6 +1193,8 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) const ip6_addr_t *dest_addr; u16_t lladdr_opt_len; + LWIP_ASSERT("target address is required", target_addr != NULL); + /* Use link-local address as source address. */ /* src_addr = netif_ip6_addr(netif, 0); */ /* Use target address as source address. */ @@ -1206,7 +1219,7 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) na_hdr->reserved[0] = 0; na_hdr->reserved[1] = 0; na_hdr->reserved[2] = 0; - ip6_addr_set(&(na_hdr->target_address), target_addr); + ip6_addr_copy_to_packed(na_hdr->target_address, *target_addr); lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; lladdr_opt->length = (u8_t)lladdr_opt_len; @@ -1215,9 +1228,11 @@ nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags) /* Generate the solicited node address for the target address. */ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); dest_addr = &multicast_address; } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { ip6_addr_set_allnodes_linklocal(&multicast_address); + ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); dest_addr = &multicast_address; } else { dest_addr = ip6_current_src_addr(); @@ -1262,6 +1277,7 @@ nd6_send_rs(struct netif *netif) /* Generate the all routers target address. */ ip6_addr_set_allrouters_linklocal(&multicast_address); + ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); /* Allocate a packet. */ if (src_addr != IP6_ADDR_ANY6) { @@ -1477,6 +1493,9 @@ static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr) { s8_t i; + + IP6_ADDR_ZONECHECK(ip6addr); + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { return i; @@ -1693,6 +1712,8 @@ nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif) { s8_t i; + IP6_ADDR_ZONECHECK_NETIF(router_addr, netif); + /* Look for router. */ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { if ((default_router_list[i].neighbor_entry != NULL) && @@ -1720,6 +1741,8 @@ nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif) s8_t free_router_index; s8_t neighbor_index; + IP6_ADDR_ZONECHECK_NETIF(router_addr, netif); + /* Do we have a neighbor entry for this router? */ neighbor_index = nd6_find_neighbor_cache_entry(router_addr); if (neighbor_index < 0) { @@ -1838,6 +1861,8 @@ nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif) #endif /* LWIP_HOOK_ND6_GET_GW */ s8_t i; + IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif); + #if LWIP_NETIF_HWADDRHINT if (netif->addr_hint != NULL) { /* per-pcb cached entry was given */ @@ -2106,7 +2131,9 @@ nd6_send_q(s8_t i) /* Get ipv6 header. */ ip6hdr = (struct ip6_hdr *)(q->p->payload); /* Create an aligned copy. */ - ip6_addr_set(&dest, &(ip6hdr->dest)); + ip6_addr_copy_from_packed(dest, ip6hdr->dest); + /* Restore the zone, if applicable. */ + ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif); /* send the queued IPv6 packet */ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest); /* free the queued IP packet */ @@ -2119,7 +2146,9 @@ nd6_send_q(s8_t i) /* Get ipv6 header. */ ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); /* Create an aligned copy. */ - ip6_addr_set(&dest, &(ip6hdr->dest)); + ip6_addr_copy_from_packed(dest, ip6hdr->dest); + /* Restore the zone, if applicable. */ + ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif); /* send the queued IPv6 packet */ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest); /* free the queued IP packet */ @@ -2317,6 +2346,7 @@ nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state) if (old_member != new_member) { ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]); + ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif); if (new_member) { mld6_joingroup_netif(netif, &multicast_address); diff --git a/src/core/netif.c b/src/core/netif.c index 5522f3f1..afd14585 100644 --- a/src/core/netif.c +++ b/src/core/netif.c @@ -1042,6 +1042,7 @@ void netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3) { const ip6_addr_t *old_addr; + ip_addr_t new_ipaddr; LWIP_ASSERT("netif != NULL", netif != NULL); LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES); @@ -1051,11 +1052,10 @@ netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, (old_addr->addr[2] != i2) || (old_addr->addr[3] != i3)) { LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n")); + IP_ADDR6(&new_ipaddr, i0, i1, i2, i3); + ip6_addr_assign_zone(ip_2_ip6(&new_ipaddr), IP6_UNICAST, netif); + if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) { -#if LWIP_TCP || LWIP_UDP - ip_addr_t new_ipaddr; - IP_ADDR6(&new_ipaddr, i0, i1, i2, i3); -#endif /* LWIP_TCP || LWIP_UDP */ #if LWIP_TCP tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr); #endif /* LWIP_TCP */ @@ -1068,8 +1068,7 @@ netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, } /* @todo: remove/readd mib2 ip6 entries? */ - IP6_ADDR(ip_2_ip6(&(netif->ip6_addr[addr_idx])), i0, i1, i2, i3); - IP_SET_TYPE_VAL(netif->ip6_addr[addr_idx], IPADDR_TYPE_V6); + ip_addr_copy(netif->ip6_addr[addr_idx], new_ipaddr); if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) { netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6); @@ -1130,6 +1129,8 @@ netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state) if (!old_valid && new_valid) { /* address added by setting valid */ + /* This is a good moment to check that the address is properly zoned. */ + IP6_ADDR_ZONECHECK_NETIF(netif_ip6_addr(netif, addr_idx), netif); /* @todo: add mib2 ip6 entries? */ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6); } @@ -1150,6 +1151,10 @@ netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state) * index. Depending on its state, it may or may not be assigned to the * interface (as per RFC terminology). * + * The given address may or may not be zoned (i.e., have a zone index other + * than IP6_NO_ZONE). If the address is zoned, it must have the correct zone + * for the given netif, or no match will be found. + * * @param netif the netif to check * @param ip6addr the IPv6 address to find * @return >= 0: address found, this is its index @@ -1159,9 +1164,16 @@ s8_t netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr) { s8_t i; + +#if LWIP_IPV6_SCOPES + if (ip6_addr_has_zone(ip6addr) && !ip6_addr_test_zone(ip6addr, netif)) { + return -1; /* wrong zone, no match */ + } +#endif /* LWIP_IPV6_SCOPES */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) && - ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + ip6_addr_cmp_zoneless(netif_ip6_addr(netif, i), ip6addr)) { return i; } } @@ -1210,6 +1222,16 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) } } + /* Set a link-local zone. Even though the zone is implied by the owning + * netif, setting the zone anyway has two important conceptual advantages: + * 1) it avoids the need for a ton of exceptions in internal code, allowing + * e.g. ip6_addr_cmp() to be used on local addresses; + * 2) the properly zoned address is visible externally, e.g. when any outside + * code enumerates available addresses or uses one to bind a socket. + * Any external code unaware of address scoping is likely to just ignore the + * zone field, so this should not create any compatibility problems. */ + ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[0]), IP6_UNICAST, netif); + /* Set address state. */ #if LWIP_IPV6_DUP_DETECT_ATTEMPTS /* Will perform duplicate address detection (DAD). */ @@ -1244,10 +1266,11 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos 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++) { + /* Find a free slot. The first one is reserved for link-local addresses. */ + for (i = ip6_addr_islinklocal(ip6addr) ? 0 : 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) { if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) { ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr); + ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[i]), IP6_UNICAST, netif); netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE); if (chosen_idx != NULL) { *chosen_idx = i; diff --git a/src/core/raw.c b/src/core/raw.c index 333fcb11..29393775 100644 --- a/src/core/raw.c +++ b/src/core/raw.c @@ -215,6 +215,15 @@ raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) return ERR_VAL; } ip_addr_set_ipaddr(&pcb->local_ip, ipaddr); +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now. + * This is legacy support: scope-aware callers should always provide properly + * zoned source addresses. */ + if (IP_IS_V6(&pcb->local_ip) && + ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNICAST)) { + ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip)); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ return ERR_OK; } @@ -239,6 +248,14 @@ raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr) return ERR_VAL; } ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr); +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now, + * using the bound address to make a more informed decision when possible. */ + if (IP_IS_V6(&pcb->remote_ip) && + ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) { + ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip)); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ pcb->flags |= RAW_FLAGS_CONNECTED; return ERR_OK; } diff --git a/src/core/tcp.c b/src/core/tcp.c index 30896c45..f20b44a9 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -542,6 +542,9 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) int i; int max_pcb_list = NUM_TCP_PCB_LISTS; struct tcp_pcb *cpcb; +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + ip_addr_t zoned_ipaddr; +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ #if LWIP_IPV4 /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */ @@ -568,6 +571,18 @@ tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) } #endif /* SO_REUSE */ +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now. + * This is legacy support: scope-aware callers should always provide properly + * zoned source addresses. Do the zone selection before the address-in-use + * check below; as such we have to make a temporary copy of the address. */ + if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) { + ip_addr_copy(zoned_ipaddr, *ipaddr); + ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr)); + ipaddr = &zoned_ipaddr; + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ + if (port == 0) { port = tcp_new_port(); if (port == 0) { @@ -855,6 +870,7 @@ err_t tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, tcp_connected_fn connected) { + struct netif *netif = NULL; err_t ret; u32_t iss; u16_t old_local_port; @@ -872,7 +888,6 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, /* check if we have a route to the remote host */ if (ip_addr_isany(&pcb->local_ip)) { /* no local IP address set, yet. */ - struct netif *netif; const ip_addr_t *local_ip; ip_route_get_local_ip(&pcb->local_ip, &pcb->remote_ip, netif, local_ip); if ((netif == NULL) || (local_ip == NULL)) { @@ -882,7 +897,24 @@ tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, } /* Use the address as local address of the pcb. */ ip_addr_copy(pcb->local_ip, *local_ip); + } else { + netif = ip_route(&pcb->local_ip, &pcb->remote_ip); + if (netif == NULL) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } } + LWIP_ASSERT("netif != NULL", netif != NULL); + +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now. + * Given that we already have the target netif, this is easy and cheap. */ + if (IP_IS_V6(&pcb->remote_ip) && + ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST)) { + ip6_addr_assign_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST, netif); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ old_local_port = pcb->local_port; if (pcb->local_port == 0) { diff --git a/src/core/udp.c b/src/core/udp.c index ce2e3d29..7c1c60ed 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -522,16 +522,12 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); -#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) +#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS if (ip_addr_ismulticast(dst_ip_route)) { #if LWIP_IPV6 - if (IP_IS_V6(dst_ip)) { - /* For multicast, find a netif based on source address. */ - dst_ip_route = &pcb->local_ip; - } else + if (IP_IS_V4(dst_ip)) #endif /* LWIP_IPV6 */ { -#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS /* IPv4 does not use source-based routing by default, so we use an administratively selected interface for multicast by default. However, this can be overridden by setting an interface address @@ -540,10 +536,9 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, !ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) { dst_ip_route = &pcb->multicast_ip; } -#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ } } -#endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */ +#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ /* find the outgoing network interface for this packet */ if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { @@ -883,6 +878,9 @@ udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) { struct udp_pcb *ipcb; u8_t rebind; +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + ip_addr_t zoned_ipaddr; +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ #if LWIP_IPV4 /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */ @@ -910,6 +908,18 @@ udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) } } +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now. + * This is legacy support: scope-aware callers should always provide properly + * zoned source addresses. Do the zone selection before the address-in-use + * check below; as such we have to make a temporary copy of the address. */ + if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) { + ip_addr_copy(zoned_ipaddr, *ipaddr); + ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr)); + ipaddr = &zoned_ipaddr; + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ + /* no port specified? */ if (port == 0) { port = udp_new_port(); @@ -994,6 +1004,15 @@ udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) } ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr); +#if LWIP_IPV6 && LWIP_IPV6_SCOPES + /* If the given IP address should have a zone but doesn't, assign one now, + * using the bound address to make a more informed decision when possible. */ + if (IP_IS_V6(&pcb->remote_ip) && + ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) { + ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip)); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ + pcb->remote_port = port; pcb->flags |= UDP_FLAGS_CONNECTED; diff --git a/src/include/lwip/icmp6.h b/src/include/lwip/icmp6.h index a29dc8c1..374ff448 100644 --- a/src/include/lwip/icmp6.h +++ b/src/include/lwip/icmp6.h @@ -57,6 +57,8 @@ void icmp6_input(struct pbuf *p, struct netif *inp); void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c, + const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr); void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); #endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/src/include/lwip/inet.h b/src/include/lwip/inet.h index 4a34f026..2982a0f4 100644 --- a/src/include/lwip/inet.h +++ b/src/include/lwip/inet.h @@ -134,8 +134,6 @@ extern const struct in6_addr in6addr_any; #define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr)) #define inet_addr_to_ip4addr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr)) -/* ATTENTION: the next define only works because both s_addr and ip4_addr_t are an u32_t effectively! */ -#define inet_addr_to_ip4addr_p(target_ip4addr_p, source_inaddr) ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr)) /* directly map this to the lwip internal functions */ #define inet_addr(cp) ipaddr_addr(cp) @@ -153,9 +151,8 @@ extern const struct in6_addr in6addr_any; #define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ - (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} -/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ -#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3]; \ + ip6_addr_clear_zone(target_ip6addr);} /* directly map this to the lwip internal functions */ #define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) diff --git a/src/include/lwip/ip6_addr.h b/src/include/lwip/ip6_addr.h index 6215bc04..e1118331 100644 --- a/src/include/lwip/ip6_addr.h +++ b/src/include/lwip/ip6_addr.h @@ -47,6 +47,7 @@ #if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ +#include "lwip/ip6_zone.h" #ifdef __cplusplus extern "C" { @@ -57,6 +58,9 @@ extern "C" { used as local variable, on the stack, etc. */ struct ip6_addr { u32_t addr[4]; +#if LWIP_IPV6_SCOPES + u8_t zone; +#endif /* LWIP_IPV6_SCOPES */ }; /** IPv6 address */ @@ -72,7 +76,8 @@ typedef struct ip6_addr ip6_addr_t; (ip6addr)->addr[0] = idx0; \ (ip6addr)->addr[1] = idx1; \ (ip6addr)->addr[2] = idx2; \ - (ip6addr)->addr[3] = idx3; } while(0) + (ip6addr)->addr[3] = idx3; \ + ip6_addr_clear_zone(ip6addr); } while(0) /** Access address in 16-bit block */ #define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xffff)) @@ -95,18 +100,34 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ (dest).addr[1] = (src).addr[1]; \ (dest).addr[2] = (src).addr[2]; \ - (dest).addr[3] = (src).addr[3];}while(0) + (dest).addr[3] = (src).addr[3]; \ + ip6_addr_copy_zone((dest), (src)); }while(0) /** Safely copy one IPv6 address to another (src may be NULL) */ #define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ - (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3]; \ + ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src)); }while(0) + +/** Copy packed IPv6 address to unpacked IPv6 address; zone is not set */ +#define ip6_addr_copy_from_packed(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3]; \ + ip6_addr_clear_zone(&dest); }while(0) + +/** Copy unpacked IPv6 address to packed IPv6 address; zone is lost */ +#define ip6_addr_copy_to_packed(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3]; }while(0) /** Set complete address to zero */ #define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ (ip6addr)->addr[1] = 0; \ (ip6addr)->addr[2] = 0; \ - (ip6addr)->addr[3] = 0;}while(0) + (ip6addr)->addr[3] = 0; \ + ip6_addr_clear_zone(ip6addr);}while(0) /** Set address to ipv6 'any' (no need for lwip_htonl()) */ #define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) @@ -114,33 +135,57 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ (ip6addr)->addr[1] = 0; \ (ip6addr)->addr[2] = 0; \ - (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL); \ + ip6_addr_clear_zone(ip6addr);}while(0) /** Safely copy one IPv6 address to another and change byte order * from host- to network-order. */ #define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : lwip_htonl((src)->addr[0]); \ (dest)->addr[1] = (src) == NULL ? 0 : lwip_htonl((src)->addr[1]); \ (dest)->addr[2] = (src) == NULL ? 0 : lwip_htonl((src)->addr[2]); \ - (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]);}while(0) + (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]); \ + ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src));}while(0) +/** Compare IPv6 networks, ignoring zone information. To be used sparingly! */ +#define ip6_addr_netcmp_zoneless(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + /** * Determine if two IPv6 address are on the same network. * - * @arg addr1 IPv6 address 1 - * @arg addr2 IPv6 address 2 - * @return !0 if the network identifiers of both address match + * @param addr1 IPv6 address 1 + * @param addr2 IPv6 address 2 + * @return 1 if the network identifiers of both address match, 0 if not */ -#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ - ((addr1)->addr[1] == (addr2)->addr[1])) +#define ip6_addr_netcmp(addr1, addr2) (ip6_addr_netcmp_zoneless((addr1), (addr2)) && \ + ip6_addr_cmp_zone((addr1), (addr2))) /* Exact-host comparison *after* ip6_addr_netcmp() succeeded, for efficiency. */ #define ip6_addr_nethostcmp(addr1, addr2) (((addr1)->addr[2] == (addr2)->addr[2]) && \ ((addr1)->addr[3] == (addr2)->addr[3])) -#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ +/** Compare IPv6 addresses, ignoring zone information. To be used sparingly! */ +#define ip6_addr_cmp_zoneless(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ ((addr1)->addr[1] == (addr2)->addr[1]) && \ ((addr1)->addr[2] == (addr2)->addr[2]) && \ ((addr1)->addr[3] == (addr2)->addr[3])) +/** + * Determine if two IPv6 addresses are the same. In particular, the address + * part of both must be the same, and the zone must be compatible. + * + * @param addr1 IPv6 address 1 + * @param addr2 IPv6 address 2 + * @return 1 if the addresses are considered equal, 0 if not + */ +#define ip6_addr_cmp(addr1, addr2) (ip6_addr_cmp_zoneless((addr1), (addr2)) && \ + ip6_addr_cmp_zone((addr1), (addr2))) + +/** Compare IPv6 address to packed address and zone */ +#define ip6_addr_cmp_packed(ip6addr, paddr, zone_idx) (((ip6addr)->addr[0] == (paddr)->addr[0]) && \ + ((ip6addr)->addr[1] == (paddr)->addr[1]) && \ + ((ip6addr)->addr[2] == (paddr)->addr[2]) && \ + ((ip6addr)->addr[3] == (paddr)->addr[3]) && \ + ip6_addr_equals_zone((ip6addr), (zone_idx))) #define ip6_get_subnet_id(ip6addr) (lwip_htonl((ip6addr)->addr[2]) & 0x0000ffffUL) @@ -187,7 +232,13 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff080000UL)) #define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff0e0000UL)) -/* @todo define get/set for well-know multicast addresses, e.g. ff02::1 */ +/* Scoping note: while interface-local and link-local multicast addresses do + * have a scope (i.e., they are meaningful only in the context of a particular + * interface), the following functions are not assigning or comparing zone + * indices. The reason for this is backward compatibility. Any call site that + * produces a non-global multicast address must assign a multicast address as + * appropriate itself. */ + #define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ ((ip6addr)->addr[1] == 0UL) && \ ((ip6addr)->addr[2] == 0UL) && \ @@ -200,7 +251,8 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ (ip6addr)->addr[1] = 0; \ (ip6addr)->addr[2] = 0; \ - (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL); \ + ip6_addr_clear_zone(ip6addr); }while(0) #define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ ((ip6addr)->addr[1] == 0UL) && \ @@ -209,7 +261,8 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ (ip6addr)->addr[1] = 0; \ (ip6addr)->addr[2] = 0; \ - (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL); \ + ip6_addr_clear_zone(ip6addr); }while(0) #define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ @@ -218,7 +271,8 @@ typedef struct ip6_addr ip6_addr_t; #define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ (ip6addr)->addr[1] = 0; \ (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ - (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id));}while(0) + (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id)); \ + ip6_addr_clear_zone(ip6addr); }while(0) #define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ ((ip6addr)->addr[1] == 0) && \ diff --git a/src/include/lwip/ip6_frag.h b/src/include/lwip/ip6_frag.h index 3176d526..87e0e86a 100644 --- a/src/include/lwip/ip6_frag.h +++ b/src/include/lwip/ip6_frag.h @@ -105,6 +105,10 @@ struct ip6_reassdata { u16_t datagram_len; u8_t nexth; u8_t timer; +#if LWIP_IPV6_SCOPES + u8_t src_zone; /* zone of original packet's source address */ + u8_t dest_zone; /* zone of original packet's destination address */ +#endif /* LWIP_IPV6_SCOPES */ }; #define ip6_reass_init() /* Compatibility define */ diff --git a/src/include/lwip/ip6_zone.h b/src/include/lwip/ip6_zone.h new file mode 100644 index 00000000..d16a6b8a --- /dev/null +++ b/src/include/lwip/ip6_zone.h @@ -0,0 +1,278 @@ +/** + * @file + * + * IPv6 address scopes, zones, and scoping policy. + * + * This header provides the means to implement support for IPv6 address scopes, + * as per RFC 4007. An address scope can be either global or more constrained. + * In lwIP, we say that an address "has a scope" or "is scoped" when its scope + * is constrained, in which case the address is meaningful only in a specific + * "zone." For unicast addresses, only link-local addresses have a scope; in + * that case, the scope is the link. For multicast addresses, there are various + * scopes defined by RFC 4007 and others. For any constrained scope, a system + * must establish a (potentially one-to-many) mapping between zones and local + * interfaces. For example, a link-local address is valid on only one link (its + * zone). That link may be attached to one or more local interfaces. The + * decisions on which scopes are constrained and the mapping between zones and + * interfaces is together what we refer to as the "scoping policy" - more on + * this in a bit. + * + * In lwIP, each IPv6 address has an associated zone index. This zone index may + * be set to "no zone" (IP6_NO_ZONE, 0) or an actual zone. We say that an + * address "has a zone" or "is zoned" when its zone index is *not* set to "no + * zone." In lwIP, in principle, each address should be "properly zoned," which + * means that if the address has a zone if and only if has a scope. As such, it + * is a rule that an unscoped (e.g., global) address must never have a zone. + * Even though one could argue that there is always one zone even for global + * scopes, this rule exists for implementation simplicity. Violation of the + * rule will trigger assertions or otherwise result in undesired behavior. + * + * Backward compatibility prevents us from requiring that applications always + * provide properly zoned addresses. We do enforce the rule that the in the + * lwIP link layer (everything below netif->output_ip6() and in particular ND6) + * *all* addresses are properly zoned. Thus, on the output paths down the + * stack, various places deal with the case of addresses that lack a zone. + * Some of them are best-effort for efficiency (e.g. the PCB bind and connect + * API calls' attempts to add missing zones); ultimately the IPv6 output + * handler (@ref ip6_output_if_src) will set a zone if necessary. + * + * Aside from dealing with scoped addresses lacking a zone, a proper IPv6 + * implementation must also ensure that a packet with a scoped source and/or + * destination address does not leave its zone. This is currently implemented + * in the input and forward functions. However, for output, these checks are + * deliberately omitted in order to keep the implementation lightweight. The + * routing algorithm in @ref ip6_route will take decisions such that it will + * not cause zone violations unless the application sets bad addresses, though. + * + * In terms of scoping policy, lwIP implements the default policy from RFC 4007 + * using macros in this file. This policy considers link-local unicast + * addresses and (only) interface-local and link-local multicast addresses as + * having a scope. For all these addresses, the zone is equal to the interface. + * As shown below in this file, it is possible to implement a custom policy. + */ + +/* + * Copyright (c) 2017 The MINIX 3 Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: David van Moolenbroek + * + */ +#ifndef LWIP_HDR_IP6_ZONE_H +#define LWIP_HDR_IP6_ZONE_H + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +/** Identifier for "no zone". */ +#define IP6_NO_ZONE 0 + +#if LWIP_IPV6_SCOPES + +/** Zone initializer for static IPv6 address initialization, including comma. */ +#define IPADDR6_ZONE_INIT , IP6_NO_ZONE + +/** Return the zone index of the given IPv6 address; possibly "no zone". */ +#define ip6_addr_zone(ip6addr) ((ip6addr)->zone) + +/** Does the given IPv6 address have a zone set? (0/1) */ +#define ip6_addr_has_zone(ip6addr) (ip6_addr_zone(ip6addr) != IP6_NO_ZONE) + +/** Set the zone field of an IPv6 address to a particular value. */ +#define ip6_addr_set_zone(ip6addr, zone_idx) ((ip6addr)->zone = (zone_idx)) + +/** Clear the zone field of an IPv6 address, setting it to "no zone". */ +#define ip6_addr_clear_zone(ip6addr) ((ip6addr)->zone = IP6_NO_ZONE) + +/** Copy the zone field from the second IPv6 address to the first one. */ +#define ip6_addr_copy_zone(ip6addr1, ip6addr2) ((ip6addr1).zone = (ip6addr2).zone) + +/** Is the zone field of the given IPv6 address equal to the given zone index? (0/1) */ +#define ip6_addr_equals_zone(ip6addr, zone_idx) ((ip6addr)->zone == (zone_idx)) + +/** Are the zone fields of the given IPv6 addresses equal? (0/1) + * This macro must only be used on IPv6 addresses of the same scope. */ +#define ip6_addr_cmp_zone(ip6addr1, ip6addr2) ((ip6addr1)->zone == (ip6addr2)->zone) + +/** Symbolic constants for the 'type' parameters in some of the macros below. + * These exist for efficiency only, allowing the macros to avoid certain tests + * when the address is known not to be of a certain type. Dead code elimination + * will do the rest. IP6_MULTICAST is supported but currently not optimized. */ +#define IP6_UNKNOWN 0 +#define IP6_UNICAST 1 +#define IP6_MULTICAST 2 + +/** IPV6_CUSTOM_SCOPES: together, the following three macro definitions, + * @ref ip6_addr_has_scope, @ref ip6_addr_assign_zone, and + * @ref ip6_addr_test_zone, completely define the lwIP scoping policy. + * The definitions below implement the default policy from RFC 4007 Sec. 6. + * Should an implementation desire to implement a different policy, it can + * define IPV6_CUSTOM_SCOPES to 1 and supply its own definitions for the three + * macros instead. + */ +#ifndef IPV6_CUSTOM_SCOPES +#define IPV6_CUSTOM_SCOPES 0 +#endif /* !IPV6_CUSTOM_SCOPES */ + +#if !IPV6_CUSTOM_SCOPES + +/** + * Determine whether an IPv6 address has a constrained scope, and as such is + * meaningful only if accompanied by a zone index to identify the scope's zone. + * The given address type may be used to eliminate at compile time certain + * checks that will evaluate to false at run time anyway. + * + * This default implementation follows the default model of RFC 4007, where + * only interface-local and link-local scopes are defined. + * + * Even though the unicast loopback address does have an implied link-local + * scope, in this implementation it does not have an explicitly assigned zone + * index. As such it should not be tested for in this macro. + * + * @param ip6addr the IPv6 address (const); only its address part is examined. + * @param type address type; one of IP6_UNICAST, IP6_MULTICAST, IP6_UNKNOWN. + * @return 1 if the address has a constrained scope, 0 if it does not. + */ +#define ip6_addr_has_scope(ip6addr, type) \ + (ip6_addr_islinklocal(ip6addr) || (((type) != IP6_UNICAST) && \ + ip6_addr_ismulticast_iflocal(ip6addr) || \ + ip6_addr_ismulticast_linklocal(ip6addr))) + +/** + * Assign a zone index to an IPv6 address, based on a network interface. If the + * given address has a scope, the assigned zone index is that scope's zone of + * the given netif; otherwise, the assigned zone index is "no zone". + * + * This default implementation follows the default model of RFC 4007, where + * only interface-local and link-local scopes are defined, and the zone index + * of both of those scopes always equals the index of the network interface. + * As such, this default implementation need not distinguish between different + * constrained scopes when assigning the zone. + * + * @param ip6addr the IPv6 address; its address part is examined, and its zone + * index is assigned. + * @param type address type; one of IP6_UNICAST, IP6_MULTICAST, IP6_UNKNOWN. + * @param netif the network interface (const). + */ +#define ip6_addr_assign_zone(ip6addr, type, netif) \ + (ip6_addr_set_zone((ip6addr), \ + ip6_addr_has_scope((ip6addr), (type)) ? netif_num_to_index(netif) : 0)) + +/** + * Test whether an IPv6 address is "zone-compatible" with a network interface. + * That is, test whether the network interface is part of the zone associated + * with the address. For efficiency, this macro is only ever called if the + * given address is either scoped or zoned, and thus, it need not test this. + * If an address is scoped but not zoned, or zoned and not scoped, it is + * considered not zone-compatible with any netif. + * + * This default implementation follows the default model of RFC 4007, where + * only interface-local and link-local scopes are defined, and the zone index + * of both of those scopes always equals the index of the network interface. + * As such, there is always only one matching netif for a specific zone index, + * but all call sites of this macro currently support multiple matching netifs + * as well (at no additional expense in the common case). + * + * @param ip6addr the IPv6 address (const). + * @param netif the network interface (const). + * @return 1 if the address is scope-compatible with the netif, 0 if not. + */ +#define ip6_addr_test_zone(ip6addr, netif) \ + (ip6_addr_equals_zone((ip6addr), netif_num_to_index(netif))) + +#endif /* !IPV6_CUSTOM_SCOPES */ + +/** Does the given IPv6 address have a scope, and as such should also have a + * zone to be meaningful, but does not actually have a zone? (0/1) */ +#define ip6_addr_lacks_zone(ip6addr, type) \ + (!ip6_addr_has_zone(ip6addr) && ip6_addr_has_scope((ip6addr), (type))) + +/** + * Try to select a zone for a scoped address that does not yet have a zone. + * Called from PCB bind and connect routines, for two reasons: 1) to save on + * this (relatively expensive) selection for every individual packet route + * operation and 2) to allow the application to obtain the selected zone from + * the PCB as is customary for e.g. getsockname/getpeername BSD socket calls. + * + * Ideally, callers would always supply a properly zoned address, in which case + * this function would not be needed. It exists both for compatibility with the + * BSD socket API (which accepts zoneless destination addresses) and for + * backward compatibility with pre-scoping lwIP code. + * + * It may be impossible to select a zone, e.g. if there are no netifs. In that + * case, the address's zone field will be left as is. + * + * @param dest the IPv6 address for which to select and set a zone. + * @param src source IPv6 address (const); may be equal to dest. + */ +#define ip6_addr_select_zone(dest, src) do { struct netif *selected_netif; \ + selected_netif = ip6_route((src), (dest)); \ + if (selected_netif != NULL) { \ + ip6_addr_assign_zone((dest), IP6_UNKNOWN, selected_netif); \ + } } while (0) + +#else /* LWIP_IPV6_SCOPES */ + +#define IPADDR6_ZONE_INIT +#define ip6_addr_zone(ip6addr) (IP6_NO_ZONE) +#define ip6_addr_has_zone(ip6addr) (0) +#define ip6_addr_set_zone(ip6addr, zone_idx) +#define ip6_addr_clear_zone(ip6addr) +#define ip6_addr_copy_zone(ip6addr1, ip6addr2) +#define ip6_addr_equals_zone(ip6addr, zone_idx) (1) +#define ip6_addr_cmp_zone(ip6addr1, ip6addr2) (1) +#define IPV6_CUSTOM_SCOPES 0 +#define ip6_addr_has_scope(ip6addr, type) (0) +#define ip6_addr_assign_zone(ip6addr, type, netif) +#define ip6_addr_test_zone(ip6addr, netif) (1) +#define ip6_addr_lacks_zone(ip6addr, type) (0) +#define ip6_addr_select_zone(ip6addr, src) + +#endif /* LWIP_IPV6_SCOPES */ + +#if LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG + +/** Verify that the given IPv6 address is properly zoned. */ +#define IP6_ADDR_ZONECHECK(ip6addr) LWIP_ASSERT("IPv6 zone check failed", \ + ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) == ip6_addr_has_zone(ip6addr)) + +/** Verify that the given IPv6 address is properly zoned for the given netif. */ +#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif) LWIP_ASSERT("IPv6 netif zone check failed", \ + ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) ? \ + (ip6_addr_has_zone(ip6addr) && \ + (((netif) == NULL) || ip6_addr_test_zone((ip6addr), (netif)))) : \ + !ip6_addr_has_zone(ip6addr)) + +#else /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */ + +#define IP6_ADDR_ZONECHECK(ip6addr) +#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif) + +#endif /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */ + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_IP6_ADDR_H */ diff --git a/src/include/lwip/ip_addr.h b/src/include/lwip/ip_addr.h index def2d08c..844a06f2 100644 --- a/src/include/lwip/ip_addr.h +++ b/src/include/lwip/ip_addr.h @@ -78,18 +78,19 @@ typedef struct ip_addr { extern const ip_addr_t ip_addr_any_type; /** @ingroup ip4addr */ -#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 } +#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V4 } /** @ingroup ip4addr */ #define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d))) + /** @ingroup ip6addr */ -#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } } }, IPADDR_TYPE_V6 } +#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V6 } /** @ingroup ip6addr */ -#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 } +#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_V6 } /** @ingroup ipaddr */ #define IP_IS_ANY_TYPE_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY) /** @ingroup ipaddr */ -#define IPADDR_ANY_TYPE_INIT { { { { 0ul, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_ANY } +#define IPADDR_ANY_TYPE_INIT { { { { 0ul, 0ul, 0ul, 0ul } IPADDR6_ZONE_INIT } }, IPADDR_TYPE_ANY } /** @ingroup ip4addr */ #define IP_IS_V4_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4) @@ -132,6 +133,9 @@ extern const ip_addr_t ip_addr_any_type; /** @ingroup ip6addr */ #define ip_addr_copy_from_ip6(dest, src) do{ \ ip6_addr_copy(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0) +/** @ingroup ip6addr */ +#define ip_addr_copy_from_ip6_packed(dest, src) do{ \ + ip6_addr_copy_from_packed(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0) /** @ingroup ip4addr */ #define ip_addr_copy_from_ip4(dest, src) do{ \ ip4_addr_copy(*ip_2_ip4(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V4); }while(0) @@ -226,7 +230,8 @@ int ipaddr_aton(const char *cp, ip_addr_t *addr); (ip6addr)->addr[3] = (ip4addr)->addr; \ (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \ (ip6addr)->addr[1] = 0; \ - (ip6addr)->addr[0] = 0; } while(0); + (ip6addr)->addr[0] = 0; \ + ip6_addr_clear_zone(ip6addr); } while(0); /** @ingroup ipaddr */ #define unmap_ipv6_mapped_ipv4(ip4addr, ip6addr) \ @@ -288,8 +293,8 @@ typedef ip4_addr_t ip_addr_t; #else /* LWIP_IPV4 */ typedef ip6_addr_t ip_addr_t; -#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } } -#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } +#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } IPADDR6_ZONE_INIT } +#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } IP6ADDR_ZONE_INIT } #define IP_IS_V4_VAL(ipaddr) 0 #define IP_IS_V6_VAL(ipaddr) 1 #define IP_IS_V4(ipaddr) 0 @@ -304,6 +309,7 @@ typedef ip6_addr_t ip_addr_t; #define ip_addr_copy(dest, src) ip6_addr_copy(dest, src) #define ip_addr_copy_from_ip6(dest, src) ip6_addr_copy(dest, src) +#define ip_addr_copy_from_ip6_packed(dest, src) ip6_addr_copy_from_packed(dest, src) #define ip_addr_set(dest, src) ip6_addr_set(dest, src) #define ip_addr_set_ipaddr(dest, src) ip6_addr_set(dest, src) #define ip_addr_set_zero(ipaddr) ip6_addr_set_zero(ipaddr) diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 99511984..7f3ecedc 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -2164,6 +2164,25 @@ #define LWIP_IPV6 0 #endif +/** + * LWIP_IPV6_SCOPES==1: Enable support for IPv6 address scopes, ensuring that + * e.g. link-local addresses are really treated as link-local. Disable this + * setting only for single-interface configurations. + */ +#if !defined LWIP_IPV6_SCOPES || defined __DOXYGEN__ +#define LWIP_IPV6_SCOPES (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_SCOPES_DEBUG==1: Perform run-time checks to verify that addresses + * are properly zoned (see ip6_zone.h on what that means) where it matters. + * Enabling this setting is highly recommended when upgrading from an existing + * installation that is not yet scope-aware; otherwise it may be too expensive. + */ +#if !defined LWIP_IPV6_SCOPES_DEBUG || defined __DOXYGEN__ +#define LWIP_IPV6_SCOPES_DEBUG 0 +#endif + /** * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. */ diff --git a/src/netif/lowpan6.c b/src/netif/lowpan6.c index 9a84cbcc..deb09fff 100644 --- a/src/netif/lowpan6.c +++ b/src/netif/lowpan6.c @@ -262,8 +262,10 @@ lowpan6_frag(struct netif *netif, struct pbuf *p, const struct ieee_802154_addr /* Point to ip6 header and align copies of src/dest addresses. */ ip6hdr = (struct ip6_hdr *)p->payload; - ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest); - ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src); + ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_dest, ip6hdr->dest); + ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, netif); + ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_src, ip6hdr->src); + ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNKNOWN, netif); /* Basic length of 6LowPAN header, set dispatch and clear fields. */ lowpan6_header_len = 2; @@ -574,6 +576,8 @@ lowpan6_set_context(u8_t idx, const ip6_addr_t * context) return ERR_ARG; } + IP6_ADDR_ZONECHECK(context); + ip6_addr_set(&lowpan6_context[idx], context); return ERR_OK; @@ -627,7 +631,8 @@ lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) #if LWIP_6LOWPAN_INFER_SHORT_ADDRESS /* Check if we can compress source address (use aligned copy) */ ip6_hdr = (struct ip6_hdr *)q->payload; - ip6_addr_set(&ip6_src, &ip6_hdr->src); + ip6_addr_copy_from_packed(ip6_src, ip6_hdr->src); + ip6_addr_assign_zone(&ip6_src, IP6_UNICAST, netif); if (lowpan6_get_address_mode(&ip6_src, &short_mac_addr) == 3) { src.addr_len = 2; src.addr[0] = short_mac_addr.addr[0]; @@ -656,7 +661,7 @@ lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr) dest.addr_len = 2; dest.addr[0] = ((u8_t *)q->payload)[38]; dest.addr[1] = ((u8_t *)q->payload)[39]; - if ((src.addr_len == 2) && (ip6_addr_netcmp(&ip6_hdr->src, &ip6_hdr->dest)) && + if ((src.addr_len == 2) && (ip6_addr_netcmp_zoneless(&ip6_hdr->src, &ip6_hdr->dest)) && (lowpan6_get_address_mode(ip6addr, &dest) == 3)) { MIB2_STATS_NETIF_INC(netif, ifoutucastpkts); return lowpan6_frag(netif, q, &src, &dest);