From: Aurelien DARRAGON Date: Tue, 5 Sep 2023 12:04:51 +0000 (+0200) Subject: MEDIUM: tools/ip: v4tov6() and v6tov4() rework X-Git-Tag: v2.9-dev6~16 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72514a44;p=thirdparty%2Fhaproxy.git MEDIUM: tools/ip: v4tov6() and v6tov4() rework 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. --- diff --git a/src/tools.c b/src/tools.c index 20d13c3841..b26e3efee3 100644 --- a/src/tools.c +++ b/src/tools.c @@ -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 is true,