diff --git a/mongoose.c b/mongoose.c index f9e530b172..5eae733ef0 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5214,27 +5214,44 @@ static void tx_ndp_rs(struct mg_tcpip_if *ifp, uint64_t *ip_dst, uint8_t *mac) { MG_DEBUG(("NDP Router Solicitation sent")); } +static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) { + uint8_t full = len / 8; + uint8_t rem = len % 8; + if (full > 0) memcpy(dst, src, full); + if (rem > 0) { + uint8_t mask = (uint8_t) (0xFF << (8 - rem)); + dst[full] = src[full] & mask; + } +} + static bool fill_global(uint64_t *ip6, uint8_t *prefix, uint8_t prefix_len, uint8_t *mac) { uint8_t full = prefix_len / 8; uint8_t rem = prefix_len % 8; - if (full >= 8 && rem != 0) { + if (full > 8 || (full == 8 && rem != 0)) { MG_ERROR(("Prefix length > 64, UNSUPPORTED")); return false; } else if (full == 8 && rem == 0) { ip6gen((uint8_t *) ip6, prefix, mac); } else { - ip6[0] = ip6[1] = 0; // already zeroed before firing RS... - if (full) memcpy(ip6, prefix, full); - if (rem) { - uint8_t mask = (uint8_t) (0xFF << (8 - rem)); - ((uint8_t *) ip6)[full] = prefix[full] & mask; - } + ip6[0] = ip6[1] = 0; + fill_prefix((uint8_t *) ip6, prefix, prefix_len); meui64(((uint8_t *) &ip6[1]), mac); // RFC-4291 2.5.4, 2.5.1 } return true; } +static bool match_prefix(uint8_t *new, uint8_t *cur, uint8_t len) { + uint8_t full = len / 8; + uint8_t rem = len % 8; + if (full > 0 && memcmp(cur, new, full) != 0) return false; + if (rem > 0) { + uint8_t mask = (uint8_t) (0xFF << (8 - rem)); + if (cur[full] != (new[full] & mask)) return false; + } + return true; +} + // Router Advertisement, 4.2 static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (pkt->pay.len < 12) return; @@ -5270,15 +5287,14 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint8_t *prefix = opts + 16; // TODO (robertc2000): handle prefix options if necessary - (void) prefix_len; (void) pfx_flags; (void) valid; (void) pref_lifetime; - (void) prefix; - // fill prefix length and global - ifp->prefix_len = prefix_len; + // fill prefix and global if (!fill_global(ifp->ip6, prefix, prefix_len, ifp->mac)) return; + ifp->prefix_len = prefix_len; + fill_prefix(ifp->prefix, prefix, prefix_len); } opts += length; opt_left -= length; @@ -5345,9 +5361,11 @@ static uint8_t *get_return_mac(struct mg_tcpip_if *ifp, struct mg_addr *rem, bool is_udp, struct pkt *pkt) { #if MG_ENABLE_IPV6 if (rem->is_ip6) { - if (is_udp && MG_IP6MATCH(rem->addr.ip6, ip6_allnodes.u)) // local broadcast + if (is_udp && + MG_IP6MATCH(rem->addr.ip6, ip6_allnodes.u)) // local broadcast return (uint8_t *) ip6mac_allnodes; - if (rem->addr.ip6[0] == ifp->ip6[0]) // TODO(): HANDLE PREFIX *** + if (rem->addr.ip6[0] == ifp->ip6ll[0] || + match_prefix((uint8_t *) rem->addr.ip6, ifp->prefix, ifp->prefix_len)) return pkt->eth->src; // we're on the same LAN, get MAC from frame if (is_udp && *((uint8_t *) rem->addr.ip6) == 0xFF) // multicast { @@ -5531,7 +5549,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, if ((mac = get_return_mac(lsn->mgr->ifp, &c->rem, false, pkt)) == NULL) { free(c); // safety net for lousy networks, not actually needed return NULL; // as path has already been checked at SYN (sending SYN+ACK) - } + } memcpy(s->mac, mac, sizeof(s->mac)); settmout(c, MIP_TTYPE_KEEPALIVE); MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem)); @@ -6348,15 +6366,16 @@ void mg_connect_resolved(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); memcpy(s->mac, ip6mac_allnodes, sizeof(s->mac)); mac_resolved(c); - } else if (c->rem.addr.ip6[0] == ifp->ip6[0] && // TODO(): HANDLE PREFIX *** - !MG_IP6MATCH(c->rem.addr.ip6, - ifp->gw6)) { // skip if gw (onstate6change -> NS) - // If we're in the same LAN, fire a Neighbor Solicitation - MG_DEBUG(("%lu NS lookup...", c->id)); + } else if (match_prefix((uint8_t *) c->rem.addr.ip6, ifp->prefix, + ifp->prefix_len) // same global LAN + || (c->rem.addr.ip6[0] == ifp->ip6ll[0] // same local LAN + && !MG_IP6MATCH(c->rem.addr.ip6, ifp->gw6))) { // and not gw + MG_DEBUG(("%lu NS lookup...", c->id)); // fire a Neighbor Solicitation tx_ndp_ns(ifp, c->rem.addr.ip6, ifp->mac); settmout(c, MIP_TTYPE_ARP); c->is_arplooking = 1; - } else if (c->is_udp && *((uint8_t *) c->rem.addr.ip6) == 0xFF) { // multicast + } else if (c->is_udp && + *((uint8_t *) c->rem.addr.ip6) == 0xFF) { // multicast struct connstate *s = (struct connstate *) (c + 1); ip6_mcastmac(s->mac, c->rem.addr.ip6); mac_resolved(c); diff --git a/mongoose.h b/mongoose.h index f70ff72c83..3bb506aaa0 100644 --- a/mongoose.h +++ b/mongoose.h @@ -3202,6 +3202,7 @@ struct mg_tcpip_if { #define MG_TCPIP_MTU_DEFAULT 1500 #if MG_ENABLE_IPV6 uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses + uint8_t prefix[8]; // IPv6 global address prefix uint8_t prefix_len; // Prefix length uint64_t gw6[2]; // Default gateway bool enable_slaac; // Enable IPv6 address autoconfiguration diff --git a/src/net_builtin.c b/src/net_builtin.c index b48ea6cc11..4df91ce80d 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -822,27 +822,44 @@ static void tx_ndp_rs(struct mg_tcpip_if *ifp, uint64_t *ip_dst, uint8_t *mac) { MG_DEBUG(("NDP Router Solicitation sent")); } +static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) { + uint8_t full = len / 8; + uint8_t rem = len % 8; + if (full > 0) memcpy(dst, src, full); + if (rem > 0) { + uint8_t mask = (uint8_t) (0xFF << (8 - rem)); + dst[full] = src[full] & mask; + } +} + static bool fill_global(uint64_t *ip6, uint8_t *prefix, uint8_t prefix_len, uint8_t *mac) { uint8_t full = prefix_len / 8; uint8_t rem = prefix_len % 8; - if (full >= 8 && rem != 0) { + if (full > 8 || (full == 8 && rem != 0)) { MG_ERROR(("Prefix length > 64, UNSUPPORTED")); return false; } else if (full == 8 && rem == 0) { ip6gen((uint8_t *) ip6, prefix, mac); } else { - ip6[0] = ip6[1] = 0; // already zeroed before firing RS... - if (full) memcpy(ip6, prefix, full); - if (rem) { - uint8_t mask = (uint8_t) (0xFF << (8 - rem)); - ((uint8_t *) ip6)[full] = prefix[full] & mask; - } + ip6[0] = ip6[1] = 0; + fill_prefix((uint8_t *) ip6, prefix, prefix_len); meui64(((uint8_t *) &ip6[1]), mac); // RFC-4291 2.5.4, 2.5.1 } return true; } +static bool match_prefix(uint8_t *new, uint8_t *cur, uint8_t len) { + uint8_t full = len / 8; + uint8_t rem = len % 8; + if (full > 0 && memcmp(cur, new, full) != 0) return false; + if (rem > 0) { + uint8_t mask = (uint8_t) (0xFF << (8 - rem)); + if (cur[full] != (new[full] & mask)) return false; + } + return true; +} + // Router Advertisement, 4.2 static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { if (pkt->pay.len < 12) return; @@ -878,15 +895,14 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) { uint8_t *prefix = opts + 16; // TODO (robertc2000): handle prefix options if necessary - (void) prefix_len; (void) pfx_flags; (void) valid; (void) pref_lifetime; - (void) prefix; - // fill prefix length and global - ifp->prefix_len = prefix_len; + // fill prefix and global if (!fill_global(ifp->ip6, prefix, prefix_len, ifp->mac)) return; + ifp->prefix_len = prefix_len; + fill_prefix(ifp->prefix, prefix, prefix_len); } opts += length; opt_left -= length; @@ -953,9 +969,11 @@ static uint8_t *get_return_mac(struct mg_tcpip_if *ifp, struct mg_addr *rem, bool is_udp, struct pkt *pkt) { #if MG_ENABLE_IPV6 if (rem->is_ip6) { - if (is_udp && MG_IP6MATCH(rem->addr.ip6, ip6_allnodes.u)) // local broadcast + if (is_udp && + MG_IP6MATCH(rem->addr.ip6, ip6_allnodes.u)) // local broadcast return (uint8_t *) ip6mac_allnodes; - if (rem->addr.ip6[0] == ifp->ip6[0]) // TODO(): HANDLE PREFIX *** + if (rem->addr.ip6[0] == ifp->ip6ll[0] || + match_prefix((uint8_t *) rem->addr.ip6, ifp->prefix, ifp->prefix_len)) return pkt->eth->src; // we're on the same LAN, get MAC from frame if (is_udp && *((uint8_t *) rem->addr.ip6) == 0xFF) // multicast { @@ -1139,7 +1157,7 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn, if ((mac = get_return_mac(lsn->mgr->ifp, &c->rem, false, pkt)) == NULL) { free(c); // safety net for lousy networks, not actually needed return NULL; // as path has already been checked at SYN (sending SYN+ACK) - } + } memcpy(s->mac, mac, sizeof(s->mac)); settmout(c, MIP_TTYPE_KEEPALIVE); MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem)); @@ -1956,15 +1974,16 @@ void mg_connect_resolved(struct mg_connection *c) { struct connstate *s = (struct connstate *) (c + 1); memcpy(s->mac, ip6mac_allnodes, sizeof(s->mac)); mac_resolved(c); - } else if (c->rem.addr.ip6[0] == ifp->ip6[0] && // TODO(): HANDLE PREFIX *** - !MG_IP6MATCH(c->rem.addr.ip6, - ifp->gw6)) { // skip if gw (onstate6change -> NS) - // If we're in the same LAN, fire a Neighbor Solicitation - MG_DEBUG(("%lu NS lookup...", c->id)); + } else if (match_prefix((uint8_t *) c->rem.addr.ip6, ifp->prefix, + ifp->prefix_len) // same global LAN + || (c->rem.addr.ip6[0] == ifp->ip6ll[0] // same local LAN + && !MG_IP6MATCH(c->rem.addr.ip6, ifp->gw6))) { // and not gw + MG_DEBUG(("%lu NS lookup...", c->id)); // fire a Neighbor Solicitation tx_ndp_ns(ifp, c->rem.addr.ip6, ifp->mac); settmout(c, MIP_TTYPE_ARP); c->is_arplooking = 1; - } else if (c->is_udp && *((uint8_t *) c->rem.addr.ip6) == 0xFF) { // multicast + } else if (c->is_udp && + *((uint8_t *) c->rem.addr.ip6) == 0xFF) { // multicast struct connstate *s = (struct connstate *) (c + 1); ip6_mcastmac(s->mac, c->rem.addr.ip6); mac_resolved(c); diff --git a/src/net_builtin.h b/src/net_builtin.h index 1992dc6879..f2b782b3e4 100644 --- a/src/net_builtin.h +++ b/src/net_builtin.h @@ -57,6 +57,7 @@ struct mg_tcpip_if { #define MG_TCPIP_MTU_DEFAULT 1500 #if MG_ENABLE_IPV6 uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses + uint8_t prefix[8]; // IPv6 global address prefix uint8_t prefix_len; // Prefix length uint64_t gw6[2]; // Default gateway bool enable_slaac; // Enable IPv6 address autoconfiguration diff --git a/test/mip_test.c b/test/mip_test.c index 0f18447911..a7b284993e 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -251,6 +251,8 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp, #if MG_ENABLE_IPV6 if (ipp->ip6 != NULL) { mif->ip6[0] = 1; + mif->prefix[0] = 1; + mif->prefix_len = 64; mif->gw6[0] = 1; mif->gw6_ready = true; mif->state = MG_TCPIP_STATE_READY; // so DHCP stops