]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: tools/ip: v4tov6() and v6tov4() rework
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 5 Sep 2023 12:04:51 +0000 (14:04 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 21 Sep 2023 07:50:55 +0000 (09:50 +0200)
v4tov6() and v6tov4() helper function were initially implemented in
4f92d3200 ("[MEDIUM] IPv6 support for stick-tables").

However, since ceb4ac9c3 ("MEDIUM: acl: support IPv6 address matching")
support for legacy ip6 to ip4 conversion formats were added, with the
parsing logic directly performed in acl_match_ip (which later became
pat_match_ip)

The issue is that the original v6tov4() function which is used for sample
expressions handling lacks those additional formats, so we could face
inconsistencies whether we rely on ip4/ip6 conversions from an acl context
or an expression context.

To unify ip4/ip6 automatic mapping behavior, we reworked v4tov6 and v6tov4
functions so that they now behave like in pat_match_ip() function.

Note: '6to4 (RFC3056)' and 'RFC4291 ipv4 compatible address' formats are
still supported for legacy purposes despite being deprecated for a while
now.

src/tools.c

index 20d13c38414588b959e8c42ae111f4e4d2c5eae9..b26e3efee37cdc5a2230a36fd548b69fe115ee5a 100644 (file)
@@ -3327,35 +3327,47 @@ int in_net_ipv6(const void *addr, const struct in6_addr *mask, const struct in6_
        return 1;
 }
 
-/* RFC 4291 prefix */
-const char rfc4291_pfx[] = { 0x00, 0x00, 0x00, 0x00,
-                            0x00, 0x00, 0x00, 0x00,
-                            0x00, 0x00, 0xFF, 0xFF };
-
-/* Map IPv4 address on IPv6 address, as specified in RFC 3513.
+/* Map IPv4 address on IPv6 address, as specified in RFC4291
+ * "IPv4-Mapped IPv6 Address" (using the :ffff: prefix)
+ *
  * Input and output may overlap.
  */
 void v4tov6(struct in6_addr *sin6_addr, struct in_addr *sin_addr)
 {
-       struct in_addr tmp_addr;
+       uint32_t ip4_addr;
 
-       tmp_addr.s_addr = sin_addr->s_addr;
-       memcpy(sin6_addr->s6_addr, rfc4291_pfx, sizeof(rfc4291_pfx));
-       memcpy(sin6_addr->s6_addr+12, &tmp_addr.s_addr, 4);
+       ip4_addr = sin_addr->s_addr;
+       memset(&sin6_addr->s6_addr, 0, 10);
+       write_u16(&sin6_addr->s6_addr[10], htons(0xFFFF));
+       write_u32(&sin6_addr->s6_addr[12], ip4_addr);
 }
 
-/* Map IPv6 address on IPv4 address, as specified in RFC 3513.
+/* Try to convert IPv6 address to IPv4 address thanks to the
+ * following mapping methods:
+ *  - RFC4291 IPv4-Mapped IPv6 Address (prefered method)
+ *    -> ::ffff:ip:v4
+ *  - RFC4291 IPv4-Compatible IPv6 Address (deprecated, RFC3513 legacy for
+ *    "IPv6 Addresses with Embedded IPv4 Addresses)
+ *    -> ::0000:ip:v4
+ *  - 6to4 (defined in RFC3056 proposal, seems deprecated nowadays)
+ *    -> 2002:ip:v4::
  * Return true if conversion is possible and false otherwise.
  */
 int v6tov4(struct in_addr *sin_addr, struct in6_addr *sin6_addr)
 {
-       if (memcmp(sin6_addr->s6_addr, rfc4291_pfx, sizeof(rfc4291_pfx)) == 0) {
-               memcpy(&(sin_addr->s_addr), &(sin6_addr->s6_addr[12]),
-                       sizeof(struct in_addr));
-               return 1;
+       if (read_u64(&sin6_addr->s6_addr[0]) == 0 &&
+           (read_u32(&sin6_addr->s6_addr[8]) == htonl(0xFFFF) ||
+            read_u32(&sin6_addr->s6_addr[8]) == 0)) {
+               // RFC4291 ipv4 mapped or compatible ipv6 address
+               sin_addr->s_addr = read_u32(&sin6_addr->s6_addr[12]);
+       } else if (read_u16(&sin6_addr->s6_addr[0]) == htons(0x2002)) {
+               // RFC3056 6to4 address
+               sin_addr->s_addr = htonl((ntohs(read_u16(&sin6_addr->s6_addr[2])) << 16) +
+                                        ntohs(read_u16(&sin6_addr->s6_addr[4])));
        }
-
-       return 0;
+       else
+               return 0; /* unrecognized input */
+       return 1; /* mapping completed */
 }
 
 /* compare two struct sockaddr_storage, including port if <check_port> is true,