diff --git a/inany.h b/inany.h index 03e3e3f..52d43f2 100644 --- a/inany.h +++ b/inany.h @@ -30,11 +30,11 @@ union inany_addr { * * Return: IPv4 address if @addr is IPv4, NULL otherwise */ -static inline const struct in_addr *inany_v4(const union inany_addr *addr) +static inline struct in_addr *inany_v4(const union inany_addr *addr) { if (!IN6_IS_ADDR_V4MAPPED(&addr->a6)) return NULL; - return &addr->v4mapped.a4; + return (struct in_addr *)&addr->v4mapped.a4; } /** inany_equals - Compare two IPv[46] addresses @@ -66,3 +66,29 @@ static inline void inany_from_af(union inany_addr *aa, int af, const void *addr) assert(0); } } + +/** inany_from_sockaddr - Extract IPv[46] address and port number from sockaddr + * @aa: Pointer to store IPv[46] address + * @port: Pointer to store port number, host order + * @addr: struct sockaddr_in (IPv4) or struct sockaddr_in6 (IPv6) + */ +static inline void inany_from_sockaddr(union inany_addr *aa, in_port_t *port, + const void *addr) +{ + const struct sockaddr *sa = (const struct sockaddr *)addr; + + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa; + + inany_from_af(aa, AF_INET6, &sa6->sin6_addr); + *port = ntohs(sa6->sin6_port); + } else if (sa->sa_family == AF_INET) { + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + + inany_from_af(aa, AF_INET, &sa4->sin_addr); + *port = ntohs(sa4->sin_port); + } else { + /* Not valid to call with other address families */ + assert(0); + } +} diff --git a/tcp.c b/tcp.c index 7d5ac6c..59c1a93 100644 --- a/tcp.c +++ b/tcp.c @@ -2724,6 +2724,34 @@ static void tcp_connect_finish(struct ctx *c, struct tcp_tap_conn *conn) conn_flag(c, conn, ACK_FROM_TAP_DUE); } +/** + * tcp_snat_inbound() - Translate source address for inbound data if needed + * @c: Execution context + * @addr: Source address of inbound packet/connection + */ +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; + } + } +} + /** * tcp_tap_conn_from_sock() - Initialize state for non-spliced connection * @c: Execution context @@ -2744,43 +2772,10 @@ static void tcp_tap_conn_from_sock(struct ctx *c, union epoll_ref ref, conn->ws_to_tap = conn->ws_from_tap = 0; conn_event(c, conn, SOCK_ACCEPTED); - if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 sa6; + inany_from_sockaddr(&conn->addr, &conn->sock_port, sa); + conn->tap_port = ref.r.p.tcp.tcp.index; - memcpy(&sa6, sa, sizeof(sa6)); - - if (IN6_IS_ADDR_LOOPBACK(&sa6.sin6_addr) || - IN6_ARE_ADDR_EQUAL(&sa6.sin6_addr, &c->ip6.addr_seen) || - IN6_ARE_ADDR_EQUAL(&sa6.sin6_addr, &c->ip6.addr)) { - struct in6_addr *src; - - if (IN6_IS_ADDR_LINKLOCAL(&c->ip6.gw)) - src = &c->ip6.gw; - else - src = &c->ip6.addr_ll; - - memcpy(&sa6.sin6_addr, src, sizeof(*src)); - } - - inany_from_af(&conn->addr, AF_INET6, &sa6.sin6_addr); - - conn->sock_port = ntohs(sa6.sin6_port); - conn->tap_port = ref.r.p.tcp.tcp.index; - } else { - struct sockaddr_in sa4; - - memcpy(&sa4, sa, sizeof(sa4)); - - if (IN4_IS_ADDR_LOOPBACK(&sa4.sin_addr) || - IN4_IS_ADDR_UNSPECIFIED(&sa4.sin_addr) || - IN4_ARE_ADDR_EQUAL(&sa4.sin_addr, &c->ip4.addr_seen)) - sa4.sin_addr = c->ip4.gw; - - inany_from_af(&conn->addr, AF_INET, &sa4.sin_addr); - - conn->sock_port = ntohs(sa4.sin_port); - conn->tap_port = ref.r.p.tcp.tcp.index; - } + tcp_snat_inbound(c, &conn->addr); tcp_seq_init(c, conn, now); tcp_hash_insert(c, conn);