aboutgitcodebugslistschat
path: root/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'conf.c')
-rw-r--r--conf.c269
1 files changed, 175 insertions, 94 deletions
diff --git a/conf.c b/conf.c
index 02a4b65..11d8453 100644
--- a/conf.c
+++ b/conf.c
@@ -135,9 +135,9 @@ static int parse_port_range(const char *s, char **endptr,
* @ifname: Listening interface
* @first: First port to forward
* @last: Last port to forward
- * @exclude: Bitmap of ports to exclude
+ * @exclude: Bitmap of ports to exclude (may be NULL)
* @to: Port to translate @first to when forwarding
- * @weak: Ignore errors, as long as at least one port is mapped
+ * @flags: Flags for forwarding entries
*/
static void conf_ports_range_except(const struct ctx *c, char optname,
const char *optarg, struct fwd_ports *fwd,
@@ -145,58 +145,58 @@ static void conf_ports_range_except(const struct ctx *c, char optname,
const char *ifname,
uint16_t first, uint16_t last,
const uint8_t *exclude, uint16_t to,
- bool weak)
+ uint8_t flags)
{
- bool bound_one = false;
- unsigned i;
- int ret;
+ unsigned delta = to - first;
+ unsigned base, i;
if (first == 0) {
die("Can't forward port 0 for option '-%c %s'",
optname, optarg);
}
- if (ifname && c->no_bindtodevice) {
- die(
-"Device binding for '-%c %s' unsupported (requires kernel 5.7+)",
- optname, optarg);
+ if (addr) {
+ if (!c->ifi4 && inany_v4(addr)) {
+ die("IPv4 is disabled, can't use -%c %s",
+ optname, optarg);
+ } else if (!c->ifi6 && !inany_v4(addr)) {
+ die("IPv6 is disabled, can't use -%c %s",
+ optname, optarg);
+ }
}
- for (i = first; i <= last; i++) {
- if (bitmap_isset(exclude, i))
+ for (base = first; base <= last; base++) {
+ if (exclude && bitmap_isset(exclude, base))
continue;
- if (bitmap_isset(fwd->map, i)) {
- warn(
-"Altering mapping of already mapped port number: %s", optarg);
+ for (i = base; i <= last; i++) {
+ if (exclude && bitmap_isset(exclude, i))
+ break;
}
- bitmap_set(fwd->map, i);
- fwd->delta[i] = to - first;
-
- if (optname == 't')
- ret = tcp_sock_init(c, PIF_HOST, addr, ifname, i);
- else if (optname == 'u')
- ret = udp_sock_init(c, PIF_HOST, addr, ifname, i);
- else
- /* No way to check in advance for -T and -U */
- ret = 0;
-
- if (ret == -ENFILE || ret == -EMFILE) {
- die("Can't open enough sockets for port specifier: %s",
- optarg);
- }
+ if ((optname == 'T' || optname == 'U') && c->no_bindtodevice) {
+ /* FIXME: Once the fwd bitmaps are removed, move this
+ * workaround to the caller
+ */
+ ASSERT(!addr && ifname && !strcmp(ifname, "lo"));
+ warn(
+"SO_BINDTODEVICE unavailable, forwarding only 127.0.0.1 and ::1 for '-%c %s'",
+ optname, optarg);
- if (!ret) {
- bound_one = true;
- } else if (!weak) {
- die("Failed to bind port %u (%s) for option '-%c %s'",
- i, strerror_(-ret), optname, optarg);
+ if (c->ifi4) {
+ fwd_rule_add(fwd, flags, &inany_loopback4, NULL,
+ base, i - 1, base + delta);
+ }
+ if (c->ifi6) {
+ fwd_rule_add(fwd, flags, &inany_loopback6, NULL,
+ base, i - 1, base + delta);
+ }
+ } else {
+ fwd_rule_add(fwd, flags, addr, ifname,
+ base, i - 1, base + delta);
}
+ base = i - 1;
}
-
- if (!bound_one)
- die("Failed to bind any port for '-%c %s'", optname, optarg);
}
/**
@@ -262,7 +262,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
conf_ports_range_except(c, optname, optarg, fwd,
NULL, NULL,
1, NUM_PORTS - 1, exclude,
- 1, true);
+ 1, FWD_WEAK);
return;
}
@@ -338,6 +338,15 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
}
} while ((p = next_chunk(p, ',')));
+ if (ifname && c->no_bindtodevice) {
+ die(
+"Device binding for '-%c %s' unsupported (requires kernel 5.7+)",
+ optname, optarg);
+ }
+ /* Outbound forwards come from guest loopback */
+ if ((optname == 'T' || optname == 'U') && !ifname)
+ ifname = "lo";
+
if (exclude_only) {
/* Exclude ephemeral ports */
for (i = 0; i < NUM_PORTS; i++)
@@ -347,7 +356,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
conf_ports_range_except(c, optname, optarg, fwd,
addr, ifname,
1, NUM_PORTS - 1, exclude,
- 1, true);
+ 1, FWD_WEAK);
return;
}
@@ -380,7 +389,7 @@ static void conf_ports(const struct ctx *c, char optname, const char *optarg,
addr, ifname,
orig_range.first, orig_range.last,
exclude,
- mapped_range.first, false);
+ mapped_range.first, 0);
} while ((p = next_chunk(p, ',')));
return;
@@ -682,7 +691,7 @@ static int conf_ip4_prefix(const char *arg)
return -1;
} else {
errno = 0;
- len = strtoul(optarg, NULL, 0);
+ len = strtoul(arg, NULL, 0);
if (len > 32 || errno)
return -1;
}
@@ -826,7 +835,7 @@ static void conf_ip6_local(struct ip6_ctx *ip6)
* usage() - Print usage, exit with given status code
* @name: Executable name
* @f: Stream to print usage info to
- * @status: Status code for _exit()
+ * @status: Status code for exit(2)
*/
static void usage(const char *name, FILE *f, int status)
{
@@ -896,7 +905,7 @@ static void usage(const char *name, FILE *f, int status)
" 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"
+ " -a, --address ADDR Assign IPv4 or IPv6 address ADDR[/PREFIXLEN]\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"
@@ -997,8 +1006,7 @@ static void usage(const char *name, FILE *f, int status)
" SPEC is as described for TCP above\n"
" default: none\n");
- (void)fflush(f);
- _exit(status);
+ passt_exit(status);
pasta_opts:
@@ -1050,10 +1058,10 @@ pasta_opts:
" --no-copy-addrs DEPRECATED:\n"
" Don't copy all addresses to namespace\n"
" --ns-mac-addr ADDR Set MAC address on tap interface\n"
- " --no-splice Disable inbound socket splicing\n");
+ " --no-splice Disable inbound socket splicing\n"
+ " --splice-only Only enable loopback forwarding\n");
- (void)fflush(f);
- _exit(status);
+ passt_exit(status);
}
/**
@@ -1109,7 +1117,7 @@ static void conf_print(const struct ctx *c)
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->ifi4 > 0 && c->ifi6 > 0) ? ", " : "",
c->ifi6 > 0 ? if_indextoname(c->ifi6, ifn) : "",
c->ifi6 > 0 ? " (IPv6)" : "");
}
@@ -1134,7 +1142,7 @@ static void conf_print(const struct ctx *c)
inet_ntop(AF_INET6, &c->ip6.addr_out, buf6, sizeof(buf6)));
}
- if (c->mode == MODE_PASTA)
+ if (c->mode == MODE_PASTA && !c->splice_only)
info("Namespace interface: %s", c->pasta_ifn);
info("MAC:");
@@ -1161,7 +1169,9 @@ static void conf_print(const struct ctx *c)
buf4, sizeof(buf4)));
}
- for (i = 0; !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) {
+ for (i = 0; i < ARRAY_SIZE(c->ip4.dns); i++) {
+ if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]))
+ break;
if (!i)
info("DNS:");
inet_ntop(AF_INET, &c->ip4.dns[i], buf4, sizeof(buf4));
@@ -1199,7 +1209,9 @@ static void conf_print(const struct ctx *c)
buf6, sizeof(buf6)));
dns6:
- for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) {
+ for (i = 0; i < ARRAY_SIZE(c->ip6.dns); i++) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]))
+ break;
if (!i)
info("DNS:");
inet_ntop(AF_INET6, &c->ip6.dns[i], buf6, sizeof(buf6));
@@ -1212,6 +1224,17 @@ dns6:
info(" %s", c->dns_search[i].n);
}
}
+
+ info("Inbound TCP forwarding:");
+ fwd_rules_print(&c->tcp.fwd_in);
+ info("Inbound UDP forwarding:");
+ fwd_rules_print(&c->udp.fwd_in);
+ if (c->mode == MODE_PASTA) {
+ info("Outbound TCP forwarding:");
+ fwd_rules_print(&c->tcp.fwd_out);
+ info("Outbound UDP forwarding:");
+ fwd_rules_print(&c->udp.fwd_out);
+ }
}
/**
@@ -1222,7 +1245,7 @@ dns6:
*
* Return: 0 on success, negative error code on failure
*/
-static int conf_runas(char *opt, unsigned int *uid, unsigned int *gid)
+static int conf_runas(const char *opt, unsigned int *uid, unsigned int *gid)
{
const char *uopt, *gopt = NULL;
char *sep = strchr(opt, ':');
@@ -1453,6 +1476,7 @@ void conf(struct ctx *c, int argc, char **argv)
{"no-ndp", no_argument, &c->no_ndp, 1 },
{"no-ra", no_argument, &c->no_ra, 1 },
{"no-splice", no_argument, &c->no_splice, 1 },
+ {"splice-only", no_argument, &c->splice_only, 1 },
{"freebind", no_argument, &c->freebind, 1 },
{"no-map-gw", no_argument, &no_map_gw, 1 },
{"ipv4-only", no_argument, NULL, '4' },
@@ -1506,6 +1530,8 @@ void conf(struct ctx *c, int argc, char **argv)
unsigned dns4_idx = 0, dns6_idx = 0;
unsigned long max_mtu = IP_MAX_MTU;
struct fqdn *dnss = c->dns_search;
+ bool addr_has_prefix_len = false;
+ uint8_t prefix_len_from_opt = 0;
unsigned int ifi4 = 0, ifi6 = 0;
const char *logfile = NULL;
size_t logsize = 0;
@@ -1621,8 +1647,7 @@ void conf(struct ctx *c, int argc, char **argv)
FPRINTF(stdout,
c->mode == MODE_PASTA ? "pasta " : "passt ");
FPRINTF(stdout, VERSION_BLOB);
- (void)fflush(stdout);
- _exit(EXIT_SUCCESS);
+ passt_exit(EXIT_SUCCESS);
case 15:
ret = snprintf(c->ip4.ifname_out,
sizeof(c->ip4.ifname_out), "%s", optarg);
@@ -1811,36 +1836,56 @@ void conf(struct ctx *c, int argc, char **argv)
c->mtu = mtu;
break;
}
- case 'a':
- 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)) {
- if (c->mode == MODE_PASTA)
- c->ip6.no_copy_addrs = true;
- break;
- }
+ case 'a': {
+ union inany_addr addr;
+ uint8_t prefix_len;
+
+ addr_has_prefix_len = inany_prefix_pton(optarg, &addr,
+ &prefix_len);
+
+ if (addr_has_prefix_len && prefix_len_from_opt)
+ die("Redundant prefix length specification");
+
+ if (!addr_has_prefix_len && !inany_pton(optarg, &addr))
+ die("Invalid address: %s", optarg);
+
+ if (prefix_len_from_opt && inany_v4(&addr))
+ prefix_len = prefix_len_from_opt;
+ else if (!addr_has_prefix_len)
+ prefix_len = inany_default_prefix_len(&addr);
+
+ if (inany_is_unspecified(&addr) ||
+ inany_is_multicast(&addr) ||
+ inany_is_loopback(&addr) ||
+ IN6_IS_ADDR_V4COMPAT(&addr.a6))
+ die("Invalid address: %s", optarg);
- 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)) {
+ if (inany_v4(&addr)) {
+ c->ip4.addr = *inany_v4(&addr);
+ c->ip4.prefix_len = prefix_len - 96;
if (c->mode == MODE_PASTA)
c->ip4.no_copy_addrs = true;
- break;
+ } else {
+ c->ip6.addr = addr.a6;
+ if (c->mode == MODE_PASTA)
+ c->ip6.no_copy_addrs = true;
}
-
- die("Invalid address: %s", optarg);
break;
- case 'n':
- c->ip4.prefix_len = conf_ip4_prefix(optarg);
- if (c->ip4.prefix_len < 0)
- die("Invalid netmask: %s", optarg);
+ }
+ case 'n': {
+ int plen;
+
+ if (addr_has_prefix_len)
+ die("Redundant prefix length specification");
+
+ plen = conf_ip4_prefix(optarg);
+ if (plen < 0)
+ die("Invalid prefix length: %s", optarg);
+ prefix_len_from_opt = plen + 96;
+ c->ip4.prefix_len = plen;
break;
+ }
case 'M':
parse_mac(c->our_tap_mac, optarg);
break;
@@ -1950,8 +1995,11 @@ void conf(struct ctx *c, int argc, char **argv)
}
} while (name != -1);
- if (c->mode != MODE_PASTA)
+ if (c->mode != MODE_PASTA) {
c->no_splice = 1;
+ if (c->splice_only)
+ die("--splice-only is for pasta mode only");
+ }
if (c->mode == MODE_PASTA && !c->pasta_conf_ns) {
if (copy_routes_opt)
@@ -1960,6 +2008,16 @@ void conf(struct ctx *c, int argc, char **argv)
die("--no-copy-addrs needs --config-net");
}
+ if (c->mode == MODE_PASTA && c->splice_only) {
+ if (c->no_splice)
+ die("--splice-only is incompatible with --no-splice");
+
+ c->host_lo_to_ns_lo = 1;
+ c->no_icmp = 1;
+ c->no_dns = 1;
+ c->no_dns_search = 1;
+ }
+
if (!ifi4 && *c->ip4.ifname_out)
ifi4 = if_nametoindex(c->ip4.ifname_out);
@@ -1983,9 +2041,9 @@ void conf(struct ctx *c, int argc, char **argv)
log_conf_parsed = true; /* Stop printing everything */
nl_sock_init(c, false);
- if (!v6_only)
+ if (!v6_only && !c->splice_only)
c->ifi4 = conf_ip4(ifi4, &c->ip4);
- if (!v4_only)
+ if (!v4_only && !c->splice_only)
c->ifi6 = conf_ip6(ifi6, &c->ip6);
if (c->ifi4 && c->mtu < IPV4_MIN_MTU) {
@@ -2001,20 +2059,25 @@ void conf(struct ctx *c, int argc, char **argv)
(*c->ip6.ifname_out && !c->ifi6))
die("External interface not usable");
+ if (!c->ifi4 && !c->ifi6 && !*c->pasta_ifn) {
+ strncpy(c->pasta_ifn, pasta_default_ifn,
+ sizeof(c->pasta_ifn) - 1);
+ }
- if (!c->ifi4 && !c->ifi6) {
- info("No external interface as template, switch to local mode");
-
- conf_ip4_local(&c->ip4);
+ if (!c->ifi4 && !v6_only) {
+ if (!c->splice_only) {
+ info("IPv4: no external interface as template, use local mode");
+ conf_ip4_local(&c->ip4);
+ }
c->ifi4 = -1;
+ }
- conf_ip6_local(&c->ip6);
- c->ifi6 = -1;
-
- if (!*c->pasta_ifn) {
- strncpy(c->pasta_ifn, pasta_default_ifn,
- sizeof(c->pasta_ifn) - 1);
+ if (!c->ifi6 && !v4_only) {
+ if (!c->splice_only) {
+ info("IPv6: no external interface as template, use local mode");
+ conf_ip6_local(&c->ip6);
}
+ c->ifi6 = -1;
}
if (c->ifi4 && !no_map_gw &&
@@ -2032,7 +2095,6 @@ void conf(struct ctx *c, int argc, char **argv)
* settings
*/
fwd_probe_ephemeral();
- udp_portmap_clear();
optind = 0;
do {
name = getopt_long(argc, argv, optstring, options, NULL);
@@ -2144,7 +2206,26 @@ void conf(struct ctx *c, int argc, char **argv)
if (!c->udp.fwd_out.mode)
c->udp.fwd_out.mode = fwd_default;
- fwd_scan_ports_init(c);
+ if (c->tcp.fwd_in.mode == FWD_AUTO) {
+ conf_ports_range_except(c, 't', "auto", &c->tcp.fwd_in,
+ NULL, NULL, 1, NUM_PORTS - 1,
+ NULL, 1, FWD_SCAN);
+ }
+ if (c->tcp.fwd_out.mode == FWD_AUTO) {
+ conf_ports_range_except(c, 'T', "auto", &c->tcp.fwd_out,
+ NULL, "lo", 1, NUM_PORTS - 1,
+ NULL, 1, FWD_SCAN);
+ }
+ if (c->udp.fwd_in.mode == FWD_AUTO) {
+ conf_ports_range_except(c, 'u', "auto", &c->udp.fwd_in,
+ NULL, NULL, 1, NUM_PORTS - 1,
+ NULL, 1, FWD_SCAN);
+ }
+ if (c->udp.fwd_out.mode == FWD_AUTO) {
+ conf_ports_range_except(c, 'U', "auto", &c->udp.fwd_out,
+ NULL, "lo", 1, NUM_PORTS - 1,
+ NULL, 1, FWD_SCAN);
+ }
if (!c->quiet)
conf_print(c);