This adds some extra inany helpers for comparing an inany address to addresses of a specific family (including special addresses), and building an inany from an IPv4 address (either statically or at runtime). Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- inany.c | 17 ++-------- inany.h | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- tcp.c | 29 +++++++---------- 3 files changed, 106 insertions(+), 37 deletions(-) diff --git a/inany.c b/inany.c index c8479a75..5e391dc7 100644 --- a/inany.c +++ b/inany.c @@ -17,21 +17,8 @@ #include "siphash.h" #include "inany.h" -const union inany_addr inany_loopback4 = { - .v4mapped = { - .zero = { 0 }, - .one = { 0xff, 0xff, }, - .a4 = IN4ADDR_LOOPBACK_INIT, - }, -}; - -const union inany_addr inany_any4 = { - .v4mapped = { - .zero = { 0 }, - .one = { 0xff, 0xff, }, - .a4 = IN4ADDR_ANY_INIT, - }, -}; +const union inany_addr inany_loopback4 = INANY_INIT4(IN4ADDR_LOOPBACK_INIT); +const union inany_addr inany_any4 = INANY_INIT4(IN4ADDR_ANY_INIT); /** inany_ntop - Convert an IPv[46] address to text format * @src: IPv[46] address diff --git a/inany.h b/inany.h index 407690e2..47b66fa9 100644 --- a/inany.h +++ b/inany.h @@ -43,6 +43,17 @@ extern const union inany_addr inany_any4; #define in4addr_loopback (inany_loopback4.v4mapped.a4) #define in4addr_any (inany_any4.v4mapped.a4) +#define INANY_INIT4(a4init) { \ + .v4mapped = { \ + .zero = { 0 }, \ + .one = { 0xff, 0xff }, \ + .a4 = a4init, \ + }, \ + } + +#define inany_from_v4(a4) \ + ((union inany_addr)INANY_INIT4((a4))) + /** union sockaddr_inany - Either a sockaddr_in or a sockaddr_in6 * @sa_family: Address family, AF_INET or AF_INET6 * @sa: Plain struct sockaddr (useful to avoid casts) @@ -79,16 +90,84 @@ static inline bool inany_equals(const union inany_addr *a, return IN6_ARE_ADDR_EQUAL(&a->a6, &b->a6); } +/** inany_equals4 - Compare an IPv[46] address to an IPv4 address + * @a: IPv[46] addresses + * @b: IPv4 address + * + * Return: true if @a and @b are the same address + */ +static inline bool inany_equals4(const union inany_addr *a, + const struct in_addr *b) +{ + const struct in_addr *a4 = inany_v4(a); + + return a4 && IN4_ARE_ADDR_EQUAL(a4, b); +} + +/** inany_equals6 - Compare an IPv[46] address to an IPv6 address + * @a: IPv[46] addresses + * @b: IPv6 address + * + * Return: true if @a and @b are the same address + */ +static inline bool inany_equals6(const union inany_addr *a, + const struct in6_addr *b) +{ + return IN6_ARE_ADDR_EQUAL(&a->a6, b); +} + +/** inany_is_loopback4() - Check if address is IPv4 loopback + * @a: IPv[46] address + * + * Return: true if @a is in 127.0.0.1/8 + */ +static inline bool inany_is_loopback4(const union inany_addr *a) +{ + const struct in_addr *v4 = inany_v4(a); + + return v4 && IN4_IS_ADDR_LOOPBACK(v4); +} + +/** inany_is_loopback6() - Check if address is IPv6 loopback + * @a: IPv[46] address + * + * Return: true if @a is in ::1 + */ +static inline bool inany_is_loopback6(const union inany_addr *a) +{ + return IN6_IS_ADDR_LOOPBACK(&a->a6); +} + /** inany_is_loopback() - Check if address is loopback * @a: IPv[46] address * * Return: true if @a is either ::1 or in 127.0.0.1/8 */ static inline bool inany_is_loopback(const union inany_addr *a) +{ + return inany_is_loopback4(a) || inany_is_loopback6(a); +} + +/** inany_is_unspecified4() - Check if address is unspecified IPv4 + * @a: IPv[46] address + * + * Return: true if @a is 0.0.0.0 + */ +static inline bool inany_is_unspecified4(const union inany_addr *a) { const struct in_addr *v4 = inany_v4(a); - return IN6_IS_ADDR_LOOPBACK(&a->a6) || (v4 && IN4_IS_ADDR_LOOPBACK(v4)); + return v4 && IN4_IS_ADDR_UNSPECIFIED(v4); +} + +/** inany_is_unspecified6() - Check if address is unspecified IPv6 + * @a: IPv[46] address + * + * Return: true if @a is :: + */ +static inline bool inany_is_unspecified6(const union inany_addr *a) +{ + return IN6_IS_ADDR_UNSPECIFIED(&a->a6); } /** inany_is_unspecified() - Check if address is unspecified @@ -98,10 +177,20 @@ static inline bool inany_is_loopback(const union inany_addr *a) */ static inline bool inany_is_unspecified(const union inany_addr *a) { - const struct in_addr *v4 = inany_v4(a); + return inany_is_unspecified4(a) || inany_is_unspecified6(a); +} - return IN6_IS_ADDR_UNSPECIFIED(&a->a6) || - (v4 && IN4_IS_ADDR_UNSPECIFIED(v4)); +/* FIXME: consider handling of IPv4 link-local addresses */ + +/** inany_is_linklocal6() - Check if address is link-local IPv6 + * @a: IPv[46] address + * + * Return: true if @a is in fe80::/10 (IPv6 link local unicast) + */ +/* cppcheck-suppress unusedFunction */ +static inline bool inany_is_linklocal6(const union inany_addr *a) +{ + return IN6_IS_ADDR_LINKLOCAL(&a->a6); } /** inany_is_multicast() - Check if address is multicast or broadcast diff --git a/tcp.c b/tcp.c index a8ba5858..4512af0b 100644 --- a/tcp.c +++ b/tcp.c @@ -2687,24 +2687,17 @@ static void tcp_connect_finish(struct ctx *c, struct tcp_tap_conn *conn) */ static void tcp_snat_inbound(const struct ctx *c, union inany_addr *addr) { - struct in_addr *addr4 = inany_v4(addr); - - if (addr4) { - if (IN4_IS_ADDR_LOOPBACK(addr4) || - IN4_IS_ADDR_UNSPECIFIED(addr4) || - IN4_ARE_ADDR_EQUAL(addr4, &c->ip4.addr_seen)) - *addr4 = c->ip4.gw; - } else { - struct in6_addr *addr6 = &addr->a6; - - if (IN6_IS_ADDR_LOOPBACK(addr6) || - IN6_ARE_ADDR_EQUAL(addr6, &c->ip6.addr_seen) || - IN6_ARE_ADDR_EQUAL(addr6, &c->ip6.addr)) { - if (IN6_IS_ADDR_LINKLOCAL(&c->ip6.gw)) - *addr6 = c->ip6.gw; - else - *addr6 = c->ip6.addr_ll; - } + if (inany_is_loopback4(addr) || + inany_is_unspecified4(addr) || + inany_equals4(addr, &c->ip4.addr_seen)) { + *addr = inany_from_v4(c->ip4.gw); + } else if (inany_is_loopback6(addr) || + inany_equals6(addr, &c->ip6.addr_seen) || + inany_equals6(addr, &c->ip6.addr)) { + if (IN6_IS_ADDR_LINKLOCAL(&c->ip6.gw)) + addr->a6 = c->ip6.gw; + else + addr->a6 = c->ip6.addr_ll; } } -- 2.45.1