From 3c6ae625101aee6ddf94e0fd85ce3a9c9067c3bf Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Sun, 1 May 2022 06:36:34 +0200 Subject: [PATCH] conf, tcp, udp: Allow address specification for forwarded ports This feature is available in slirp4netns but was missing in passt and pasta. Given that we don't do dynamic memory allocation, we need to bind sockets while parsing port configuration. This means we need to process all other options first, as they might affect addressing and IP version support. It also implies a minor rework of how TCP and UDP implementations bind sockets. Signed-off-by: Stefano Brivio --- conf.c | 120 ++++++++++++++++++++++++++++++++--------- icmp.c | 5 +- passt.1 | 12 ++++- passt.c | 22 ++++---- tcp.c | 63 ++++++++++++---------- tcp.h | 4 +- udp.c | 162 ++++++++++++++++++++++++++++++++++---------------------- udp.h | 4 +- util.c | 27 +++++----- util.h | 11 +--- 10 files changed, 274 insertions(+), 156 deletions(-) diff --git a/conf.c b/conf.c index 5c614cd..0baf4fa 100644 --- a/conf.c +++ b/conf.c @@ -109,14 +109,24 @@ enum conf_port_type { PORT_ALL, }; +/** + * conf_ports() - Parse port configuration options, initialise UDP/TCP sockets + * @c: Execution context + * @optname: Short option name, t, T, u, or U + * @optarg: Option argument (port specification) + * @set: Pointer to @conf_port_type to be set (port binding type) + * + * Return: -EINVAL on parsing error, 0 otherwise + */ static int conf_ports(struct ctx *c, char optname, const char *optarg, enum conf_port_type *set) { int start_src = -1, end_src = -1, start_dst = -1, end_dst = -1; void (*remap)(in_port_t port, in_port_t delta); - const char *p; + char addr_buf[sizeof(struct in6_addr)] = { 0 }; + sa_family_t af = AF_UNSPEC; + char buf[BUFSIZ], *sep, *p, *addr = addr_buf; uint8_t *map; - char *sep; if (optname == 't') { map = c->tcp.port_to_tap; @@ -149,10 +159,20 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, } if (!strcmp(optarg, "all")) { + int i; + if (*set || c->mode != MODE_PASST) return -EINVAL; *set = PORT_ALL; memset(map, 0xff, PORT_EPHEMERAL_MIN / 8); + + for (i = 0; i < PORT_EPHEMERAL_MIN; i++) { + if (optname == 't') + tcp_sock_init(c, 0, AF_UNSPEC, NULL, i); + else if (optname == 'u') + udp_sock_init(c, 0, AF_UNSPEC, NULL, i); + } + return 0; } @@ -161,12 +181,30 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, *set = PORT_SPEC; - if (strspn(optarg, "0123456789-,:") != strlen(optarg)) { - err("Invalid port specifier %s", optarg); - return -EINVAL; + strncpy(buf, optarg, sizeof(buf) - 1); + + if ((p = strchr(buf, '/'))) { + *p = 0; + p++; + + if (optname != 't' && optname != 'u') + goto bad; + + if (inet_pton(AF_INET, buf, addr)) + af = AF_INET; + else if (inet_pton(AF_INET6, buf, addr)) + af = AF_INET6; + else + goto bad; + } else { + p = buf; + + addr = NULL; } - p = optarg; + if (strspn(p, "0123456789-,:") != strlen(p)) + goto bad; + do { int i, port; @@ -242,11 +280,16 @@ static int conf_ports(struct ctx *c, char optname, const char *optarg, bitmap_set(map, i); - if (start_dst == -1) /* 22 or 22-80 */ - continue; + if (start_dst != -1) { + /* 80:8080 or 22-80:8080:8080 */ + remap(i, (in_port_t)(start_dst - + start_src)); + } - /* 80:8080 or 22-80:8080:8080 */ - remap(i, (in_port_t)(start_dst - start_src)); + if (optname == 't') + tcp_sock_init(c, 0, af, addr, i); + else if (optname == 'u') + udp_sock_init(c, 0, af, addr, i); } start_src = end_src = start_dst = end_dst = -1; @@ -655,13 +698,15 @@ static void usage(const char *name) info( " 'none': don't forward any ports"); info( " 'all': forward all unbound, non-ephemeral ports"); info( " a comma-separated list, optionally ranged with '-'"); - info( " and optional target ports after ':'. Examples:"); + info( " and optional target ports after ':', with optional"); + info( " address specification suffixed by '/'. Examples:"); info( " -t 22 Forward local port 22 to 22 on guest"); info( " -t 22:23 Forward local port 22 to 23 on guest"); info( " -t 22,25 Forward ports 22, 25 to ports 22, 25"); info( " -t 22-80 Forward ports 22 to 80"); info( " -t 22-80:32-90 Forward ports 22 to 80 to"); info( " corresponding port numbers plus 10"); + info( " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to guest"); info( " default: none"); info( " -u, --udp-ports SPEC UDP port forwarding to guest"); info( " SPEC is as described for TCP above"); @@ -676,13 +721,15 @@ pasta_opts: info( " 'none': don't forward any ports"); info( " 'auto': forward all ports currently bound in namespace"); info( " a comma-separated list, optionally ranged with '-'"); - info( " and optional target ports after ':'. Examples:"); + info( " and optional target ports after ':', with optional"); + info( " address specification suffixed by '/'. Examples:"); info( " -t 22 Forward local port 22 to port 22 in netns"); info( " -t 22:23 Forward local port 22 to port 23"); info( " -t 22,25 Forward ports 22, 25 to ports 22, 25"); info( " -t 22-80 Forward ports 22 to 80"); info( " -t 22-80:32-90 Forward ports 22 to 80 to"); info( " corresponding port numbers plus 10"); + info( " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to namespace"); info( " default: auto"); info( " IPv6 bound ports are also forwarded for IPv4"); info( " -u, --udp-ports SPEC UDP port forwarding to namespace"); @@ -857,7 +904,6 @@ void conf(struct ctx *c, int argc, char **argv) c->no_dhcp_dns = c->no_dhcp_dns_search = 1; do { - enum conf_port_type *set = NULL; const char *optstring; if (c->mode == MODE_PASST) @@ -1242,18 +1288,7 @@ void conf(struct ctx *c, int argc, char **argv) case 'u': case 'T': case 'U': - if (name == 't') - set = &tcp_tap; - else if (name == 'T') - set = &tcp_init; - else if (name == 'u') - set = &udp_tap; - else if (name == 'U') - set = &udp_init; - - if (conf_ports(c, name, optarg, set)) - usage(argv[0]); - + /* Handle these later, once addresses are configured */ break; case '?': case 'h': @@ -1294,6 +1329,41 @@ void conf(struct ctx *c, int argc, char **argv) conf_ip(c); + /* Now we can process port configuration options */ + optind = 1; + do { + enum conf_port_type *set = NULL; + const char *optstring; + + if (c->mode == MODE_PASST) + optstring = "dqfehs:p::P:m:a:n:M:g:i:D::S::46t:u:"; + else + optstring = "dqfehI:p::P:m:a:n:M:g:i:D::S::46t:u:T:U:"; + + name = getopt_long(argc, argv, optstring, options, NULL); + switch (name) { + case 't': + case 'u': + case 'T': + case 'U': + if (name == 't') + set = &tcp_tap; + else if (name == 'T') + set = &tcp_init; + else if (name == 'u') + set = &udp_tap; + else if (name == 'U') + set = &udp_init; + + if (!optarg || conf_ports(c, name, optarg, set)) + usage(argv[0]); + + break; + default: + break; + } + } while (name != -1); + if (!c->v4) c->no_dhcp = 1; diff --git a/icmp.c b/icmp.c index 8abc94b..2da8b58 100644 --- a/icmp.c +++ b/icmp.c @@ -168,7 +168,8 @@ int icmp_tap_handler(const struct ctx *c, int af, const void *addr, iref.icmp.id = id = ntohs(ih->un.echo.id); if ((s = icmp_id_map[V4][id].sock) <= 0) { - s = sock_l4(c, AF_INET, IPPROTO_ICMP, id, 0, iref.u32); + s = sock_l4(c, AF_INET, IPPROTO_ICMP, NULL, id, + iref.u32); if (s < 0) goto fail_sock; if (s > SOCKET_MAX) { @@ -205,7 +206,7 @@ int icmp_tap_handler(const struct ctx *c, int af, const void *addr, iref.icmp.id = id = ntohs(ih->icmp6_identifier); if ((s = icmp_id_map[V6][id].sock) <= 0) { - s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, id, 0, + s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, NULL, id, iref.u32); if (s < 0) goto fail_sock; diff --git a/passt.1 b/passt.1 index d9708b6..cdca3e9 100644 --- a/passt.1 +++ b/passt.1 @@ -298,7 +298,8 @@ For low (< 1024) ports, see \fBNOTES\fR. .TP .BR ports A comma-separated list of ports, optionally ranged with \fI-\fR, and, -optionally, with target ports after \fI:\fR, if they differ. Examples: +optionally, with target ports after \fI:\fR, if they differ. Specific addresses +can be bound as well, separated by \fI/\fR. Examples: .RS .TP -t 22 @@ -315,6 +316,9 @@ Forward local ports 22 to 80 to corresponding ports on the guest .TP -t 22-80-32:90 Forward local ports 22 to 80 to corresponding ports on the guest plus 10 +.TP +-t 192.0.2.1/22 +Forward local port 22, bound to 192.0.2.1, to port 22 on the guest .RE Default is \fBnone\fR. @@ -356,7 +360,8 @@ periodically derived (every second) from listening sockets reported by .TP .BR ports A comma-separated list of ports, optionally ranged with \fI-\fR, and, -optionally, with target ports after \fI:\fR, if they differ. Examples: +optionally, with target ports after \fI:\fR, if they differ. Specific addresses +can be bound as well, separated by \fI/\fR. Examples: .RS .TP -t 22 @@ -374,6 +379,9 @@ Forward local ports 22 to 80 to corresponding ports in the target namespace -t 22-80-32:90 Forward local ports 22 to 80 to corresponding ports plus 10 in the target namespace +.TP +-t 192.0.2.1/22 +Forward local port 22, bound to 192.0.2.1, to port 22 in the target namespace .RE IPv6 bound ports are also forwarded for IPv4. diff --git a/passt.c b/passt.c index 8781a7f..e5064f8 100644 --- a/passt.c +++ b/passt.c @@ -370,20 +370,13 @@ int main(int argc, char **argv) __setlogmask(LOG_MASK(LOG_EMERG)); - conf(&c, argc, argv); - trace_init(c.trace); - - if (!c.debug && (c.stderr || isatty(fileno(stdout)))) - __openlog(log_name, LOG_PERROR, LOG_DAEMON); - - c.epollfd = epoll_create1(c.foreground ? O_CLOEXEC : 0); + /* NOLINTNEXTLINE(android-cloexec-epoll-create1): forking in a moment */ + c.epollfd = epoll_create1(0); if (c.epollfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } - quit_fd = pasta_netns_quit_init(&c); - if (getrlimit(RLIMIT_NOFILE, &limit)) { perror("getrlimit"); exit(EXIT_FAILURE); @@ -395,13 +388,20 @@ int main(int argc, char **argv) } sock_probe_mem(&c); + conf(&c, argc, argv); + trace_init(c.trace); + + if (!c.debug && (c.stderr || isatty(fileno(stdout)))) + __openlog(log_name, LOG_PERROR, LOG_DAEMON); + + quit_fd = pasta_netns_quit_init(&c); + c.fd_tap = c.fd_tap_listen = -1; tap_sock_init(&c); clock_gettime(CLOCK_MONOTONIC, &now); - if ((!c.no_udp && udp_sock_init(&c)) || - (!c.no_tcp && tcp_sock_init(&c))) + if ((!c.no_udp && udp_init(&c)) || (!c.no_tcp && tcp_init(&c))) exit(EXIT_FAILURE); proto_update_l2_buf(c.mac_guest, c.mac, &c.addr4); diff --git a/tcp.c b/tcp.c index ad10688..e68409a 100644 --- a/tcp.c +++ b/tcp.c @@ -3082,14 +3082,18 @@ void tcp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events, } /** - * tcp_sock_init_one() - Initialise listening sockets for a given port + * 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 * @port: Port, host order */ -static void tcp_sock_init_one(const struct ctx *c, int ns, in_port_t port) +void tcp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, in_port_t port) { union tcp_epoll_ref tref = { .tcp.listen = 1 }; + const void *bind_addr; int s; if (ns) { @@ -3100,13 +3104,17 @@ static void tcp_sock_init_one(const struct ctx *c, int ns, in_port_t port) tcp_port_delta_to_tap[port]); } - if (c->v4) { - tref.tcp.v6 = 0; + if (af == AF_INET || af == AF_UNSPEC) { + if (!addr && c->mode == MODE_PASTA) + bind_addr = &c->addr4; + else + bind_addr = addr; + tref.tcp.v6 = 0; tref.tcp.splice = 0; + if (!ns) { - s = sock_l4(c, AF_INET, IPPROTO_TCP, port, - c->mode == MODE_PASTA ? BIND_EXT : BIND_ANY, + s = sock_l4(c, AF_INET, IPPROTO_TCP, bind_addr, port, tref.u32); if (s >= 0) tcp_sock_set_bufsize(c, s); @@ -3118,9 +3126,11 @@ static void tcp_sock_init_one(const struct ctx *c, int ns, in_port_t port) } if (c->mode == MODE_PASTA) { + bind_addr = &(uint32_t){ htonl(INADDR_LOOPBACK) }; + tref.tcp.splice = 1; - s = sock_l4(c, AF_INET, IPPROTO_TCP, port, - BIND_LOOPBACK, tref.u32); + s = sock_l4(c, AF_INET, IPPROTO_TCP, bind_addr, port, + tref.u32); if (s >= 0) tcp_sock_set_bufsize(c, s); else @@ -3135,13 +3145,17 @@ static void tcp_sock_init_one(const struct ctx *c, int ns, in_port_t port) } } - if (c->v6) { + if (af == AF_INET6 || af == AF_UNSPEC) { + if (!addr && c->mode == MODE_PASTA) + bind_addr = &c->addr6; + else + bind_addr = addr; + tref.tcp.v6 = 1; tref.tcp.splice = 0; if (!ns) { - s = sock_l4(c, AF_INET6, IPPROTO_TCP, port, - c->mode == MODE_PASTA ? BIND_EXT : BIND_ANY, + s = sock_l4(c, AF_INET6, IPPROTO_TCP, bind_addr, port, tref.u32); if (s >= 0) tcp_sock_set_bufsize(c, s); @@ -3153,9 +3167,11 @@ static void tcp_sock_init_one(const struct ctx *c, int ns, in_port_t port) } if (c->mode == MODE_PASTA) { + bind_addr = &in6addr_loopback; + tref.tcp.splice = 1; - s = sock_l4(c, AF_INET6, IPPROTO_TCP, port, - BIND_LOOPBACK, tref.u32); + s = sock_l4(c, AF_INET6, IPPROTO_TCP, bind_addr, port, + tref.u32); if (s >= 0) tcp_sock_set_bufsize(c, s); else @@ -3188,7 +3204,7 @@ static int tcp_sock_init_ns(void *arg) if (!bitmap_isset(c->tcp.port_to_init, port)) continue; - tcp_sock_init_one(c, 1, port); + tcp_sock_init(c, 1, AF_UNSPEC, NULL, port); } return 0; @@ -3259,15 +3275,15 @@ static int tcp_sock_refill(void *arg) } /** - * tcp_sock_init() - Bind sockets for inbound connections, get key for sequence + * tcp_init() - Get initial sequence, hash secret, initialise per-socket data * @c: Execution context * - * Return: 0 on success, -1 on failure + * Return: 0, doesn't return on failure */ -int tcp_sock_init(struct ctx *c) +int tcp_init(struct ctx *c) { struct tcp_sock_refill_arg refill_arg = { c, 0 }; - int i, port; + int i; #ifndef HAS_GETRANDOM int dev_random = open("/dev/random", O_RDONLY); unsigned int random_read = 0; @@ -3296,13 +3312,6 @@ int tcp_sock_init(struct ctx *c) exit(EXIT_FAILURE); } - for (port = 0; port < USHRT_MAX; port++) { - if (!bitmap_isset(c->tcp.port_to_tap, port)) - continue; - - tcp_sock_init_one(c, 0, port); - } - for (i = 0; i < ARRAY_SIZE(tcp_l2_mh); i++) tcp_l2_mh[i] = (struct mmsghdr) { .msg_hdr.msg_iovlen = 1 }; @@ -3412,7 +3421,7 @@ static int tcp_port_rebind(void *arg) if ((a->c->v4 && tcp_sock_ns[port][V4] == -1) || (a->c->v6 && tcp_sock_ns[port][V6] == -1)) - tcp_sock_init_one(a->c, 1, port); + tcp_sock_init(a->c, 1, AF_UNSPEC, NULL, port); } } else { for (port = 0; port < USHRT_MAX; port++) { @@ -3445,7 +3454,7 @@ static int tcp_port_rebind(void *arg) if ((a->c->v4 && tcp_sock_init_ext[port][V4] == -1) || (a->c->v6 && tcp_sock_init_ext[port][V6] == -1)) - tcp_sock_init_one(a->c, 0, port); + tcp_sock_init(a->c, 0, AF_UNSPEC, NULL, port); } } diff --git a/tcp.h b/tcp.h index 99edf81..7b720c1 100644 --- a/tcp.h +++ b/tcp.h @@ -20,7 +20,9 @@ void tcp_sock_handler(struct ctx *c, union epoll_ref ref, uint32_t events, const struct timespec *now); int tcp_tap_handler(struct ctx *c, int af, const void *addr, const struct pool *p, const struct timespec *now); -int tcp_sock_init(struct ctx *c); +void tcp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, in_port_t port); +int tcp_init(struct ctx *c); void tcp_timer(struct ctx *c, const struct timespec *ts); void tcp_defer_handler(struct ctx *c); diff --git a/udp.c b/udp.c index 86d806a..f425d14 100644 --- a/udp.c +++ b/udp.c @@ -1000,7 +1000,8 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, union udp_epoll_ref uref = { .udp.bound = 1, .udp.port = src }; - s = sock_l4(c, AF_INET, IPPROTO_UDP, src, 0, uref.u32); + s = sock_l4(c, AF_INET, IPPROTO_UDP, NULL, src, + uref.u32); if (s < 0) return p->count; @@ -1026,7 +1027,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, .sin6_port = uh->dest, .sin6_addr = *(struct in6_addr *)addr, }; - enum bind_type bind_to = BIND_ANY; + const void *bind_addr = &in6addr_any; sa = (struct sockaddr *)&s_in6; sl = sizeof(s_in6); @@ -1043,7 +1044,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, ntohs(s_in6.sin6_port) == 53) { s_in6.sin6_addr = c->dns6[0]; } else if (IN6_IS_ADDR_LINKLOCAL(&s_in6.sin6_addr)) { - bind_to = BIND_LL; + bind_addr = &c->addr6_ll; } if (!(s = udp_tap_map[V6][src].sock)) { @@ -1051,7 +1052,7 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, .udp.v6 = 1, .udp.port = src }; - s = sock_l4(c, AF_INET6, IPPROTO_UDP, src, bind_to, + s = sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, src, uref.u32); if (s < 0) return p->count; @@ -1092,6 +1093,96 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, return count; } +/** + * udp_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 + * @port: Port, host order + */ +void udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, in_port_t port) +{ + union udp_epoll_ref uref = { .udp.bound = 1 }; + const void *bind_addr; + int s; + + if (ns) { + uref.udp.port = (in_port_t)(port + + udp_port_delta_to_init[port]); + } else { + uref.udp.port = (in_port_t)(port + + udp_port_delta_to_tap[port]); + } + + if (af == AF_INET || af == AF_UNSPEC) { + if (!addr && c->mode == MODE_PASTA) + bind_addr = &c->addr4; + else + bind_addr = addr; + + uref.udp.v6 = 0; + + if (!ns) { + uref.udp.splice = 0; + s = sock_l4(c, AF_INET, IPPROTO_UDP, bind_addr, port, + uref.u32); + + udp_tap_map[V4][uref.udp.port].sock = s; + } + + if (c->mode == MODE_PASTA) { + bind_addr = &(uint32_t){ htonl(INADDR_LOOPBACK) }; + uref.udp.splice = UDP_TO_NS; + + sock_l4(c, AF_INET, IPPROTO_UDP, bind_addr, port, + uref.u32); + } + + if (ns) { + bind_addr = &(uint32_t){ htonl(INADDR_LOOPBACK) }; + uref.udp.splice = UDP_TO_INIT; + + sock_l4(c, AF_INET, IPPROTO_UDP, bind_addr, port, + uref.u32); + } + } + + if (af == AF_INET6 || af == AF_UNSPEC) { + if (!addr && c->mode == MODE_PASTA) + bind_addr = &c->addr6; + else + bind_addr = addr; + + uref.udp.v6 = 1; + + if (!ns) { + uref.udp.splice = 0; + s = sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, port, + uref.u32); + + udp_tap_map[V6][uref.udp.port].sock = s; + } + + if (c->mode == MODE_PASTA) { + bind_addr = &in6addr_loopback; + uref.udp.splice = UDP_TO_NS; + + sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, port, + uref.u32); + } + + if (ns) { + bind_addr = &in6addr_loopback; + uref.udp.splice = UDP_TO_INIT; + + sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, port, + uref.u32); + } + } +} + /** * udp_sock_init_ns() - Bind sockets in namespace for inbound connections * @arg: Execution context @@ -1100,8 +1191,6 @@ int udp_tap_handler(struct ctx *c, int af, const void *addr, */ int udp_sock_init_ns(void *arg) { - union udp_epoll_ref uref = { .udp.bound = 1, - .udp.splice = UDP_TO_INIT }; struct ctx *c = (struct ctx *)arg; int dst; @@ -1112,19 +1201,7 @@ int udp_sock_init_ns(void *arg) if (!bitmap_isset(c->udp.port_to_init, dst)) continue; - uref.udp.port = dst + udp_port_delta_to_init[dst]; - - if (c->v4) { - uref.udp.v6 = 0; - sock_l4(c, AF_INET, IPPROTO_UDP, dst, BIND_LOOPBACK, - uref.u32); - } - - if (c->v6) { - uref.udp.v6 = 1; - sock_l4(c, AF_INET6, IPPROTO_UDP, dst, BIND_LOOPBACK, - uref.u32); - } + udp_sock_init(c, 1, AF_UNSPEC, NULL, dst); } return 0; @@ -1178,54 +1255,13 @@ static void udp_splice_iov_init(void) } /** - * udp_sock_init() - Create and bind listening sockets for inbound packets + * udp_init() - Initialise per-socket data, and sockets in namespace * @c: Execution context * - * Return: 0 on success, -1 on failure + * Return: 0 */ -int udp_sock_init(const struct ctx *c) +int udp_init(const struct ctx *c) { - union udp_epoll_ref uref = { .udp.bound = 1 }; - int dst, s; - - for (dst = 0; dst < USHRT_MAX; dst++) { - if (!bitmap_isset(c->udp.port_to_tap, dst)) - continue; - - uref.udp.port = dst + udp_port_delta_to_tap[dst]; - - if (c->v4) { - uref.udp.splice = 0; - uref.udp.v6 = 0; - s = sock_l4(c, AF_INET, IPPROTO_UDP, dst, - c->mode == MODE_PASTA ? BIND_EXT : BIND_ANY, - uref.u32); - if (s > 0) - udp_tap_map[V4][uref.udp.port].sock = s; - - if (c->mode == MODE_PASTA) { - uref.udp.splice = UDP_TO_NS; - sock_l4(c, AF_INET, IPPROTO_UDP, dst, - BIND_LOOPBACK, uref.u32); - } - } - if (c->v6) { - uref.udp.splice = 0; - uref.udp.v6 = 1; - s = sock_l4(c, AF_INET6, IPPROTO_UDP, dst, - c->mode == MODE_PASTA ? BIND_EXT : BIND_ANY, - uref.u32); - if (s > 0) - udp_tap_map[V6][uref.udp.port].sock = s; - - if (c->mode == MODE_PASTA) { - uref.udp.splice = UDP_TO_NS; - sock_l4(c, AF_INET6, IPPROTO_UDP, dst, - BIND_LOOPBACK, uref.u32); - } - } - } - if (c->v4) udp_sock4_iov_init(); diff --git a/udp.h b/udp.h index 4b6f465..f16fe5e 100644 --- a/udp.h +++ b/udp.h @@ -12,7 +12,9 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref, uint32_t events, const struct timespec *now); int udp_tap_handler(struct ctx *c, int af, const void *addr, const struct pool *p, const struct timespec *now); -int udp_sock_init(const struct ctx *c); +void udp_sock_init(const struct ctx *c, int ns, sa_family_t af, + const void *addr, in_port_t port); +int udp_init(const struct ctx *c); void udp_timer(struct ctx *c, const struct timespec *ts); void udp_update_l2_buf(const unsigned char *eth_d, const unsigned char *eth_s, const uint32_t *ip_da); diff --git a/util.c b/util.c index c3475d6..9afd2a5 100644 --- a/util.c +++ b/util.c @@ -218,14 +218,14 @@ found: * @c: Execution context * @af: Address family, AF_INET or AF_INET6 * @proto: Protocol number + * @bind_addr: Address for binding, NULL for any * @port: Port, host order - * @bind_type: Type of address for binding * @data: epoll reference portion for protocol handlers * * Return: newly created socket, -1 on error */ -int sock_l4(const struct ctx *c, int af, uint8_t proto, uint16_t port, - enum bind_type bind_addr, uint32_t data) +int sock_l4(const struct ctx *c, int af, uint8_t proto, + const void *bind_addr, uint16_t port, uint32_t data) { union epoll_ref ref = { .r.proto = proto, .r.p.data = data }; struct sockaddr_in addr4 = { @@ -264,23 +264,20 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, uint16_t port, ref.r.s = fd; if (af == AF_INET) { - if (bind_addr == BIND_LOOPBACK) - addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - else if (bind_addr == BIND_EXT) - addr4.sin_addr.s_addr = c->addr4; + if (bind_addr) + addr4.sin_addr.s_addr = *(in_addr_t *)bind_addr; else addr4.sin_addr.s_addr = htonl(INADDR_ANY); sa = (const struct sockaddr *)&addr4; sl = sizeof(addr4); } else { - if (bind_addr == BIND_LOOPBACK) { - addr6.sin6_addr = in6addr_loopback; - } else if (bind_addr == BIND_EXT) { - addr6.sin6_addr = c->addr6; - } else if (bind_addr == BIND_LL) { - addr6.sin6_addr = c->addr6_ll; - addr6.sin6_scope_id = c->ifi; + if (bind_addr) { + addr6.sin6_addr = *(struct in6_addr *)bind_addr; + + if (!memcmp(bind_addr, &c->addr6_ll, + sizeof(c->addr6_ll))) + addr6.sin6_scope_id = c->ifi; } else { addr6.sin6_addr = in6addr_any; } @@ -303,7 +300,7 @@ int sock_l4(const struct ctx *c, int af, uint8_t proto, uint16_t port, */ if (proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6) { close(fd); - return 0; + return -1; } } diff --git a/util.h b/util.h index 5172bf6..f85a87a 100644 --- a/util.h +++ b/util.h @@ -170,13 +170,6 @@ enum { #include "packet.h" -enum bind_type { - BIND_ANY = 0, - BIND_LOOPBACK, - BIND_LL, - BIND_EXT, -}; - struct ctx; struct ipv6hdr { @@ -213,8 +206,8 @@ void passt_vsyslog(int pri, const char *format, va_list ap); void __setlogmask(int mask); char *ipv6_l4hdr(const struct pool *p, int index, size_t offset, uint8_t *proto, size_t *dlen); -int sock_l4(const struct ctx *c, int af, uint8_t proto, uint16_t port, - enum bind_type bind_addr, uint32_t data); +int sock_l4(const struct ctx *c, int af, uint8_t proto, + const void *bind_addr, uint16_t port, uint32_t data); void sock_probe_mem(struct ctx *c); int timespec_diff_ms(const struct timespec *a, const struct timespec *b); void bitmap_set(uint8_t *map, int bit);