diff options
Diffstat (limited to 'conf.c')
-rw-r--r-- | conf.c | 589 |
1 files changed, 385 insertions, 204 deletions
@@ -45,6 +45,23 @@ #include "lineread.h" #include "isolation.h" #include "log.h" +#include "vhost_user.h" + +#define NETNS_RUN_DIR "/run/netns" + +#define IP4_LL_GUEST_ADDR (struct in_addr){ htonl_constant(0xa9fe0201) } + /* 169.254.2.1, libslirp default: 10.0.2.1 */ + +#define IP4_LL_GUEST_GW (struct in_addr){ htonl_constant(0xa9fe0202) } + /* 169.254.2.2, libslirp default: 10.0.2.2 */ + +#define IP4_LL_PREFIX_LEN 16 + +#define IP6_LL_GUEST_GW (struct in6_addr) \ + {{{ 0xfe, 0x80, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0x01 }}} + +const char *pasta_default_ifn = "tap0"; /** * next_chunk - Return the next piece of a string delimited by a character @@ -116,11 +133,10 @@ static int parse_port_range(const char *s, char **endptr, static void conf_ports(const struct ctx *c, char optname, const char *optarg, struct fwd_ports *fwd) { - char addr_buf[sizeof(struct in6_addr)] = { 0 }, *addr = addr_buf; + union inany_addr addr_buf = inany_any6, *addr = &addr_buf; char buf[BUFSIZ], *spec, *ifname = NULL, *p; bool exclude_only = true, bound_one = false; uint8_t exclude[PORT_BITMAP_SIZE] = { 0 }; - sa_family_t af = AF_UNSPEC; unsigned i; int ret; @@ -156,19 +172,23 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, die("'all' port forwarding is only allowed for passt"); fwd->mode = FWD_ALL; - memset(fwd->map, 0xff, PORT_EPHEMERAL_MIN / 8); - for (i = 0; i < PORT_EPHEMERAL_MIN; i++) { + /* Skip port 0. It has special meaning for many socket APIs, so + * trying to bind it is not really safe. + */ + for (i = 1; i < NUM_PORTS; i++) { + if (fwd_port_is_ephemeral(i)) + continue; + + bitmap_set(fwd->map, i); if (optname == 't') { - ret = tcp_sock_init(c, AF_UNSPEC, NULL, NULL, - i); + ret = tcp_sock_init(c, NULL, NULL, i); if (ret == -ENFILE || ret == -EMFILE) goto enfile; if (!ret) bound_one = true; } else if (optname == 'u') { - ret = udp_sock_init(c, 0, AF_UNSPEC, NULL, NULL, - i); + ret = udp_sock_init(c, 0, NULL, NULL, i); if (ret == -ENFILE || ret == -EMFILE) goto enfile; if (!ret) @@ -220,11 +240,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, p++; } - if (inet_pton(AF_INET, p, addr)) - af = AF_INET; - else if (inet_pton(AF_INET6, p, addr)) - af = AF_INET6; - else + if (!inany_pton(p, addr)) goto bad; } } else { @@ -259,20 +275,24 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, } while ((p = next_chunk(p, ','))); if (exclude_only) { - for (i = 0; i < PORT_EPHEMERAL_MIN; i++) { - if (bitmap_isset(exclude, i)) + /* Skip port 0. It has special meaning for many socket APIs, so + * trying to bind it is not really safe. + */ + for (i = 1; i < NUM_PORTS; i++) { + if (fwd_port_is_ephemeral(i) || + bitmap_isset(exclude, i)) continue; bitmap_set(fwd->map, i); if (optname == 't') { - ret = tcp_sock_init(c, af, addr, ifname, i); + ret = tcp_sock_init(c, addr, ifname, i); if (ret == -ENFILE || ret == -EMFILE) goto enfile; if (!ret) bound_one = true; } else if (optname == 'u') { - ret = udp_sock_init(c, 0, af, addr, ifname, i); + ret = udp_sock_init(c, 0, addr, ifname, i); if (ret == -ENFILE || ret == -EMFILE) goto enfile; if (!ret) @@ -328,9 +348,9 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg, ret = 0; if (optname == 't') - ret = tcp_sock_init(c, af, addr, ifname, i); + ret = tcp_sock_init(c, addr, ifname, i); else if (optname == 'u') - ret = udp_sock_init(c, 0, af, addr, ifname, i); + ret = udp_sock_init(c, 0, addr, ifname, i); if (ret) goto bind_fail; } @@ -345,7 +365,7 @@ mode_conflict: die("Port forwarding mode '%s' conflicts with previous mode", optarg); bind_fail: die("Failed to bind port %u (%s) for option '-%c %s', exiting", - i, strerror(-ret), optname, optarg); + i, strerror_(-ret), optname, optarg); bind_all_fail: die("Failed to bind any port for '-%c %s', exiting", optname, optarg); } @@ -353,55 +373,93 @@ bind_all_fail: /** * add_dns4() - Possibly add the IPv4 address of a DNS resolver to configuration * @c: Execution context - * @addr: Address found in /etc/resolv.conf - * @conf: Pointer to reference of current entry in array of IPv4 resolvers + * @addr: Guest nameserver IPv4 address + * @idx: Index of free entry in array of IPv4 resolvers + * + * Return: Number of entries added (0 or 1) */ -static void add_dns4(struct ctx *c, const struct in_addr *addr, - struct in_addr **conf) +static unsigned add_dns4(struct ctx *c, const struct in_addr *addr, + unsigned idx) { - /* Guest or container can only access local addresses via redirect */ - if (IN4_IS_ADDR_LOOPBACK(addr)) { - if (!c->no_map_gw) { - **conf = c->ip4.gw; - (*conf)++; - - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match)) - c->ip4.dns_match = c->ip4.gw; - } - } else { - **conf = *addr; - (*conf)++; - } + if (idx >= ARRAY_SIZE(c->ip4.dns)) + return 0; - if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_host)) - c->ip4.dns_host = *addr; + c->ip4.dns[idx] = *addr; + return 1; } /** * add_dns6() - Possibly add the IPv6 address of a DNS resolver to configuration * @c: Execution context - * @addr: Address found in /etc/resolv.conf - * @conf: Pointer to reference of current entry in array of IPv6 resolvers + * @addr: Guest nameserver IPv6 address + * @idx: Index of free entry in array of IPv6 resolvers + * + * Return: Number of entries added (0 or 1) */ -static void add_dns6(struct ctx *c, - struct in6_addr *addr, struct in6_addr **conf) +static unsigned add_dns6(struct ctx *c, const struct in6_addr *addr, + unsigned idx) { - /* Guest or container can only access local addresses via redirect */ - if (IN6_IS_ADDR_LOOPBACK(addr)) { - if (!c->no_map_gw) { - memcpy(*conf, &c->ip6.gw, sizeof(**conf)); - (*conf)++; + if (idx >= ARRAY_SIZE(c->ip6.dns)) + return 0; - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match)) - memcpy(&c->ip6.dns_match, addr, sizeof(*addr)); + c->ip6.dns[idx] = *addr; + return 1; +} + +/** + * add_dns_resolv() - Possibly add ns from host resolv.conf to configuration + * @c: Execution context + * @nameserver: Nameserver address string from /etc/resolv.conf + * @idx4: Pointer to index of current entry in array of IPv4 resolvers + * @idx6: Pointer to index of current entry in array of IPv6 resolvers + * + * @idx4 or @idx6 may be NULL, in which case resolvers of the corresponding type + * are ignored. + */ +static void add_dns_resolv(struct ctx *c, const char *nameserver, + unsigned *idx4, unsigned *idx6) +{ + struct in6_addr ns6; + struct in_addr ns4; + + if (idx4 && inet_pton(AF_INET, nameserver, &ns4)) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_host)) + c->ip4.dns_host = ns4; + + /* Guest or container can only access local addresses via + * redirect + */ + if (IN4_IS_ADDR_LOOPBACK(&ns4)) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_host_loopback)) + return; + + ns4 = c->ip4.map_host_loopback; + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match)) + c->ip4.dns_match = c->ip4.map_host_loopback; } - } else { - memcpy(*conf, addr, sizeof(**conf)); - (*conf)++; + + *idx4 += add_dns4(c, &ns4, *idx4); } - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_host)) - c->ip6.dns_host = *addr; + if (idx6 && inet_pton(AF_INET6, nameserver, &ns6)) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_host)) + c->ip6.dns_host = ns6; + + /* Guest or container can only access local addresses via + * redirect + */ + if (IN6_IS_ADDR_LOOPBACK(&ns6)) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback)) + return; + + ns6 = c->ip6.map_host_loopback; + + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match)) + c->ip6.dns_match = c->ip6.map_host_loopback; + } + + *idx6 += add_dns6(c, &ns6, *idx6); + } } /** @@ -410,18 +468,16 @@ static void add_dns6(struct ctx *c, */ static void get_dns(struct ctx *c) { - struct in6_addr *dns6 = &c->ip6.dns[0], dns6_tmp; - struct in_addr *dns4 = &c->ip4.dns[0], dns4_tmp; int dns4_set, dns6_set, dnss_set, dns_set, fd; + unsigned dns4_idx = 0, dns6_idx = 0; struct fqdn *s = c->dns_search; struct lineread resolvconf; - unsigned int added = 0; ssize_t line_len; char *line, *end; const char *p; - dns4_set = !c->ifi4 || !IN4_IS_ADDR_UNSPECIFIED(dns4); - dns6_set = !c->ifi6 || !IN6_IS_ADDR_UNSPECIFIED(dns6); + dns4_set = !c->ifi4 || !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[0]); + dns6_set = !c->ifi6 || !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[0]); dnss_set = !!*s->n || c->no_dns_search; dns_set = (dns4_set && dns6_set) || c->no_dns; @@ -442,19 +498,9 @@ static void get_dns(struct ctx *c) if (end) *end = 0; - if (!dns4_set && - dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) - 1 - && inet_pton(AF_INET, p + 1, &dns4_tmp)) { - add_dns4(c, &dns4_tmp, &dns4); - added++; - } - - if (!dns6_set && - dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) - 1 - && inet_pton(AF_INET6, p + 1, &dns6_tmp)) { - add_dns6(c, &dns6_tmp, &dns6); - added++; - } + add_dns_resolv(c, p + 1, + dns4_set ? NULL : &dns4_idx, + dns6_set ? NULL : &dns6_idx); } else if (!dnss_set && strstr(line, "search ") == line && s == c->dns_search) { end = strpbrk(line, "\n"); @@ -481,7 +527,7 @@ static void get_dns(struct ctx *c) out: if (!dns_set) { - if (!added) + if (!(dns4_idx + dns6_idx)) warn("Couldn't get any nameserver address"); if (c->no_dhcp_dns) @@ -545,10 +591,15 @@ static void conf_pasta_ns(int *netns_only, char *userns, char *netns, if (pidval < 0 || pidval > INT_MAX) die("Invalid PID %s", argv[optind]); - snprintf(netns, PATH_MAX, "/proc/%ld/ns/net", pidval); - if (!*userns) - snprintf(userns, PATH_MAX, "/proc/%ld/ns/user", - pidval); + if (snprintf_check(netns, PATH_MAX, + "/proc/%ld/ns/net", pidval)) + die_perror("Can't build netns path"); + + if (!*userns) { + if (snprintf_check(userns, PATH_MAX, + "/proc/%ld/ns/user", pidval)) + die_perror("Can't build userns path"); + } } } @@ -586,26 +637,25 @@ static int conf_ip4_prefix(const char *arg) * conf_ip4() - Verify or detect IPv4 support, get relevant addresses * @ifi: Host interface to attempt (0 to determine one) * @ip4: IPv4 context (will be written) - * @mac: MAC address to use (written if unset) * * Return: Interface index for IPv4, or 0 on failure. */ -static unsigned int conf_ip4(unsigned int ifi, - struct ip4_ctx *ip4, unsigned char *mac) +static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) { if (!ifi) ifi = nl_get_ext_if(nl_sock, AF_INET); if (!ifi) { - info("Couldn't pick external interface: disabling IPv4"); + debug("Failed to detect external interface for IPv4"); return 0; } - if (IN4_IS_ADDR_UNSPECIFIED(&ip4->gw)) { - int rc = nl_route_get_def(nl_sock, ifi, AF_INET, &ip4->gw); + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->guest_gw)) { + int rc = nl_route_get_def(nl_sock, ifi, AF_INET, + &ip4->guest_gw); if (rc < 0) { - err("Couldn't discover IPv4 gateway address: %s", - strerror(-rc)); + debug("Couldn't discover IPv4 gateway address: %s", + strerror_(-rc)); return 0; } } @@ -614,8 +664,8 @@ static unsigned int conf_ip4(unsigned int ifi, int rc = nl_addr_get(nl_sock, ifi, AF_INET, &ip4->addr, &ip4->prefix_len, NULL); if (rc < 0) { - err("Couldn't discover IPv4 address: %s", - strerror(-rc)); + debug("Couldn't discover IPv4 address: %s", + strerror_(-rc)); return 0; } } @@ -632,21 +682,9 @@ static unsigned int conf_ip4(unsigned int ifi, ip4->prefix_len = 32; } - memcpy(&ip4->addr_seen, &ip4->addr, sizeof(ip4->addr_seen)); - - if (MAC_IS_ZERO(mac)) { - int rc = nl_link_get_mac(nl_sock, ifi, mac); - if (rc < 0) { - char ifname[IFNAMSIZ]; - - err("Couldn't discover MAC address for %s: %s", - if_indextoname(ifi, ifname), strerror(-rc)); - return 0; - } + ip4->addr_seen = ip4->addr; - if (MAC_IS_ZERO(mac)) - memcpy(mac, MAC_LAA, ETH_ALEN); - } + ip4->our_tap_addr = ip4->guest_gw; if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) return 0; @@ -655,15 +693,26 @@ static unsigned int conf_ip4(unsigned int ifi, } /** + * conf_ip4_local() - Configure IPv4 addresses and attributes for local mode + * @ip4: IPv4 context (will be written) + */ +static void conf_ip4_local(struct ip4_ctx *ip4) +{ + ip4->addr_seen = ip4->addr = IP4_LL_GUEST_ADDR; + ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW; + ip4->prefix_len = IP4_LL_PREFIX_LEN; + + ip4->no_copy_addrs = ip4->no_copy_routes = true; +} + +/** * conf_ip6() - Verify or detect IPv6 support, get relevant addresses * @ifi: Host interface to attempt (0 to determine one) * @ip6: IPv6 context (will be written) - * @mac: MAC address to use (written if unset) * * Return: Interface index for IPv6, or 0 on failure. */ -static unsigned int conf_ip6(unsigned int ifi, - struct ip6_ctx *ip6, unsigned char *mac) +static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) { int prefix_len = 0; int rc; @@ -672,51 +721,51 @@ static unsigned int conf_ip6(unsigned int ifi, ifi = nl_get_ext_if(nl_sock, AF_INET6); if (!ifi) { - info("Couldn't pick external interface: disabling IPv6"); + debug("Failed to detect external interface for IPv6"); return 0; } - if (IN6_IS_ADDR_UNSPECIFIED(&ip6->gw)) { - rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &ip6->gw); + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->guest_gw)) { + rc = nl_route_get_def(nl_sock, ifi, AF_INET6, &ip6->guest_gw); if (rc < 0) { - err("Couldn't discover IPv6 gateway address: %s", - strerror(-rc)); + debug("Couldn't discover IPv6 gateway address: %s", + strerror_(-rc)); return 0; } } rc = nl_addr_get(nl_sock, ifi, AF_INET6, IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) ? &ip6->addr : NULL, - &prefix_len, &ip6->addr_ll); + &prefix_len, &ip6->our_tap_ll); if (rc < 0) { - err("Couldn't discover IPv6 address: %s", strerror(-rc)); + debug("Couldn't discover IPv6 address: %s", strerror_(-rc)); return 0; } - memcpy(&ip6->addr_seen, &ip6->addr, sizeof(ip6->addr)); - memcpy(&ip6->addr_ll_seen, &ip6->addr_ll, sizeof(ip6->addr_ll)); + ip6->addr_seen = ip6->addr; - if (MAC_IS_ZERO(mac)) { - rc = nl_link_get_mac(nl_sock, ifi, mac); - if (rc < 0) { - char ifname[IFNAMSIZ]; - err("Couldn't discover MAC address for %s: %s", - if_indextoname(ifi, ifname), strerror(-rc)); - return 0; - } - - if (MAC_IS_ZERO(mac)) - memcpy(mac, MAC_LAA, ETH_ALEN); - } + if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) + ip6->our_tap_ll = ip6->guest_gw; if (IN6_IS_ADDR_UNSPECIFIED(&ip6->addr) || - IN6_IS_ADDR_UNSPECIFIED(&ip6->addr_ll)) + IN6_IS_ADDR_UNSPECIFIED(&ip6->our_tap_ll)) return 0; return ifi; } /** + * conf_ip6_local() - Configure IPv6 addresses and attributes for local mode + * @ip6: IPv6 context (will be written) + */ +static void conf_ip6_local(struct ip6_ctx *ip6) +{ + ip6->our_tap_ll = ip6->guest_gw = IP6_LL_GUEST_GW; + + ip6->no_copy_addrs = ip6->no_copy_routes = true; +} + +/** * usage() - Print usage, exit with given status code * @name: Executable name * @f: Stream to print usage info to @@ -725,19 +774,19 @@ static unsigned int conf_ip6(unsigned int ifi, static void usage(const char *name, FILE *f, int status) { if (strstr(name, "pasta")) { - fprintf(f, "Usage: %s [OPTION]... [COMMAND] [ARGS]...\n", name); - fprintf(f, " %s [OPTION]... PID\n", name); - fprintf(f, " %s [OPTION]... --netns [PATH|NAME]\n", name); - fprintf(f, + FPRINTF(f, "Usage: %s [OPTION]... [COMMAND] [ARGS]...\n", name); + FPRINTF(f, " %s [OPTION]... PID\n", name); + FPRINTF(f, " %s [OPTION]... --netns [PATH|NAME]\n", name); + FPRINTF(f, "\n" "Without PID or --netns, run the given command or a\n" "default shell in a new network and user namespace, and\n" "connect it via pasta.\n"); } else { - fprintf(f, "Usage: %s [OPTION]...\n", name); + FPRINTF(f, "Usage: %s [OPTION]...\n", name); } - fprintf(f, + FPRINTF(f, "\n" " -d, --debug Be verbose\n" " --trace Be extra verbose, implies --debug\n" @@ -754,17 +803,22 @@ static void usage(const char *name, FILE *f, int status) " --version Show version and exit\n"); if (strstr(name, "pasta")) { - fprintf(f, + FPRINTF(f, " -I, --ns-ifname NAME namespace interface name\n" " default: same interface name as external one\n"); } else { - fprintf(f, - " -s, --socket PATH UNIX domain socket path\n" + FPRINTF(f, + " -s, --socket, --socket-path PATH UNIX domain socket path\n" " default: probe free path starting from " UNIX_SOCK_PATH "\n", 1); + FPRINTF(f, + " --vhost-user Enable vhost-user mode\n" + " UNIX domain socket is provided by -s option\n" + " --print-capabilities print back-end capabilities in JSON format,\n" + " only meaningful for vhost-user mode\n"); } - fprintf(f, + FPRINTF(f, " -F, --fd FD Use FD as pre-opened connected socket\n" " -p, --pcap FILE Log tap-facing traffic to pcap file\n" " -P, --pid FILE Write own PID to the given file\n" @@ -778,7 +832,7 @@ static void usage(const char *name, FILE *f, int status) " -n, --netmask MASK Assign IPv4 MASK, dot-decimal or bits\n" " default: netmask from matching address on the host\n" " -M, --mac-addr ADDR Use source MAC address ADDR\n" - " default: MAC address from interface with default route\n" + " default: 9a:55:9a:55:9a:55 (locally administered)\n" " -g, --gateway ADDR Pass IPv4 or IPv6 address as gateway\n" " default: gateway from interface with default route\n" " -i, --interface NAME Interface for addresses and routes\n" @@ -795,31 +849,40 @@ static void usage(const char *name, FILE *f, int status) " can be specified multiple times\n" " a single, empty option disables DNS information\n"); if (strstr(name, "pasta")) - fprintf(f, " default: don't use any addresses\n"); + FPRINTF(f, " default: don't use any addresses\n"); else - fprintf(f, " default: use addresses from /etc/resolv.conf\n"); - fprintf(f, + FPRINTF(f, " default: use addresses from /etc/resolv.conf\n"); + FPRINTF(f, " -S, --search LIST Space-separated list, search domains\n" " a single, empty option disables the DNS search list\n"); if (strstr(name, "pasta")) - fprintf(f, " default: don't use any search list\n"); + FPRINTF(f, " default: don't use any search list\n"); else - fprintf(f, " default: use search list from /etc/resolv.conf\n"); + FPRINTF(f, " default: use search list from /etc/resolv.conf\n"); if (strstr(name, "pasta")) - fprintf(f, " --dhcp-dns \tPass DNS list via DHCP/DHCPv6/NDP\n"); + FPRINTF(f, " --dhcp-dns \tPass DNS list via DHCP/DHCPv6/NDP\n"); else - fprintf(f, " --no-dhcp-dns No DNS list in DHCP/DHCPv6/NDP\n"); + FPRINTF(f, " --no-dhcp-dns No DNS list in DHCP/DHCPv6/NDP\n"); if (strstr(name, "pasta")) - fprintf(f, " --dhcp-search Pass list via DHCP/DHCPv6/NDP\n"); + FPRINTF(f, " --dhcp-search Pass list via DHCP/DHCPv6/NDP\n"); else - fprintf(f, " --no-dhcp-search No list in DHCP/DHCPv6/NDP\n"); - - fprintf(f, + FPRINTF(f, " --no-dhcp-search No list in DHCP/DHCPv6/NDP\n"); + + FPRINTF(f, + " --map-host-loopback ADDR Translate ADDR to refer to host\n" + " can be specified zero to two times (for IPv4 and IPv6)\n" + " default: gateway address\n" + " --map-guest-addr ADDR Translate ADDR to guest's address\n" + " can be specified zero to two times (for IPv4 and IPv6)\n" + " default: none\n" " --dns-forward ADDR Forward DNS queries sent to ADDR\n" " can be specified zero to two times (for IPv4 and IPv6)\n" " default: don't forward DNS queries\n" + " --dns-host ADDR Host nameserver to direct queries to\n" + " can be specified zero to two times (for IPv4 and IPv6)\n" + " default: first nameserver from host's /etc/resolv.conf\n" " --no-tcp Disable TCP protocol handler\n" " --no-udp Disable UDP protocol handler\n" " --no-icmp Disable ICMP/ICMPv6 protocol handler\n" @@ -827,6 +890,7 @@ static void usage(const char *name, FILE *f, int status) " --no-ndp Disable NDP responses\n" " --no-dhcpv6 Disable DHCPv6 server\n" " --no-ra Disable router advertisements\n" + " --freebind Bind to any address for forwarding\n" " --no-map-gw Don't map gateway address to host\n" " -4, --ipv4-only Enable IPv4 operation only\n" " -6, --ipv6-only Enable IPv6 operation only\n"); @@ -834,7 +898,7 @@ static void usage(const char *name, FILE *f, int status) if (strstr(name, "pasta")) goto pasta_opts; - fprintf(f, + FPRINTF(f, " -1, --one-off Quit after handling one single client\n" " -t, --tcp-ports SPEC TCP port forwarding to guest\n" " can be specified multiple times\n" @@ -865,7 +929,7 @@ static void usage(const char *name, FILE *f, int status) pasta_opts: - fprintf(f, + FPRINTF(f, " -t, --tcp-ports SPEC TCP port forwarding to namespace\n" " can be specified multiple times\n" " SPEC can be:\n" @@ -899,6 +963,9 @@ pasta_opts: " -U, --udp-ns SPEC UDP port forwarding to init namespace\n" " SPEC is as described above\n" " default: auto\n" + " --host-lo-to-ns-lo DEPRECATED:\n" + " Translate host-loopback forwards to\n" + " namespace loopback\n" " --userns NSPATH Target user namespace to join\n" " --netns PATH|NAME Target network namespace to join\n" " --netns-only Don't join existing user namespace\n" @@ -910,7 +977,8 @@ pasta_opts: " Don't copy all routes to namespace\n" " --no-copy-addrs DEPRECATED:\n" " Don't copy all addresses to namespace\n" - " --ns-mac-addr ADDR Set MAC address on tap interface\n"); + " --ns-mac-addr ADDR Set MAC address on tap interface\n" + " --no-splice Disable inbound socket splicing\n"); exit(status); } @@ -921,15 +989,18 @@ pasta_opts: */ static void conf_print(const struct ctx *c) { - char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN], ifn[IFNAMSIZ]; + char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN]; + char bufmac[ETH_ADDRSTRLEN], ifn[IFNAMSIZ]; int i; - info("Template interface: %s%s%s%s%s", - c->ifi4 ? if_indextoname(c->ifi4, ifn) : "", - c->ifi4 ? " (IPv4)" : "", - (c->ifi4 && c->ifi6) ? ", " : "", - c->ifi6 ? if_indextoname(c->ifi6, ifn) : "", - c->ifi6 ? " (IPv6)" : ""); + if (c->ifi4 > 0 || c->ifi6 > 0) { + info("Template interface: %s%s%s%s%s", + c->ifi4 > 0 ? if_indextoname(c->ifi4, ifn) : "", + c->ifi4 > 0 ? " (IPv4)" : "", + (c->ifi4 && c->ifi6) ? ", " : "", + c->ifi6 > 0 ? if_indextoname(c->ifi6, ifn) : "", + c->ifi6 > 0 ? " (IPv6)" : ""); + } if (*c->ip4.ifname_out || *c->ip6.ifname_out) { info("Outbound interface: %s%s%s%s%s", @@ -955,11 +1026,14 @@ static void conf_print(const struct ctx *c) info("Namespace interface: %s", c->pasta_ifn); info("MAC:"); - info(" host: %02x:%02x:%02x:%02x:%02x:%02x", - c->mac[0], c->mac[1], c->mac[2], - c->mac[3], c->mac[4], c->mac[5]); + info(" host: %s", eth_ntop(c->our_tap_mac, bufmac, sizeof(bufmac))); if (c->ifi4) { + if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_host_loopback)) + info(" NAT to host 127.0.0.1: %s", + inet_ntop(AF_INET, &c->ip4.map_host_loopback, + buf4, sizeof(buf4))); + if (!c->no_dhcp) { uint32_t mask; @@ -971,7 +1045,8 @@ static void conf_print(const struct ctx *c) info(" mask: %s", inet_ntop(AF_INET, &mask, buf4, sizeof(buf4))); info(" router: %s", - inet_ntop(AF_INET, &c->ip4.gw, buf4, sizeof(buf4))); + inet_ntop(AF_INET, &c->ip4.guest_gw, + buf4, sizeof(buf4))); } for (i = 0; !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) { @@ -989,11 +1064,16 @@ static void conf_print(const struct ctx *c) } if (c->ifi6) { + if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback)) + info(" NAT to host ::1: %s", + inet_ntop(AF_INET6, &c->ip6.map_host_loopback, + buf6, sizeof(buf6))); + if (!c->no_ndp && !c->no_dhcpv6) info("NDP/DHCPv6:"); - else if (!c->no_ndp) - info("DHCPv6:"); else if (!c->no_dhcpv6) + info("DHCPv6:"); + else if (!c->no_ndp) info("NDP:"); else goto dns6; @@ -1001,9 +1081,10 @@ static void conf_print(const struct ctx *c) info(" assign: %s", inet_ntop(AF_INET6, &c->ip6.addr, buf6, sizeof(buf6))); info(" router: %s", - inet_ntop(AF_INET6, &c->ip6.gw, buf6, sizeof(buf6))); + inet_ntop(AF_INET6, &c->ip6.guest_gw, buf6, sizeof(buf6))); info(" our link-local: %s", - inet_ntop(AF_INET6, &c->ip6.addr_ll, buf6, sizeof(buf6))); + inet_ntop(AF_INET6, &c->ip6.our_tap_ll, + buf6, sizeof(buf6))); dns6: for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) { @@ -1122,6 +1203,38 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid) } /** + * conf_nat() - Parse --map-host-loopback or --map-guest-addr option + * @arg: String argument to option + * @addr4: IPv4 to update with parsed address + * @addr6: IPv6 to update with parsed address + * @no_map_gw: --no-map-gw flag, or NULL, updated for "none" argument + */ +static void conf_nat(const char *arg, struct in_addr *addr4, + struct in6_addr *addr6, int *no_map_gw) +{ + if (strcmp(arg, "none") == 0) { + *addr4 = in4addr_any; + *addr6 = in6addr_any; + if (no_map_gw) + *no_map_gw = 1; + } + + if (inet_pton(AF_INET6, arg, addr6) && + !IN6_IS_ADDR_UNSPECIFIED(addr6) && + !IN6_IS_ADDR_LOOPBACK(addr6) && + !IN6_IS_ADDR_MULTICAST(addr6)) + return; + + if (inet_pton(AF_INET, arg, addr4) && + !IN4_IS_ADDR_UNSPECIFIED(addr4) && + !IN4_IS_ADDR_LOOPBACK(addr4) && + !IN4_IS_ADDR_MULTICAST(addr4)) + return; + + die("Invalid address to remap to host: %s", optarg); +} + +/** * conf_open_files() - Open files as requested by configuration * @c: Execution context */ @@ -1130,7 +1243,11 @@ static void conf_open_files(struct ctx *c) if (c->mode != MODE_PASTA && c->fd_tap == -1) c->fd_tap_listen = tap_sock_unix_open(c->sock_path); - c->pidfile_fd = pidfile_open(c->pidfile); + if (*c->pidfile) { + c->pidfile_fd = output_file_open(c->pidfile, O_WRONLY); + if (c->pidfile_fd < 0) + die_perror("Couldn't open PID file %s", c->pidfile); + } } /** @@ -1174,7 +1291,7 @@ fail: */ void conf(struct ctx *c, int argc, char **argv) { - int netns_only = 0; + int netns_only = 0, no_map_gw = 0; const struct option options[] = { {"debug", no_argument, NULL, 'd' }, {"quiet", no_argument, NULL, 'q' }, @@ -1203,7 +1320,9 @@ void conf(struct ctx *c, int argc, char **argv) {"no-dhcpv6", no_argument, &c->no_dhcpv6, 1 }, {"no-ndp", no_argument, &c->no_ndp, 1 }, {"no-ra", no_argument, &c->no_ra, 1 }, - {"no-map-gw", no_argument, &c->no_map_gw, 1 }, + {"no-splice", no_argument, &c->no_splice, 1 }, + {"freebind", no_argument, &c->freebind, 1 }, + {"no-map-gw", no_argument, &no_map_gw, 1 }, {"ipv4-only", no_argument, NULL, '4' }, {"ipv6-only", no_argument, NULL, '6' }, {"one-off", no_argument, NULL, '1' }, @@ -1230,6 +1349,14 @@ void conf(struct ctx *c, int argc, char **argv) {"no-copy-routes", no_argument, NULL, 18 }, {"no-copy-addrs", no_argument, NULL, 19 }, {"netns-only", no_argument, NULL, 20 }, + {"map-host-loopback", required_argument, NULL, 21 }, + {"map-guest-addr", required_argument, NULL, 22 }, + {"host-lo-to-ns-lo", no_argument, NULL, 23 }, + {"dns-host", required_argument, NULL, 24 }, + {"vhost-user", no_argument, NULL, 25 }, + /* vhost-user backend program convention */ + {"print-capabilities", no_argument, NULL, 26 }, + {"socket-path", required_argument, NULL, 's' }, { 0 }, }; const char *logname = (c->mode == MODE_PASTA) ? "pasta" : "passt"; @@ -1237,8 +1364,7 @@ void conf(struct ctx *c, int argc, char **argv) bool copy_addrs_opt = false, copy_routes_opt = false; enum fwd_ports_mode fwd_default = FWD_NONE; bool v4_only = false, v6_only = false; - struct in6_addr *dns6 = c->ip6.dns; - struct in_addr *dns4 = c->ip4.dns; + unsigned dns4_idx = 0, dns6_idx = 0; struct fqdn *dnss = c->dns_search; unsigned int ifi4 = 0, ifi6 = 0; const char *logfile = NULL; @@ -1260,8 +1386,9 @@ void conf(struct ctx *c, int argc, char **argv) c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = FWD_UNSET; c->udp.fwd_in.mode = c->udp.fwd_out.mode = FWD_UNSET; + memcpy(c->our_tap_mac, MAC_OUR_LAA, ETH_ALEN); - optind = 1; + optind = 0; do { name = getopt_long(argc, argv, optstring, options, NULL); @@ -1290,7 +1417,7 @@ void conf(struct ctx *c, int argc, char **argv) if (c->mode != MODE_PASTA) die("--ns-mac-addr is for pasta mode only"); - parse_mac(c->mac_guest, optarg); + parse_mac(c->guest_mac, optarg); break; case 5: if (c->mode != MODE_PASTA) @@ -1352,9 +1479,9 @@ void conf(struct ctx *c, int argc, char **argv) break; case 14: - fprintf(stdout, + FPRINTF(stdout, c->mode == MODE_PASTA ? "pasta " : "passt "); - fprintf(stdout, VERSION_BLOB); + FPRINTF(stdout, VERSION_BLOB); exit(EXIT_SUCCESS); case 15: ret = snprintf(c->ip4.ifname_out, @@ -1399,6 +1526,38 @@ void conf(struct ctx *c, int argc, char **argv) netns_only = 1; *userns = 0; break; + case 21: + conf_nat(optarg, &c->ip4.map_host_loopback, + &c->ip6.map_host_loopback, &no_map_gw); + break; + case 22: + conf_nat(optarg, &c->ip4.map_guest_addr, + &c->ip6.map_guest_addr, NULL); + break; + case 23: + if (c->mode != MODE_PASTA) + die("--host-lo-to-ns-lo is for pasta mode only"); + c->host_lo_to_ns_lo = 1; + break; + case 24: + if (inet_pton(AF_INET6, optarg, &c->ip6.dns_host) && + !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_host)) + break; + + if (inet_pton(AF_INET, optarg, &c->ip4.dns_host) && + !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_host) && + !IN4_IS_ADDR_BROADCAST(&c->ip4.dns_host)) + break; + + die("Invalid host nameserver address: %s", optarg); + case 25: + if (c->mode == MODE_PASTA) + die("--vhost-user is for passt mode only"); + c->mode = MODE_VU; + break; + case 26: + vu_print_capabilities(); + break; case 'd': c->debug = 1; c->quiet = 0; @@ -1501,21 +1660,21 @@ void conf(struct ctx *c, int argc, char **argv) break; case 'M': - parse_mac(c->mac, optarg); + parse_mac(c->our_tap_mac, optarg); break; case 'g': - if (inet_pton(AF_INET6, optarg, &c->ip6.gw) && - !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw) && - !IN6_IS_ADDR_LOOPBACK(&c->ip6.gw)) { + if (inet_pton(AF_INET6, optarg, &c->ip6.guest_gw) && + !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.guest_gw) && + !IN6_IS_ADDR_LOOPBACK(&c->ip6.guest_gw)) { if (c->mode == MODE_PASTA) c->ip6.no_copy_routes = true; break; } - if (inet_pton(AF_INET, optarg, &c->ip4.gw) && - !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw) && - !IN4_IS_ADDR_BROADCAST(&c->ip4.gw) && - !IN4_IS_ADDR_LOOPBACK(&c->ip4.gw)) { + if (inet_pton(AF_INET, optarg, &c->ip4.guest_gw) && + !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.guest_gw) && + !IN4_IS_ADDR_BROADCAST(&c->ip4.guest_gw) && + !IN4_IS_ADDR_LOOPBACK(&c->ip4.guest_gw)) { if (c->mode == MODE_PASTA) c->ip4.no_copy_routes = true; break; @@ -1599,6 +1758,9 @@ void conf(struct ctx *c, int argc, char **argv) } } while (name != -1); + if (c->mode != MODE_PASTA) + c->no_splice = 1; + if (c->mode == MODE_PASTA && !c->pasta_conf_ns) { if (copy_routes_opt) die("--no-copy-routes needs --config-net"); @@ -1630,25 +1792,44 @@ void conf(struct ctx *c, int argc, char **argv) nl_sock_init(c, false); if (!v6_only) - c->ifi4 = conf_ip4(ifi4, &c->ip4, c->mac); + c->ifi4 = conf_ip4(ifi4, &c->ip4); if (!v4_only) - c->ifi6 = conf_ip6(ifi6, &c->ip6, c->mac); - if ((!c->ifi4 && !c->ifi6) || - (*c->ip4.ifname_out && !c->ifi4) || + c->ifi6 = conf_ip6(ifi6, &c->ip6); + if ((*c->ip4.ifname_out && !c->ifi4) || (*c->ip6.ifname_out && !c->ifi6)) die("External interface not usable"); + if (!c->ifi4 && !c->ifi6) { + info("No external interface as template, switch to local mode"); + + conf_ip4_local(&c->ip4); + c->ifi4 = -1; + + conf_ip6_local(&c->ip6); + c->ifi6 = -1; - if (c->ifi4 && IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw)) - c->no_map_gw = c->no_dhcp = 1; + if (!*c->pasta_ifn) { + strncpy(c->pasta_ifn, pasta_default_ifn, + sizeof(c->pasta_ifn) - 1); + } + } - if (c->ifi6 && IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw)) - c->no_map_gw = 1; + if (c->ifi4 && !no_map_gw && + IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_host_loopback)) + c->ip4.map_host_loopback = c->ip4.guest_gw; + + if (c->ifi6 && !no_map_gw && + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback)) + c->ip6.map_host_loopback = c->ip6.guest_gw; + + if (c->ifi4 && IN4_IS_ADDR_UNSPECIFIED(&c->ip4.guest_gw)) + c->no_dhcp = 1; /* Inbound port options & DNS can be parsed now (after IPv4/IPv6 * settings) */ + fwd_probe_ephemeral(); udp_portmap_clear(); - optind = 1; + optind = 0; do { name = getopt_long(argc, argv, optstring, options, NULL); @@ -1663,13 +1844,13 @@ void conf(struct ctx *c, int argc, char **argv) if (!strcmp(optarg, "none")) { c->no_dns = 1; - dns4 = &c->ip4.dns[0]; + dns4_idx = 0; memset(c->ip4.dns, 0, sizeof(c->ip4.dns)); c->ip4.dns[0] = (struct in_addr){ 0 }; c->ip4.dns_match = (struct in_addr){ 0 }; c->ip4.dns_host = (struct in_addr){ 0 }; - dns6 = &c->ip6.dns[0]; + dns6_idx = 0; memset(c->ip6.dns, 0, sizeof(c->ip6.dns)); c->ip6.dns_match = (struct in6_addr){ 0 }; c->ip6.dns_host = (struct in6_addr){ 0 }; @@ -1679,15 +1860,13 @@ void conf(struct ctx *c, int argc, char **argv) c->no_dns = 0; - if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) && - inet_pton(AF_INET, optarg, &dns4_tmp)) { - add_dns4(c, &dns4_tmp, &dns4); + if (inet_pton(AF_INET, optarg, &dns4_tmp)) { + dns4_idx += add_dns4(c, &dns4_tmp, dns4_idx); continue; } - if (dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) && - inet_pton(AF_INET6, optarg, &dns6_tmp)) { - add_dns6(c, &dns6_tmp, &dns6); + if (inet_pton(AF_INET6, optarg, &dns6_tmp)) { + dns6_idx += add_dns6(c, &dns6_tmp, dns6_idx); continue; } @@ -1720,7 +1899,7 @@ void conf(struct ctx *c, int argc, char **argv) nl_sock_init(c, true); /* ...and outbound port options now that namespaces are set up. */ - optind = 1; + optind = 0; do { name = getopt_long(argc, argv, optstring, options, NULL); @@ -1736,6 +1915,8 @@ void conf(struct ctx *c, int argc, char **argv) if (!c->ifi6) { c->no_ndp = 1; c->no_dhcpv6 = 1; + } else if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) { + c->no_dhcpv6 = 1; } if (!c->mtu) @@ -1744,9 +1925,9 @@ void conf(struct ctx *c, int argc, char **argv) get_dns(c); if (!*c->pasta_ifn) { - if (c->ifi4) + if (c->ifi4 > 0) if_indextoname(c->ifi4, c->pasta_ifn); - else + else if (c->ifi6 > 0) if_indextoname(c->ifi6, c->pasta_ifn); } |