*/
+
+/* Produce an IPv6 address following the 6bed4 structures.
+ * - The top half is taken from v6listen
+ * - The bottom contains IPv4 address and port from v4name
+ * - The last 14 bits are filled with the lanip parameter
+ */
+void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
+ memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
+ dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
+ dst_ip6->s6_addr16 [6] = v4name.sin_port;
+ dst_ip6->s6_addr [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
+ | ((lanip >> 8) & 0x3f);
+ dst_ip6->s6_addr [15] = (lanip & 0xff);
+ dst_ip6->s6_addr [8] &= 0xfc;
+}
+
/* Look for an entry in the 50ms-cycled Neighbor Discovery queue.
* Match the target address. Return the entry found, or NULL.
*/
size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
v6icmp6data [optidx++] = 3; // Type
v6icmp6data [optidx++] = 4; // Length
- v6icmp6data [optidx++] = 64; // This is a /64 prefix
+ v6icmp6data [optidx++] = 114; // This is a /114 prefix
#ifndef COMPENSATE_FOR_AUTOCONF
v6icmp6data [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
#else
memset (v6icmp6data + optidx, 0, 4);
optidx += 4;
// Reserved2=0
- memcpy (v6icmp6data + optidx + 0, &v6listen, 8);
- memset (v6icmp6data + optidx + 8, 0, 8);
+ addr_6bed4 ((struct in6_addr *) (v6icmp6data + optidx), 0);
// Set IPv6 prefix
optidx += 16;
return optidx;
/*
- * Test if the provided IPv6 address matches the prefix used for 6bed4.
- */
-static inline bool prefix_6bed4 (struct in6_addr *ip6) {
- return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
-}
-
-
-/*
* Validate the originator's IPv6 address. It should match the
* UDP/IPv4 coordinates of the receiving 6bed4 socket. Also,
- * the /64 prefix must match that of v6listen.
+ * the /64 prefix (but not the /114 prefix!) must match v6listen.
*/
-bool validate_originator (struct sockaddr_in *sin, struct in6_addr *ip6) {
- if ((sin->sin_addr.s_addr == v4peer.sin_addr.s_addr) && (sin->sin_port == v4peer.sin_port)) {
+bool validate_originator (struct in6_addr *ip6) {
+ uint32_t addr;
+ //
+ // Communication from the configured router is welcomed
+ //TODO// Why should we trust the ip6 address at face value?
+ if ((v4name.sin_addr.s_addr == v4peer.sin_addr.s_addr)
+ && (v4name.sin_port == v4peer.sin_port)) {
return true;
}
- uint16_t port = ntohs (sin->sin_port);
- uint32_t addr = ntohl (sin->sin_addr.s_addr);
-if (memcmp (ip6, v6listen_linklocal, 8) != 0)
- if (!prefix_6bed4 (ip6)) {
- return false;
- }
- if ((port & 0xff) != (ip6->s6_addr [8] ^ 0x02)) {
- return false;
- }
- if ((port >> 8) != ip6->s6_addr [9]) {
- return false;
+ //
+ // Require non-local top halves to match our v6listen_linklocal address
+ //TODO// When do we receive local top halves?
+ //TODO// We should really be more flexible and allow fallback addrs
+ if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
+ if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
+ return false;
+ }
}
- if ((addr >> 24) != ip6->s6_addr [10]) {
+ //
+ // Require the sender port to appear in its IPv6 address
+ if (v4name.sin_port != ip6->s6_addr16 [6]) {
return false;
}
- if ((addr & 0x00ffffff) != (htonl (ip6->s6_addr32 [3]) & 0x00ffffff)) {
+ //
+ // Require the sender address to appear in its IPv6 address
+ addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
+ addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
+ if (addr != ntohl (v4name.sin_addr.s_addr)) {
return false;
}
+ //
+ // We passed with flying colours
return true;
}
return; /* out of packet length */
} else if (v4v6icmpdata [rdofs + 3] & 0xc0 != 0xc0) {
/* no on-link autoconfig prefix */
- } else if (v4v6icmpdata [rdofs + 2] != 64) {
+ } else if (v4v6icmpdata [rdofs + 2] != 114) {
+ /* not a /114 prefix, so no 6bed4 offer */
return;
} else {
destprefix = &v4v6icmpdata [rdofs + 16];
rdofs += (v4v6icmpdata [rdofs + 1] << 3);
}
if (destprefix) {
- memcpy (v6listen.s6_addr + 0, destprefix, 8);
- memcpy (v6listen.s6_addr + 8, v4dst6->s6_addr + 8, 8);
- memcpy (v6listen_linklocal_complete, v4dst6, 16);
- v6lladdr [0] = v6listen_linklocal_complete [8] ^ 0x02;
- v6lladdr [1] = v6listen_linklocal_complete [9];
- v6lladdr [2] = v6listen_linklocal_complete [10];
- v6lladdr [3] = v6listen_linklocal_complete [13];
- v6lladdr [4] = v6listen_linklocal_complete [14];
- v6lladdr [5] = v6listen_linklocal_complete [15];
+ memcpy (v6listen.s6_addr + 0, destprefix, 14);
+ v6listen.s6_addr [14] &= 0xc0;
+ v6listen.s6_addr [15] = 0x00;
+ memcpy (v6listen_linklocal_complete+0,
+ v6listen_linklocal, 8);
+ memcpy (v6listen_linklocal_complete+8,
+ v6listen.s6_addr+8, 8);
+ memcpy (v6lladdr, v6listen_linklocal_complete+8, 8);
+ v6lladdr [0] &= 0xfc;
+ v6lladdr [0] |= (v6listen_linklocal_complete [14] >> 6);
inet_ntop (AF_INET6,
&v6listen,
v6prefix,
if ((v4data [0] & 0xf0) != 0x60) {
return; /* not an IPv6 packet, drop */
}
- if (!validate_originator (&v4name, v4src6)) {
+ if (!validate_originator (v4src6)) {
return; /* source appears fake, drop */
}
/*
0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x02,
};
-void addr_6bed4(struct in6_addr *dst_ip6, uint8_t *s_addr, int sin_port, int lanip) {
-
- dst_ip6->s6_addr [8] = s_addr [0] & 0xfc;
- memcpy (&dst_ip6->s6_addr [9], s_addr + 1, 3);
- dst_ip6->s6_addr [12] = sin_port >> 8;
- dst_ip6->s6_addr [13] = sin_port & 0xff;
- dst_ip6->s6_addr [14] = (s_addr [0] & 0x03) << 6 | lanip >> 8;
- dst_ip6->s6_addr [15] = lanip & 0xff;
-}
/*
*
*/
+/* Produce an IPv6 address following the 6bed4 structures.
+ * - The top half is taken from v6listen
+ * - The bottom contains IPv4 address and port from v4name
+ * - The last 14 bits are filled with the lanip parameter
+ */
+void addr_6bed4 (struct in6_addr *dst_ip6, uint16_t lanip) {
+ memcpy (&dst_ip6->s6_addr [0], &v6listen, 8);
+ dst_ip6->s6_addr32 [2] = v4name.sin_addr.s_addr;
+ dst_ip6->s6_addr16 [6] = v4name.sin_port;
+ dst_ip6->s6_addr [14] = ((dst_ip6->s6_addr [8] & 0x03) << 6)
+ | ((lanip >> 8) & 0x3f);
+ dst_ip6->s6_addr [15] = (lanip & 0xff);
+ dst_ip6->s6_addr [8] &= 0xfc;
+}
+
/* Calculate the ICMPv6 checksum field
*/
uint16_t icmp6_checksum (size_t payloadlen) {
}
v4v6plen = htons (icmp6bodylen + 4);
memcpy (v4dst6,
- (v4src6->s6_addr16 [0]) ? v4src6 : allnodes_linklocal_address,
+ (v4src6->s6_addr16 [0])
+ ? (uint8_t *) v4src6
+ : allnodes_linklocal_address,
16);
memcpy (v4src6, router_linklocal_address, 16);
v4v6icmpcksum = icmp6_checksum (ntohs (v4v6plen));
size_t icmp6_prefix (size_t optidx, uint8_t endlife) {
v4v6icmpdata [optidx++] = 3; // Type
v4v6icmpdata [optidx++] = 4; // Length
- v4v6icmpdata [optidx++] = 64; // This is a /64 prefix
+ v4v6icmpdata [optidx++] = 114; // This is a /114 prefix
v4v6icmpdata [optidx++] = 0xc0; // L=1, A=1, Reserved1=0
memset (v4v6icmpdata + optidx, endlife? 0x00: 0xff, 8);
optidx += 8;
memset (v4v6icmpdata + optidx, 0, 4);
optidx += 4;
// Reserved2=0
- memcpy (v4v6icmpdata + optidx + 0, &v6listen, 8);
- memset (v4v6icmpdata + optidx + 8, 0, 8);
+ addr_6bed4 ((struct in6_addr *) (v4v6icmpdata + optidx), 1);
+ // Set IPv6 prefix
optidx += 16;
return optidx;
}
/*
* Test if the provided IPv6 address matches the prefix used for 6bed4.
+ *TODO: This is oversimplistic, it only cares for the Hetzner /64
*/
static inline bool is_6bed4 (struct in6_addr *ip6) {
return memcmp (&v6listen, ip6->s6_addr, 8) == 0;
/*
* Validate the originator's IPv6 address. It should match the
* UDP/IPv4 coordinates of the receiving 6bed4 socket. Also,
- * the /64 prefix must match that of v6listen.
+ * the /64 prefix (but not the /114 prefix!) must match v6listen.
*/
-bool validate_originator (struct sockaddr_in *sin, struct in6_addr *ip6) {
- uint16_t port = ntohs (sin->sin_port);
- uint32_t addr = ntohl (sin->sin_addr.s_addr);
-
- if (!is_6bed4 (ip6)) {
- return false;
- }
- if ((port & 0xff) != ip6->s6_addr [13]) {
- return false;
+bool validate_originator (struct in6_addr *ip6) {
+ uint32_t addr;
+ //
+ // Require non-local top halves to match our v6listen_linklocal address
+ // We will enforce our own fallback address (and fc64:<port>)
+ if (memcmp (ip6, v6listen_linklocal, 8) != 0) {
+ if (memcmp (&v6listen, ip6->s6_addr, 8) != 0) {
+ return false;
+ }
}
- if ((port >> 8) != ip6->s6_addr [12]) {
+ //
+ // Require the sender port to appear in its IPv6 address
+ if (v4name.sin_port != ip6->s6_addr16 [6]) {
return false;
}
- if (addr != ((ntohl(ip6->s6_addr32 [2]) & 0xfcffffff) | (((uint32_t) ip6->s6_addr [14] & 0xc0) << 18))) {
+ //
+ // Require the sender address to appear in its IPv6 address
+ addr = ntohl (ip6->s6_addr32 [2]) & 0xfcffffff;
+ addr |= ((uint32_t) (ip6->s6_addr [14] & 0xc0)) << (24-6);
+ if (addr != ntohl (v4name.sin_addr.s_addr)) {
return false;
}
+ //
+ // We passed with flying colours
return true;
}
// Retrans Timer: max
writepos += 2+4+4;
writepos = icmp6_prefix (writepos, 0);
- //TODO:DEPRECATED// writepos = icmp6_dest_linkaddr (writepos);
- memcpy (&observed, v6listen_linklocal, 8);
- addr_6bed4(&observed, (uint8_t *) &v4name.sin_addr.s_addr, ntohs(v4name.sin_port), 1);
icmp6_reply (writepos);
}
case ND_NEIGHBOR_SOLICIT:
//
// Validate Neigbour Solicitation
- if (!validate_originator (&v4name, v4src6)) {
+ if (!validate_originator (v4src6)) {
break; /* bad source address, drop */
}
if ((v4ngbcmdlen != sizeof (struct ip6_hdr) + 24) &&
// Plain Unicast
if (is_local_override (v4dst6)) {
handle_4to6_plain_unicast (buflen);
- } else if (validate_originator (&v4name, v4src6)) {
+ } else if (validate_originator (v4src6)) {
if (v4v6hoplimit-- <= 1) {
return;
}
lladdr_6bed4 [0] = UDP_PORT_6BED4 & 0xff;
lladdr_6bed4 [1] = UDP_PORT_6BED4 >> 8;
memcpy (lladdr_6bed4 + 2, (uint8_t *) &v4name.sin_addr, 4);
- memcpy (&v6listen_complete, &v6listen, 8);
- addr_6bed4(&v6listen_complete, lladdr_6bed4 + 2, ntohs(v4name.sin_port), 0);
+ addr_6bed4 (&v6listen_complete, 0);
memcpy (v6listen_linklocal_complete, v6listen_linklocal, 8);
memcpy (v6listen_linklocal_complete + 8, &v6listen_complete.s6_addr [8], 8);