diff options
-rw-r--r-- | tcp.c | 183 | ||||
-rw-r--r-- | util.h | 3 |
2 files changed, 117 insertions, 69 deletions
@@ -275,6 +275,7 @@ #include <netinet/in.h> #include <netinet/ip.h> #include <stdint.h> +#include <stdbool.h> #include <stddef.h> #include <string.h> #include <sys/epoll.h> @@ -2906,8 +2907,8 @@ static void tcp_conn_from_sock(struct ctx *c, union epoll_ref ref, memset(&conn->a.a4.zero, 0, sizeof(conn->a.a4.zero)); memset(&conn->a.a4.one, 0xff, sizeof(conn->a.a4.one)); - if (s_addr >> IN_CLASSA_NSHIFT == IN_LOOPBACKNET || - s_addr == INADDR_ANY || htonl(s_addr) == c->ip4.addr_seen) + if (IPV4_IS_LOOPBACK(s_addr) || s_addr == INADDR_ANY || + htonl(s_addr) == c->ip4.addr_seen) s_addr = ntohl(c->ip4.gw); s_addr = htonl(s_addr); @@ -3073,110 +3074,154 @@ void tcp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events, } /** - * tcp_sock_init() - Initialise listening sockets for a given port + * tcp_sock_init4() - Initialise listening sockets for a given IPv4 port * @c: Execution context * @ns: In pasta mode, if set, bind with loopback address in namespace - * @af: Address family to select a specific IP version, or AF_UNSPEC * @addr: Pointer to address for binding, NULL if not configured * @ifname: Name of interface to bind to, NULL if not configured * @port: Port, host order */ -void tcp_sock_init(const struct ctx *c, int ns, sa_family_t af, - const void *addr, const char *ifname, in_port_t port) +static void tcp_sock_init4(const struct ctx *c, int ns, const in_addr_t *addr, + const char *ifname, in_port_t port) { union tcp_epoll_ref tref = { .tcp.listen = 1, .tcp.outbound = ns }; - const void *bind_addr; + bool spliced = false, tap = true; int s; + if (c->mode == MODE_PASTA) { + spliced = !addr || + ntohl(*addr) == INADDR_ANY || + IPV4_IS_LOOPBACK(ntohl(*addr)); + + if (!addr) + addr = &c->ip4.addr; + + tap = !ns && !IPV4_IS_LOOPBACK(ntohl(*addr)); + } + if (ns) tref.tcp.index = (in_port_t)(port + c->tcp.fwd_out.delta[port]); else tref.tcp.index = (in_port_t)(port + c->tcp.fwd_in.delta[port]); - if (af == AF_INET || af == AF_UNSPEC) { - if (!addr && c->mode == MODE_PASTA) - bind_addr = &c->ip4.addr; + if (tap) { + s = sock_l4(c, AF_INET, IPPROTO_TCP, addr, ifname, port, + tref.u32); + if (s >= 0) + tcp_sock_set_bufsize(c, s); else - bind_addr = addr; + s = -1; - tref.tcp.v6 = 0; - tref.tcp.splice = 0; + if (c->tcp.fwd_in.mode == FWD_AUTO) + tcp_sock_init_ext[port][V4] = s; + } - if (!ns) { - s = sock_l4(c, AF_INET, IPPROTO_TCP, bind_addr, ifname, - port, tref.u32); - if (s >= 0) - tcp_sock_set_bufsize(c, s); - else - s = -1; + if (spliced) { + tref.tcp.splice = 1; - if (c->tcp.fwd_in.mode == FWD_AUTO) - tcp_sock_init_ext[port][V4] = s; - } + addr = &(uint32_t){ htonl(INADDR_LOOPBACK) }; - if (c->mode == MODE_PASTA) { - bind_addr = &(uint32_t){ htonl(INADDR_LOOPBACK) }; + s = sock_l4(c, AF_INET, IPPROTO_TCP, addr, ifname, port, + tref.u32); + if (s >= 0) + tcp_sock_set_bufsize(c, s); + else + s = -1; - tref.tcp.splice = 1; - s = sock_l4(c, AF_INET, IPPROTO_TCP, bind_addr, ifname, - port, tref.u32); - if (s >= 0) - tcp_sock_set_bufsize(c, s); + if (c->tcp.fwd_out.mode == FWD_AUTO) { + if (ns) + tcp_sock_ns[port][V4] = s; else - s = -1; - - if (c->tcp.fwd_out.mode == FWD_AUTO) { - if (ns) - tcp_sock_ns[port][V4] = s; - else - tcp_sock_init_lo[port][V4] = s; - } + tcp_sock_init_lo[port][V4] = s; } } +} + +/** + * tcp_sock_init6() - Initialise listening sockets for a given IPv6 port + * @c: Execution context + * @ns: In pasta mode, if set, bind with loopback address in namespace + * @addr: Pointer to address for binding, NULL if not configured + * @ifname: Name of interface to bind to, NULL if not configured + * @port: Port, host order + */ +static void tcp_sock_init6(const struct ctx *c, int ns, + const struct in6_addr *addr, const char *ifname, + in_port_t port) +{ + union tcp_epoll_ref tref = { .tcp.listen = 1, .tcp.outbound = ns, + .tcp.v6 = 1 }; + bool spliced = false, tap = true; + int s; + + if (c->mode == MODE_PASTA) { + spliced = !addr || + IN6_IS_ADDR_UNSPECIFIED(addr) || + IN6_IS_ADDR_LOOPBACK(addr); + + if (!addr) + addr = &c->ip6.addr; + + tap = !ns && !IN6_IS_ADDR_LOOPBACK(addr); + } + + if (ns) + tref.tcp.index = (in_port_t)(port + c->tcp.fwd_out.delta[port]); + else + tref.tcp.index = (in_port_t)(port + c->tcp.fwd_in.delta[port]); - if (af == AF_INET6 || af == AF_UNSPEC) { - if (!addr && c->mode == MODE_PASTA) - bind_addr = &c->ip6.addr; + if (tap) { + s = sock_l4(c, AF_INET6, IPPROTO_TCP, addr, ifname, port, + tref.u32); + if (s >= 0) + tcp_sock_set_bufsize(c, s); else - bind_addr = addr; + s = -1; - tref.tcp.v6 = 1; + if (c->tcp.fwd_in.mode == FWD_AUTO) + tcp_sock_init_ext[port][V6] = s; + } - tref.tcp.splice = 0; - if (!ns) { - s = sock_l4(c, AF_INET6, IPPROTO_TCP, bind_addr, ifname, - port, tref.u32); - if (s >= 0) - tcp_sock_set_bufsize(c, s); - else - s = -1; + if (spliced) { + tref.tcp.splice = 1; - if (c->tcp.fwd_in.mode == FWD_AUTO) - tcp_sock_init_ext[port][V6] = s; - } + addr = &in6addr_loopback; - if (c->mode == MODE_PASTA) { - bind_addr = &in6addr_loopback; + s = sock_l4(c, AF_INET6, IPPROTO_TCP, addr, ifname, port, + tref.u32); + if (s >= 0) + tcp_sock_set_bufsize(c, s); + else + s = -1; - tref.tcp.splice = 1; - s = sock_l4(c, AF_INET6, IPPROTO_TCP, bind_addr, ifname, - port, tref.u32); - if (s >= 0) - tcp_sock_set_bufsize(c, s); + if (c->tcp.fwd_out.mode == FWD_AUTO) { + if (ns) + tcp_sock_ns[port][V6] = s; else - s = -1; - - if (c->tcp.fwd_out.mode == FWD_AUTO) { - if (ns) - tcp_sock_ns[port][V6] = s; - else - tcp_sock_init_lo[port][V6] = s; - } + tcp_sock_init_lo[port][V6] = s; } } } /** + * tcp_sock_init() - Initialise listening sockets for a given port + * @c: Execution context + * @ns: In pasta mode, if set, bind with loopback address in namespace + * @af: Address family to select a specific IP version, or AF_UNSPEC + * @addr: Pointer to address for binding, NULL if not configured + * @ifname: Name of interface to bind to, NULL if not configured + * @port: Port, host order + */ +void tcp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, const char *ifname, in_port_t port) +{ + if (af == AF_INET || af == AF_UNSPEC) + tcp_sock_init4(c, ns, addr, ifname, port); + if (af == AF_INET6 || af == AF_UNSPEC) + tcp_sock_init6(c, ns, addr, ifname, port); +} + +/** * tcp_sock_init_ns() - Bind sockets in namespace for outbound connections * @arg: Execution context * @@ -81,6 +81,9 @@ #define MAC_ZERO ((uint8_t [ETH_ALEN]){ 0 }) #define MAC_IS_ZERO(addr) (!memcmp((addr), MAC_ZERO, ETH_ALEN)) +#define IPV4_IS_LOOPBACK(addr) \ + ((addr) >> IN_CLASSA_NSHIFT == IN_LOOPBACKNET) + #define NS_FN_STACK_SIZE (RLIMIT_STACK_VAL * 1024 / 4) #define NS_CALL(fn, arg) \ do { \ |