Add support for prefix, link local and global in same net

This commit is contained in:
Sergio R. Caprile committed 2025-12-08 16:05:32 -03:00
1 parent a42271a088
commit b0109c3319
5 files changed
+82 -40

No files matched your search

+39 -20
View File
@@ -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);
+1
View File
@@ -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
+39 -20
View File
@@ -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);
+1
View File
@@ -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
+2
View File
@@ -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