aboutgitcodebugslistschat
path: root/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'conf.c')
-rw-r--r--conf.c1143
1 files changed, 631 insertions, 512 deletions
diff --git a/conf.c b/conf.c
index 27fa44a..091d46e 100644
--- a/conf.c
+++ b/conf.c
@@ -38,6 +38,7 @@
#include "ip.h"
#include "passt.h"
#include "netlink.h"
+#include "tap.h"
#include "udp.h"
#include "tcp.h"
#include "pasta.h"
@@ -46,6 +47,8 @@
#include "log.h"
#include "vhost_user.h"
+#define NETNS_RUN_DIR "/run/netns"
+
/**
* next_chunk - Return the next piece of a string delimited by a character
* @s: String to search
@@ -116,11 +119,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;
@@ -132,6 +134,11 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
return;
}
+ if ((optname == 't' || optname == 'T') && c->no_tcp)
+ die("TCP port forwarding requested but TCP is disabled");
+ if ((optname == 'u' || optname == 'U') && c->no_udp)
+ die("UDP port forwarding requested but UDP is disabled");
+
if (!strcmp(optarg, "auto")) {
if (fwd->mode)
goto mode_conflict;
@@ -151,19 +158,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)
@@ -204,14 +215,20 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
}
- if (ifname == buf + 1) /* Interface without address */
+ if (ifname == buf + 1) { /* Interface without address */
addr = NULL;
- else 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;
+
+ /* Allow square brackets for IPv4 too for convenience */
+ if (*p == '[' && p[strlen(p) - 1] == ']') {
+ p[strlen(p) - 1] = '\0';
+ p++;
+ }
+
+ if (!inany_pton(p, addr))
+ goto bad;
+ }
} else {
spec = buf;
@@ -244,20 +261,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)
@@ -313,9 +334,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;
}
@@ -338,55 +359,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);
+ }
}
/**
@@ -395,17 +454,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;
+ ssize_t line_len;
char *line, *end;
const char *p;
- int line_len;
- 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;
@@ -426,15 +484,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);
-
- 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);
+ 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");
@@ -448,7 +500,7 @@ static void get_dns(struct ctx *c)
while (s - c->dns_search < ARRAY_SIZE(c->dns_search) - 1
/* cppcheck-suppress strtokCalled */
&& (p = strtok(NULL, " \t"))) {
- strncpy(s->n, p, sizeof(c->dns_search[0]));
+ strncpy(s->n, p, sizeof(c->dns_search[0]) - 1);
s++;
*s->n = 0;
}
@@ -456,12 +508,25 @@ static void get_dns(struct ctx *c)
}
if (line_len < 0)
- warn("Error reading /etc/resolv.conf: %s", strerror(errno));
+ warn_perror("Error reading /etc/resolv.conf");
close(fd);
out:
- if (!dns_set && dns4 == c->ip4.dns && dns6 == c->ip6.dns)
- warn("Couldn't get any nameserver address");
+ if (!dns_set) {
+ if (!(dns4_idx + dns6_idx))
+ warn("Couldn't get any nameserver address");
+
+ if (c->no_dhcp_dns)
+ return;
+
+ if (c->ifi4 && !c->no_dhcp &&
+ IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[0]))
+ warn("No IPv4 nameserver available for DHCP");
+
+ if (c->ifi6 && ((!c->no_ndp && !c->no_ra) || !c->no_dhcpv6) &&
+ IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[0]))
+ warn("No IPv6 nameserver available for NDP/DHCPv6");
+ }
}
/**
@@ -499,9 +564,6 @@ static void conf_netns_opt(char *netns, const char *arg)
static void conf_pasta_ns(int *netns_only, char *userns, char *netns,
int optind, int argc, char *argv[])
{
- if (*netns_only && *userns)
- die("Both --userns and --netns-only given");
-
if (*netns && optind != argc)
die("Both --netns and PID or command given");
@@ -515,10 +577,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");
+ }
}
}
@@ -556,23 +623,22 @@ 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("No interface with a default route for IPv4: disabling IPv4");
+ info("Couldn't pick external interface: disabling 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));
@@ -602,20 +668,11 @@ static unsigned int conf_ip4(unsigned int ifi,
ip4->prefix_len = 32;
}
- memcpy(&ip4->addr_seen, &ip4->addr, sizeof(ip4->addr_seen));
+ ip4->addr_seen = ip4->addr;
- 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 for %s: %s",
- if_indextoname(ifi, ifname), strerror(-rc));
- return 0;
- }
- }
+ ip4->our_tap_addr = ip4->guest_gw;
- if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr) ||
- MAC_IS_ZERO(mac))
+ if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr))
return 0;
return ifi;
@@ -625,12 +682,10 @@ static unsigned int conf_ip4(unsigned int ifi,
* 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;
@@ -639,12 +694,12 @@ static unsigned int conf_ip6(unsigned int ifi,
ifi = nl_get_ext_if(nl_sock, AF_INET6);
if (!ifi) {
- info("No interface with a default route for IPv6: disabling IPv6");
+ info("Couldn't pick external interface: disabling 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));
@@ -654,246 +709,249 @@ static unsigned int conf_ip6(unsigned int ifi,
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));
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 for %s: %s",
- if_indextoname(ifi, ifname), strerror(-rc));
- return 0;
- }
- }
+ 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) ||
- MAC_IS_ZERO(mac))
+ IN6_IS_ADDR_UNSPECIFIED(&ip6->our_tap_ll))
return 0;
return ifi;
}
/**
- * print_usage() - Print usage, exit with given status code
+ * usage() - Print usage, exit with given status code
* @name: Executable name
+ * @f: Stream to print usage info to
* @status: Status code for exit()
*/
-static void print_usage(const char *name, int status)
+static void usage(const char *name, FILE *f, int status)
{
if (strstr(name, "pasta")) {
- info("Usage: %s [OPTION]... [COMMAND] [ARGS]...", name);
- info(" %s [OPTION]... PID", name);
- info(" %s [OPTION]... --netns [PATH|NAME]", name);
- info("");
- info("Without PID or --netns, run the given command or a");
- info("default shell in a new network and user namespace, and");
- info("connect it via 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,
+ "\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 {
- info("Usage: %s [OPTION]...", name);
+ FPRINTF(f, "Usage: %s [OPTION]...\n", name);
}
- info("");
-
-
- info( " -d, --debug Be verbose");
- info( " --trace Be extra verbose, implies --debug");
- info( " -q, --quiet Don't print informational messages");
- info( " -f, --foreground Don't run in background");
- info( " default: run in background if started from a TTY");
- info( " -e, --stderr Log to stderr too");
- info( " default: log to system logger only if started from a TTY");
- info( " -l, --log-file PATH Log (only) to given file");
- info( " --log-size BYTES Maximum size of log file");
- info( " default: 1 MiB");
- info( " --runas UID|UID:GID Run as given UID, GID, which can be");
- info( " numeric, or login and group names");
- info( " default: drop to user \"nobody\"");
- info( " -h, --help Display this help message and exit");
- info( " --version Show version and exit");
+
+ FPRINTF(f,
+ "\n"
+ " -d, --debug Be verbose\n"
+ " --trace Be extra verbose, implies --debug\n"
+ " -q, --quiet Don't print informational messages\n"
+ " -f, --foreground Don't run in background\n"
+ " default: run in background\n"
+ " -l, --log-file PATH Log (only) to given file\n"
+ " --log-size BYTES Maximum size of log file\n"
+ " default: 1 MiB\n"
+ " --runas UID|UID:GID Run as given UID, GID, which can be\n"
+ " numeric, or login and group names\n"
+ " default: drop to user \"nobody\"\n"
+ " -h, --help Display this help message and exit\n"
+ " --version Show version and exit\n");
if (strstr(name, "pasta")) {
- info( " -I, --ns-ifname NAME namespace interface name");
- info( " default: same interface name as external one");
+ FPRINTF(f,
+ " -I, --ns-ifname NAME namespace interface name\n"
+ " default: same interface name as external one\n");
} else {
- info( " -s, --socket, --socket-path PATH UNIX domain socket path");
- info( " default: probe free path starting from "
- UNIX_SOCK_PATH, 1);
- info( " --vhost-user Enable vhost-user mode");
- info( " UNIX domain socket is provided by -s option");
- info( " --print-capabilities print back-end capabilities in JSON format");
+ 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");
}
- info( " -F, --fd FD Use FD as pre-opened connected socket");
- info( " -p, --pcap FILE Log tap-facing traffic to pcap file");
- info( " -P, --pid FILE Write own PID to the given file");
- info( " -m, --mtu MTU Assign MTU via DHCP/NDP");
- info( " a zero value disables assignment");
- info( " default: 65520: maximum 802.3 MTU minus 802.3 header");
- info( " length, rounded to 32 bits (IPv4 words)");
- info( " -a, --address ADDR Assign IPv4 or IPv6 address ADDR");
- info( " can be specified zero to two times (for IPv4 and IPv6)");
- info( " default: use addresses from interface with default route");
- info( " -n, --netmask MASK Assign IPv4 MASK, dot-decimal or bits");
- info( " default: netmask from matching address on the host");
- info( " -M, --mac-addr ADDR Use source MAC address ADDR");
- info( " default: MAC address from interface with default route");
- info( " -g, --gateway ADDR Pass IPv4 or IPv6 address as gateway");
- info( " default: gateway from interface with default route");
- info( " -i, --interface NAME Interface for addresses and routes");
- info( " default: from --outbound-if4 and --outbound-if6, if any");
- info( " otherwise interface with first default route");
- info( " -o, --outbound ADDR Bind to address as outbound source");
- info( " can be specified zero to two times (for IPv4 and IPv6)");
- info( " default: use source address from routing tables");
- info( " --outbound-if4 NAME Bind to outbound interface for IPv4");
- info( " default: use interface from default route");
- info( " --outbound-if6 NAME Bind to outbound interface for IPv6");
- info( " default: use interface from default route");
- info( " -D, --dns ADDR Use IPv4 or IPv6 address as DNS");
- info( " can be specified multiple times");
- info( " a single, empty option disables DNS information");
+ 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"
+ " -m, --mtu MTU Assign MTU via DHCP/NDP\n"
+ " a zero value disables assignment\n"
+ " default: 65520: maximum 802.3 MTU minus 802.3 header\n"
+ " length, rounded to 32 bits (IPv4 words)\n"
+ " -a, --address ADDR Assign IPv4 or IPv6 address ADDR\n"
+ " can be specified zero to two times (for IPv4 and IPv6)\n"
+ " default: use addresses from interface with default route\n"
+ " -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"
+ " -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"
+ " default: from --outbound-if4 and --outbound-if6, if any\n"
+ " otherwise interface with first default route\n"
+ " -o, --outbound ADDR Bind to address as outbound source\n"
+ " can be specified zero to two times (for IPv4 and IPv6)\n"
+ " default: use source address from routing tables\n"
+ " --outbound-if4 NAME Bind to outbound interface for IPv4\n"
+ " default: use interface from default route\n"
+ " --outbound-if6 NAME Bind to outbound interface for IPv6\n"
+ " default: use interface from default route\n"
+ " -D, --dns ADDR Use IPv4 or IPv6 address as DNS\n"
+ " can be specified multiple times\n"
+ " a single, empty option disables DNS information\n");
if (strstr(name, "pasta"))
- info( " default: don't use any addresses");
+ FPRINTF(f, " default: don't use any addresses\n");
else
- info( " default: use addresses from /etc/resolv.conf");
-
- info( " -S, --search LIST Space-separated list, search domains");
- info( " a single, empty option disables the DNS search list");
+ 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"))
- info( " default: don't use any search list");
+ FPRINTF(f, " default: don't use any search list\n");
else
- info( " default: use search list from /etc/resolv.conf");
+ FPRINTF(f, " default: use search list from /etc/resolv.conf\n");
if (strstr(name, "pasta"))
- info(" --dhcp-dns \tPass DNS list via DHCP/DHCPv6/NDP");
+ FPRINTF(f, " --dhcp-dns \tPass DNS list via DHCP/DHCPv6/NDP\n");
else
- info(" --no-dhcp-dns No DNS list in DHCP/DHCPv6/NDP");
+ FPRINTF(f, " --no-dhcp-dns No DNS list in DHCP/DHCPv6/NDP\n");
if (strstr(name, "pasta"))
- info(" --dhcp-search Pass list via DHCP/DHCPv6/NDP");
+ FPRINTF(f, " --dhcp-search Pass list via DHCP/DHCPv6/NDP\n");
else
- info(" --no-dhcp-search No list in DHCP/DHCPv6/NDP");
-
- info( " --dns-forward ADDR Forward DNS queries sent to ADDR");
- info( " can be specified zero to two times (for IPv4 and IPv6)");
- info( " default: don't forward DNS queries");
-
- info( " --no-tcp Disable TCP protocol handler");
- info( " --no-udp Disable UDP protocol handler");
- info( " --no-icmp Disable ICMP/ICMPv6 protocol handler");
- info( " --no-dhcp Disable DHCP server");
- info( " --no-ndp Disable NDP responses");
- info( " --no-dhcpv6 Disable DHCPv6 server");
- info( " --no-ra Disable router advertisements");
- info( " --no-map-gw Don't map gateway address to host");
- info( " -4, --ipv4-only Enable IPv4 operation only");
- info( " -6, --ipv6-only Enable IPv6 operation only");
+ 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"
+ " --no-dhcp Disable DHCP server\n"
+ " --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");
if (strstr(name, "pasta"))
goto pasta_opts;
- info( " -1, --one-off Quit after handling one single client");
- info( " -t, --tcp-ports SPEC TCP port forwarding to guest");
- info( " can be specified multiple times");
- info( " SPEC can be:");
- 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 ':', with optional");
- info( " address specification suffixed by '/' and optional");
- info( " interface prefixed by '%%'. Ranges can be reduced by");
- info( " excluding ports or ranges prefixed by '~'");
- info( " 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( " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25");
- info( " -t ~25 Forward all ports except for 25");
- info( " default: none");
- info( " -u, --udp-ports SPEC UDP port forwarding to guest");
- info( " SPEC is as described for TCP above");
- info( " default: none");
+ 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"
+ " SPEC can be:\n"
+ " 'none': don't forward any ports\n"
+ " 'all': forward all unbound, non-ephemeral ports\n"
+ " a comma-separated list, optionally ranged with '-'\n"
+ " and optional target ports after ':', with optional\n"
+ " address specification suffixed by '/' and optional\n"
+ " interface prefixed by '%%'. Ranges can be reduced by\n"
+ " excluding ports or ranges prefixed by '~'\n"
+ " Examples:\n"
+ " -t 22 Forward local port 22 to 22 on guest\n"
+ " -t 22:23 Forward local port 22 to 23 on guest\n"
+ " -t 22,25 Forward ports 22, 25 to ports 22, 25\n"
+ " -t 22-80 Forward ports 22 to 80\n"
+ " -t 22-80:32-90 Forward ports 22 to 80 to\n"
+ " corresponding port numbers plus 10\n"
+ " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to guest\n"
+ " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25\n"
+ " -t ~25 Forward all ports except for 25\n"
+ " default: none\n"
+ " -u, --udp-ports SPEC UDP port forwarding to guest\n"
+ " SPEC is as described for TCP above\n"
+ " default: none\n");
exit(status);
pasta_opts:
- info( " -t, --tcp-ports SPEC TCP port forwarding to namespace");
- info( " can be specified multiple times");
- info( " SPEC can be:");
- 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 ':', with optional");
- info( " address specification suffixed by '/' and optional");
- info( " interface prefixed 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( " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25");
- info( " -t ~25 Forward all bound ports except for 25");
- info( " default: auto");
- info( " IPv6 bound ports are also forwarded for IPv4");
- info( " -u, --udp-ports SPEC UDP port forwarding to namespace");
- info( " SPEC is as described for TCP above");
- info( " default: auto");
- info( " IPv6 bound ports are also forwarded for IPv4");
- info( " unless specified, with '-t auto', UDP ports with numbers");
- info( " corresponding to forwarded TCP port numbers are");
- info( " forwarded too");
- info( " -T, --tcp-ns SPEC TCP port forwarding to init namespace");
- info( " SPEC is as described above");
- info( " default: auto");
- info( " -U, --udp-ns SPEC UDP port forwarding to init namespace");
- info( " SPEC is as described above");
- info( " default: auto");
- info( " --userns NSPATH Target user namespace to join");
- info( " --netns PATH|NAME Target network namespace to join");
- info( " --netns-only Don't join existing user namespace");
- info( " implied if PATH or NAME are given without --userns");
- info( " --no-netns-quit Don't quit if filesystem-bound target");
- info( " network namespace is deleted");
- info( " --config-net Configure tap interface in namespace");
- info( " --no-copy-routes DEPRECATED:");
- info( " Don't copy all routes to namespace");
- info( " --no-copy-addrs DEPRECATED:");
- info( " Don't copy all addresses to namespace");
- info( " --ns-mac-addr ADDR Set MAC address on tap interface");
+ FPRINTF(f,
+ " -t, --tcp-ports SPEC TCP port forwarding to namespace\n"
+ " can be specified multiple times\n"
+ " SPEC can be:\n"
+ " 'none': don't forward any ports\n"
+ " 'auto': forward all ports currently bound in namespace\n"
+ " a comma-separated list, optionally ranged with '-'\n"
+ " and optional target ports after ':', with optional\n"
+ " address specification suffixed by '/' and optional\n"
+ " interface prefixed by '%%'. Examples:\n"
+ " -t 22 Forward local port 22 to port 22 in netns\n"
+ " -t 22:23 Forward local port 22 to port 23\n"
+ " -t 22,25 Forward ports 22, 25 to ports 22, 25\n"
+ " -t 22-80 Forward ports 22 to 80\n"
+ " -t 22-80:32-90 Forward ports 22 to 80 to\n"
+ " corresponding port numbers plus 10\n"
+ " -t 192.0.2.1/5 Bind port 5 of 192.0.2.1 to namespace\n"
+ " -t 5-25,~10-20 Forward ports 5 to 9, and 21 to 25\n"
+ " -t ~25 Forward all bound ports except for 25\n"
+ " default: auto\n"
+ " IPv6 bound ports are also forwarded for IPv4\n"
+ " -u, --udp-ports SPEC UDP port forwarding to namespace\n"
+ " SPEC is as described for TCP above\n"
+ " default: auto\n"
+ " IPv6 bound ports are also forwarded for IPv4\n"
+ " unless specified, with '-t auto', UDP ports with numbers\n"
+ " corresponding to forwarded TCP port numbers are\n"
+ " forwarded too\n"
+ " -T, --tcp-ns SPEC TCP port forwarding to init namespace\n"
+ " SPEC is as described above\n"
+ " default: auto\n"
+ " -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"
+ " implied if PATH or NAME are given without --userns\n"
+ " --no-netns-quit Don't quit if filesystem-bound target\n"
+ " network namespace is deleted\n"
+ " --config-net Configure tap interface in namespace\n"
+ " --no-copy-routes DEPRECATED:\n"
+ " 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");
exit(status);
}
/**
- * usage() - Print usage and exit with failure
- * @name: Executable name
- */
-static void usage(const char *name)
-{
- print_usage(name, EXIT_FAILURE);
-}
-
-/**
* conf_print() - Print fundamental configuration parameters
* @c: Execution context
*/
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",
@@ -927,11 +985,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;
@@ -943,7 +1004,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++) {
@@ -961,6 +1023,11 @@ 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)
@@ -973,9 +1040,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++) {
@@ -1075,16 +1143,14 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid)
return;
/* ...otherwise use nobody:nobody */
- warn("Don't run as root. Changing to nobody...");
+ warn("Started as root, will change to nobody.");
{
#ifndef GLIBC_NO_STATIC_NSS
const struct passwd *pw;
/* cppcheck-suppress getpwnamCalled */
pw = getpwnam("nobody");
- if (!pw) {
- perror("getpwnam");
- exit(EXIT_FAILURE);
- }
+ if (!pw)
+ die_perror("Can't get password file entry for nobody");
*uid = pw->pw_uid;
*gid = pw->pw_gid;
@@ -1096,6 +1162,87 @@ 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
+ */
+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);
+
+ 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);
+ }
+}
+
+/**
+ * parse_mac - Parse a MAC address from a string
+ * @mac: Binary MAC address, initialised on success
+ * @str: String to parse
+ *
+ * Parses @str as an Ethernet MAC address stored in @mac on success. Exits on
+ * failure.
+ */
+static void parse_mac(unsigned char mac[ETH_ALEN], const char *str)
+{
+ size_t i;
+
+ if (strlen(str) != (ETH_ALEN * 3 - 1))
+ goto fail;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ const char *octet = str + 3 * i;
+ unsigned long b;
+ char *end;
+
+ errno = 0;
+ b = strtoul(octet, &end, 16);
+ if (b > UCHAR_MAX || errno || end != octet + 2 ||
+ *end != ((i == ETH_ALEN - 1) ? '\0' : ':'))
+ goto fail;
+ mac[i] = b;
+ }
+ return;
+
+fail:
+ die("Invalid MAC address: %s", str);
+}
+
+/**
* conf() - Process command-line arguments and set configuration
* @c: Execution context
* @argc: Argument count
@@ -1103,7 +1250,7 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid)
*/
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' },
@@ -1113,7 +1260,6 @@ void conf(struct ctx *c, int argc, char **argv)
{"help", no_argument, NULL, 'h' },
{"socket", required_argument, NULL, 's' },
{"fd", required_argument, NULL, 'F' },
- {"socket-path", required_argument, NULL, 's' }, /* vhost-user mandatory */
{"ns-ifname", required_argument, NULL, 'I' },
{"pcap", required_argument, NULL, 'p' },
{"pid", required_argument, NULL, 'P' },
@@ -1133,7 +1279,8 @@ 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 },
+ {"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' },
@@ -1143,7 +1290,6 @@ void conf(struct ctx *c, int argc, char **argv)
{"udp-ns", required_argument, NULL, 'U' },
{"userns", required_argument, NULL, 2 },
{"netns", required_argument, NULL, 3 },
- {"netns-only", no_argument, &netns_only, 1 },
{"ns-mac-addr", required_argument, NULL, 4 },
{"dhcp-dns", no_argument, NULL, 5 },
{"no-dhcp-dns", no_argument, NULL, 6 },
@@ -1160,37 +1306,47 @@ void conf(struct ctx *c, int argc, char **argv)
{"config-net", no_argument, NULL, 17 },
{"no-copy-routes", no_argument, NULL, 18 },
{"no-copy-addrs", no_argument, NULL, 19 },
- {"vhost-user", no_argument, NULL, 20 },
- {"print-capabilities", no_argument, NULL, 21 }, /* vhost-user mandatory */
+ {"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";
char userns[PATH_MAX] = { 0 }, netns[PATH_MAX] = { 0 };
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;
+ unsigned dns4_idx = 0, dns6_idx = 0;
struct fqdn *dnss = c->dns_search;
- struct in_addr *dns4 = c->ip4.dns;
unsigned int ifi4 = 0, ifi6 = 0;
const char *logfile = NULL;
const char *optstring;
- int name, ret, b, i;
size_t logsize = 0;
char *runas = NULL;
+ long fd_tap_opt;
+ int name, ret;
uid_t uid;
gid_t gid;
if (c->mode == MODE_PASTA) {
c->no_dhcp_dns = c->no_dhcp_dns_search = 1;
fwd_default = FWD_AUTO;
- optstring = "dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:";
+ optstring = "+dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:";
} else {
- optstring = "dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:";
+ optstring = "+dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:";
}
- c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = 0;
- c->udp.fwd_in.f.mode = c->udp.fwd_out.f.mode = 0;
+ 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 = 0;
do {
name = getopt_long(argc, argv, optstring, options, NULL);
@@ -1206,6 +1362,8 @@ void conf(struct ctx *c, int argc, char **argv)
if (ret <= 0 || ret >= (int)sizeof(userns))
die("Invalid userns: %s", optarg);
+ netns_only = 0;
+
break;
case 3:
if (c->mode != MODE_PASTA)
@@ -1217,14 +1375,7 @@ void conf(struct ctx *c, int argc, char **argv)
if (c->mode != MODE_PASTA)
die("--ns-mac-addr is for pasta mode only");
- for (i = 0; i < ETH_ALEN; i++) {
- errno = 0;
- b = strtol(optarg + (intptr_t)i * 3, NULL, 16);
- if (b < 0 || b > UCHAR_MAX || errno)
- die("Invalid MAC address: %s", optarg);
-
- c->mac_guest[i] = b;
- }
+ parse_mac(c->guest_mac, optarg);
break;
case 5:
if (c->mode != MODE_PASTA)
@@ -1251,14 +1402,12 @@ void conf(struct ctx *c, int argc, char **argv)
c->no_dhcp_dns_search = 1;
break;
case 9:
- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) &&
- inet_pton(AF_INET6, optarg, &c->ip6.dns_match) &&
+ if (inet_pton(AF_INET6, optarg, &c->ip6.dns_match) &&
!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns_match) &&
!IN6_IS_ADDR_LOOPBACK(&c->ip6.dns_match))
break;
- if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) &&
- inet_pton(AF_INET, optarg, &c->ip4.dns_match) &&
+ if (inet_pton(AF_INET, optarg, &c->ip4.dns_match) &&
!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns_match) &&
!IN4_IS_ADDR_BROADCAST(&c->ip4.dns_match) &&
!IN4_IS_ADDR_LOOPBACK(&c->ip4.dns_match))
@@ -1273,24 +1422,13 @@ void conf(struct ctx *c, int argc, char **argv)
c->no_netns_quit = 1;
break;
case 11:
- if (c->trace)
- die("Multiple --trace options given");
-
- if (c->quiet)
- die("Either --trace or --quiet");
-
c->trace = c->debug = 1;
+ c->quiet = 0;
break;
case 12:
- if (runas)
- die("Multiple --runas options given");
-
runas = optarg;
break;
case 13:
- if (logsize)
- die("Multiple --log-size options given");
-
errno = 0;
logsize = strtol(optarg, NULL, 0);
@@ -1299,14 +1437,11 @@ 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:
- if (*c->ip4.ifname_out)
- die("Redundant outbound interface: %s", optarg);
-
ret = snprintf(c->ip4.ifname_out,
sizeof(c->ip4.ifname_out), "%s", optarg);
if (ret <= 0 || ret >= (int)sizeof(c->ip4.ifname_out))
@@ -1314,13 +1449,11 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case 16:
- if (*c->ip6.ifname_out)
- die("Redundant outbound interface: %s", optarg);
-
ret = snprintf(c->ip6.ifname_out,
sizeof(c->ip6.ifname_out), "%s", optarg);
if (ret <= 0 || ret >= (int)sizeof(c->ip6.ifname_out))
die("Invalid interface name: %s", optarg);
+
break;
case 17:
if (c->mode != MODE_PASTA)
@@ -1333,94 +1466,96 @@ void conf(struct ctx *c, int argc, char **argv)
die("--no-copy-routes is for pasta mode only");
warn("--no-copy-routes will be dropped soon");
- c->no_copy_routes = copy_routes_opt = true;
+ c->ip4.no_copy_routes = c->ip6.no_copy_routes = true;
+ copy_routes_opt = true;
break;
case 19:
if (c->mode != MODE_PASTA)
die("--no-copy-addrs is for pasta mode only");
warn("--no-copy-addrs will be dropped soon");
- c->no_copy_addrs = copy_addrs_opt = true;
+ c->ip4.no_copy_addrs = c->ip6.no_copy_addrs = true;
+ copy_addrs_opt = true;
break;
case 20:
+ if (c->mode != MODE_PASTA)
+ die("--netns-only is for pasta mode only");
+
+ 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) {
err("--vhost-user is for passt mode only");
- usage(argv[0]);
+ usage(argv[0], stdout, EXIT_SUCCESS);
}
c->mode = MODE_VU;
break;
- case 21:
+ case 26:
vu_print_capabilities();
break;
case 'd':
- if (c->debug)
- die("Multiple --debug options given");
-
- if (c->quiet)
- die("Either --debug or --quiet");
-
c->debug = 1;
+ c->quiet = 0;
break;
case 'e':
- if (logfile)
- die("Can't log to both file and stderr");
-
- if (c->force_stderr)
- die("Multiple --stderr options given");
-
- c->force_stderr = 1;
+ warn("--stderr will be dropped soon");
break;
case 'l':
- if (c->force_stderr)
- die("Can't log to both stderr and file");
-
- if (logfile)
- die("Multiple --log-file options given");
-
logfile = optarg;
break;
case 'q':
- if (c->quiet)
- die("Multiple --quiet options given");
-
- if (c->debug)
- die("Either --debug or --quiet");
-
c->quiet = 1;
+ c->debug = c->trace = 0;
break;
case 'f':
- if (c->foreground)
- die("Multiple --foreground options given");
-
c->foreground = 1;
break;
case 's':
- if (*c->sock_path)
- die("Multiple --socket options given");
-
- ret = snprintf(c->sock_path, UNIX_SOCK_MAX - 1, "%s",
+ ret = snprintf(c->sock_path, sizeof(c->sock_path), "%s",
optarg);
if (ret <= 0 || ret >= (int)sizeof(c->sock_path))
die("Invalid socket path: %s", optarg);
+ c->fd_tap = -1;
break;
case 'F':
- if (c->fd_tap >= 0)
- die("Multiple --fd options given");
-
errno = 0;
- c->fd_tap = strtol(optarg, NULL, 0);
+ fd_tap_opt = strtol(optarg, NULL, 0);
- if (c->fd_tap < 0 || errno)
+ if (errno ||
+ fd_tap_opt <= STDERR_FILENO || fd_tap_opt > INT_MAX)
die("Invalid --fd: %s", optarg);
+ c->fd_tap = fd_tap_opt;
c->one_off = true;
-
+ *c->sock_path = 0;
break;
case 'I':
- if (*c->pasta_ifn)
- die("Multiple --ns-ifname options given");
-
ret = snprintf(c->pasta_ifn, IFNAMSIZ, "%s",
optarg);
if (ret <= 0 || ret >= IFNAMSIZ)
@@ -1428,28 +1563,19 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case 'p':
- if (*c->pcap)
- die("Multiple --pcap options given");
-
ret = snprintf(c->pcap, sizeof(c->pcap), "%s", optarg);
if (ret <= 0 || ret >= (int)sizeof(c->pcap))
die("Invalid pcap path: %s", optarg);
break;
case 'P':
- if (*c->pid_file)
- die("Multiple --pid options given");
-
- ret = snprintf(c->pid_file, sizeof(c->pid_file), "%s",
+ ret = snprintf(c->pidfile, sizeof(c->pidfile), "%s",
optarg);
- if (ret <= 0 || ret >= (int)sizeof(c->pid_file))
+ if (ret <= 0 || ret >= (int)sizeof(c->pidfile))
die("Invalid PID file: %s", optarg);
break;
case 'm':
- if (c->mtu)
- die("Multiple --mtu options given");
-
errno = 0;
c->mtu = strtol(optarg, NULL, 0);
@@ -1464,25 +1590,26 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case 'a':
- if (c->mode == MODE_PASTA)
- c->no_copy_addrs = 1;
-
- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr) &&
- inet_pton(AF_INET6, optarg, &c->ip6.addr) &&
+ if (inet_pton(AF_INET6, optarg, &c->ip6.addr) &&
!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr) &&
!IN6_IS_ADDR_LOOPBACK(&c->ip6.addr) &&
!IN6_IS_ADDR_V4MAPPED(&c->ip6.addr) &&
!IN6_IS_ADDR_V4COMPAT(&c->ip6.addr) &&
- !IN6_IS_ADDR_MULTICAST(&c->ip6.addr))
+ !IN6_IS_ADDR_MULTICAST(&c->ip6.addr)) {
+ if (c->mode == MODE_PASTA)
+ c->ip6.no_copy_addrs = true;
break;
+ }
- if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr) &&
- inet_pton(AF_INET, optarg, &c->ip4.addr) &&
+ if (inet_pton(AF_INET, optarg, &c->ip4.addr) &&
!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr) &&
!IN4_IS_ADDR_BROADCAST(&c->ip4.addr) &&
!IN4_IS_ADDR_LOOPBACK(&c->ip4.addr) &&
- !IN4_IS_ADDR_MULTICAST(&c->ip4.addr))
+ !IN4_IS_ADDR_MULTICAST(&c->ip4.addr)) {
+ if (c->mode == MODE_PASTA)
+ c->ip4.no_copy_addrs = true;
break;
+ }
die("Invalid address: %s", optarg);
break;
@@ -1493,45 +1620,34 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case 'M':
- for (i = 0; i < ETH_ALEN; i++) {
- errno = 0;
- b = strtol(optarg + (intptr_t)i * 3, NULL, 16);
- if (b < 0 || b > UCHAR_MAX || errno)
- die("Invalid MAC address: %s", optarg);
-
- c->mac[i] = b;
- }
+ parse_mac(c->our_tap_mac, optarg);
break;
case 'g':
- if (c->mode == MODE_PASTA)
- c->no_copy_routes = 1;
-
- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw) &&
- 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 (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw) &&
- 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;
+ }
die("Invalid gateway address: %s", optarg);
break;
case 'i':
- if (ifi4 || ifi6)
- die("Redundant interface: %s", optarg);
-
if (!(ifi4 = ifi6 = if_nametoindex(optarg)))
- die("Invalid interface name %s: %s", optarg,
- strerror(errno));
+ die_perror("Invalid interface name %s", optarg);
break;
case 'o':
- if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) &&
- inet_pton(AF_INET6, optarg, &c->ip6.addr_out) &&
+ if (inet_pton(AF_INET6, optarg, &c->ip6.addr_out) &&
!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) &&
!IN6_IS_ADDR_LOOPBACK(&c->ip6.addr_out) &&
!IN6_IS_ADDR_V4MAPPED(&c->ip6.addr_out) &&
@@ -1539,8 +1655,7 @@ void conf(struct ctx *c, int argc, char **argv)
!IN6_IS_ADDR_MULTICAST(&c->ip6.addr_out))
break;
- if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
- inet_pton(AF_INET, optarg, &c->ip4.addr_out) &&
+ if (inet_pton(AF_INET, optarg, &c->ip4.addr_out) &&
!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
!IN4_IS_ADDR_BROADCAST(&c->ip4.addr_out) &&
!IN4_IS_ADDR_MULTICAST(&c->ip4.addr_out))
@@ -1549,49 +1664,16 @@ void conf(struct ctx *c, int argc, char **argv)
die("Invalid or redundant outbound address: %s",
optarg);
break;
- case 'D':
- if (!strcmp(optarg, "none")) {
- if (c->no_dns)
- die("Redundant DNS options");
-
- if (dns4 - c->ip4.dns || dns6 - c->ip6.dns)
- die("Conflicting DNS options");
-
- c->no_dns = 1;
- break;
- }
-
- if (c->no_dns)
- die("Conflicting DNS options");
-
- if (dns4 - &c->ip4.dns[0] < ARRAY_SIZE(c->ip4.dns) &&
- inet_pton(AF_INET, optarg, dns4)) {
- dns4++;
- break;
- }
-
- if (dns6 - &c->ip6.dns[0] < ARRAY_SIZE(c->ip6.dns) &&
- inet_pton(AF_INET6, optarg, dns6)) {
- dns6++;
- break;
- }
-
- die("Cannot use DNS address %s", optarg);
- break;
case 'S':
if (!strcmp(optarg, "none")) {
- if (c->no_dns_search)
- die("Redundant DNS search options");
+ c->no_dns_search = 1;
- if (dnss != c->dns_search)
- die("Conflicting DNS search options");
+ memset(c->dns_search, 0, sizeof(c->dns_search));
- c->no_dns_search = 1;
break;
}
- if (c->no_dns_search)
- die("Conflicting DNS search options");
+ c->no_dns_search = 0;
if (dnss - c->dns_search < ARRAY_SIZE(c->dns_search)) {
ret = snprintf(dnss->n, sizeof(*c->dns_search),
@@ -1607,42 +1689,35 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case '4':
v4_only = true;
+ v6_only = false;
break;
case '6':
v6_only = true;
+ v4_only = false;
break;
case '1':
if (c->mode == MODE_PASTA)
die("--one-off is for passt mode only");
- if (c->one_off)
- die("Redundant --one-off option");
-
c->one_off = true;
break;
case 't':
case 'u':
case 'T':
case 'U':
+ case 'D':
/* Handle these later, once addresses are configured */
break;
case 'h':
- log_to_stdout = 1;
- print_usage(argv[0], EXIT_SUCCESS);
+ usage(argv[0], stdout, EXIT_SUCCESS);
break;
case '?':
default:
- usage(argv[0]);
+ usage(argv[0], stderr, EXIT_FAILURE);
break;
}
} while (name != -1);
- if (v4_only && v6_only)
- die("Options ipv4-only and ipv6-only are mutually exclusive");
-
- if (*c->sock_path && c->fd_tap >= 0)
- die("Options --socket and --fd are mutually exclusive");
-
if (c->mode == MODE_PASTA && !c->pasta_conf_ns) {
if (copy_routes_opt)
die("--no-copy-routes needs --config-net");
@@ -1658,14 +1733,11 @@ void conf(struct ctx *c, int argc, char **argv)
conf_ugid(runas, &uid, &gid);
- if (logfile) {
- logfile_init(c->mode == MODE_PASTA ? "pasta" : "passt",
- logfile, logsize);
- }
+ if (logfile)
+ logfile_init(logname, logfile, logsize);
+ else
+ __openlog(logname, 0, LOG_DAEMON);
- /* Once the log mask is not LOG_EARLY, we will no longer log to stderr
- * if there was a log file specified.
- */
if (c->debug)
__setlogmask(LOG_UPTO(LOG_DEBUG));
else if (c->quiet)
@@ -1673,32 +1745,77 @@ void conf(struct ctx *c, int argc, char **argv)
else
__setlogmask(LOG_UPTO(LOG_INFO));
+ log_conf_parsed = true; /* Stop printing everything */
+
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);
+ c->ifi6 = conf_ip6(ifi6, &c->ip6);
if ((!c->ifi4 && !c->ifi6) ||
(*c->ip4.ifname_out && !c->ifi4) ||
(*c->ip6.ifname_out && !c->ifi6))
die("External interface not usable");
- if (c->ifi4 && IN4_IS_ADDR_UNSPECIFIED(&c->ip4.gw))
- c->no_map_gw = c->no_dhcp = 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 && IN6_IS_ADDR_UNSPECIFIED(&c->ip6.gw))
- c->no_map_gw = 1;
+ if (c->ifi6 && !no_map_gw &&
+ IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback))
+ c->ip6.map_host_loopback = c->ip6.guest_gw;
- /* Inbound port options can be parsed now (after IPv4/IPv6 settings) */
+ 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);
- if (name == 't')
+ if (name == 't') {
conf_ports(c, name, optarg, &c->tcp.fwd_in);
- else if (name == 'u')
- conf_ports(c, name, optarg, &c->udp.fwd_in.f);
+ } else if (name == 'u') {
+ conf_ports(c, name, optarg, &c->udp.fwd_in);
+ } else if (name == 'D') {
+ struct in6_addr dns6_tmp;
+ struct in_addr dns4_tmp;
+
+ if (!strcmp(optarg, "none")) {
+ c->no_dns = 1;
+
+ 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_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 };
+
+ continue;
+ }
+
+ c->no_dns = 0;
+
+ if (inet_pton(AF_INET, optarg, &dns4_tmp)) {
+ dns4_idx += add_dns4(c, &dns4_tmp, dns4_idx);
+ continue;
+ }
+
+ if (inet_pton(AF_INET6, optarg, &dns6_tmp)) {
+ dns6_idx += add_dns6(c, &dns6_tmp, dns6_idx);
+ continue;
+ }
+
+ die("Cannot use DNS address %s", optarg);
+ }
} while (name != -1);
if (c->mode == MODE_PASTA)
@@ -1706,6 +1823,8 @@ void conf(struct ctx *c, int argc, char **argv)
else if (optind != argc)
die("Extra non-option argument: %s", argv[optind]);
+ conf_open_files(c); /* Before any possible setuid() / setgid() */
+
isolate_user(uid, gid, !netns_only, userns, c->mode);
if (c->pasta_conf_ns)
@@ -1724,14 +1843,14 @@ 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);
if (name == 'T')
conf_ports(c, name, optarg, &c->tcp.fwd_out);
else if (name == 'U')
- conf_ports(c, name, optarg, &c->udp.fwd_out.f);
+ conf_ports(c, name, optarg, &c->udp.fwd_out);
} while (name != -1);
if (!c->ifi4)
@@ -1758,10 +1877,10 @@ void conf(struct ctx *c, int argc, char **argv)
c->tcp.fwd_in.mode = fwd_default;
if (!c->tcp.fwd_out.mode)
c->tcp.fwd_out.mode = fwd_default;
- if (!c->udp.fwd_in.f.mode)
- c->udp.fwd_in.f.mode = fwd_default;
- if (!c->udp.fwd_out.f.mode)
- c->udp.fwd_out.f.mode = fwd_default;
+ if (!c->udp.fwd_in.mode)
+ c->udp.fwd_in.mode = fwd_default;
+ if (!c->udp.fwd_out.mode)
+ c->udp.fwd_out.mode = fwd_default;
fwd_scan_ports_init(c);