aboutgitcodebugslistschat
path: root/conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'conf.c')
-rw-r--r--conf.c376
1 files changed, 8 insertions, 368 deletions
diff --git a/conf.c b/conf.c
index b470b0d..5aacfe0 100644
--- a/conf.c
+++ b/conf.c
@@ -13,7 +13,6 @@
*/
#include <arpa/inet.h>
-#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -67,365 +66,6 @@
const char *pasta_default_ifn = "tap0";
/**
- * port_range() - Represents a non-empty range of ports
- * @first: First port number in the range
- * @last: Last port number in the range (inclusive)
- *
- * Invariant: @last >= @first
- */
-struct port_range {
- in_port_t first, last;
-};
-
-/**
- * parse_port_range() - Parse a range of port numbers '<first>[-<last>]'
- * @s: String to parse
- * @endptr: Update to the character after the parsed range (similar to
- * strtol() etc.)
- * @range: Update with the parsed values on success
- *
- * Return: -EINVAL on parsing error, -ERANGE on out of range port
- * numbers, 0 on success
- */
-static int parse_port_range(const char *s, const char **endptr,
- struct port_range *range)
-{
- unsigned long first, last;
- char *ep;
-
- last = first = strtoul(s, &ep, 10);
- if (ep == s) /* Parsed nothing */
- return -EINVAL;
- if (*ep == '-') { /* we have a last value too */
- const char *lasts = ep + 1;
- last = strtoul(lasts, &ep, 10);
- if (ep == lasts) /* Parsed nothing */
- return -EINVAL;
- }
-
- if ((last < first) || (last >= NUM_PORTS))
- return -ERANGE;
-
- range->first = first;
- range->last = last;
- *endptr = ep;
-
- return 0;
-}
-
-/**
- * parse_keyword() - Parse a literal keyword
- * @s: String to parse
- * @endptr: Update to the character after the keyword
- * @kw: Keyword to accept
- *
- * Return: 0, if @s starts with @kw, -EINVAL if it does not
- */
-static int parse_keyword(const char *s, const char **endptr, const char *kw)
-{
- size_t len = strlen(kw);
-
- if (strlen(s) < len)
- return -EINVAL;
-
- if (memcmp(s, kw, len))
- return -EINVAL;
-
- *endptr = s + len;
- return 0;
-}
-
-/**
- * conf_ports_range_except() - Set up forwarding for a range of ports minus a
- * bitmap of exclusions
- * @fwd: Forwarding table to be updated
- * @proto: Protocol to forward
- * @addr: Listening address
- * @ifname: Listening interface
- * @first: First port to forward
- * @last: Last port to forward
- * @exclude: Bitmap of ports to exclude (may be NULL)
- * @to: Port to translate @first to when forwarding
- * @flags: Flags for forwarding entries
- */
-static void conf_ports_range_except(struct fwd_table *fwd, uint8_t proto,
- const union inany_addr *addr,
- const char *ifname,
- uint16_t first, uint16_t last,
- const uint8_t *exclude, uint16_t to,
- uint8_t flags)
-{
- struct fwd_rule rule = {
- .addr = addr ? *addr : inany_any6,
- .ifname = { 0 },
- .proto = proto,
- .flags = flags,
- };
- char rulestr[FWD_RULE_STRLEN];
- unsigned delta = to - first;
- unsigned base, i;
-
- if (!addr)
- rule.flags |= FWD_DUAL_STACK_ANY;
- if (ifname) {
- int ret;
-
- ret = snprintf(rule.ifname, sizeof(rule.ifname),
- "%s", ifname);
- if (ret <= 0 || (size_t)ret >= sizeof(rule.ifname))
- die("Invalid interface name: %s", ifname);
- }
-
- for (base = first; base <= last; base++) {
- if (exclude && bitmap_isset(exclude, base))
- continue;
-
- for (i = base; i <= last; i++) {
- if (exclude && bitmap_isset(exclude, i))
- break;
- }
-
- rule.first = base;
- rule.last = i - 1;
- rule.to = base + delta;
-
- fwd_rule_conflict_check(&rule, fwd->rules, fwd->count);
- if (fwd_rule_add(fwd, &rule) < 0)
- goto fail;
-
- base = i - 1;
- }
- return;
-
-fail:
- die("Unable to add rule %s",
- fwd_rule_fmt(&rule, rulestr, sizeof(rulestr)));
-}
-
-/*
- * for_each_chunk - Step through delimited chunks of a string
- * @p_: Pointer to start of each chunk (updated)
- * @ep_: Pointer to end of each chunk (updated)
- * @s_: String to step through
- * @sep_: String of all allowed delimiters
- */
-#define for_each_chunk(p_, ep_, s_, sep_) \
- for ((p_) = (s_); \
- (ep_) = (p_) + strcspn((p_), (sep_)), *(p_); \
- (p_) = *(ep_) ? (ep_) + 1 : (ep_))
-
-/**
- * conf_ports_spec() - Parse port range(s) specifier
- * @fwd: Forwarding table to be updated
- * @proto: Protocol to forward
- * @addr: Listening address for forwarding
- * @ifname: Interface name for listening
- * @spec: Port range(s) specifier
- */
-static void conf_ports_spec(struct fwd_table *fwd, uint8_t proto,
- const union inany_addr *addr, const char *ifname,
- const char *spec)
-{
- uint8_t exclude[PORT_BITMAP_SIZE] = { 0 };
- bool exclude_only = true;
- const char *p, *ep;
- uint8_t flags = 0;
- unsigned i;
-
- if (!strcmp(spec, "all")) {
- /* Treat "all" as equivalent to "": all non-ephemeral ports */
- spec = "";
- }
-
- /* Parse excluded ranges and "auto" in the first pass */
- for_each_chunk(p, ep, spec, ",") {
- struct port_range xrange;
-
- if (isdigit(*p)) {
- /* Include range, parse later */
- exclude_only = false;
- continue;
- }
-
- if (parse_keyword(p, &p, "auto") == 0) {
- if (p != ep) /* Garbage after the keyword */
- goto bad;
-
- if (!(fwd->caps & FWD_CAP_SCAN)) {
- die(
-"'auto' port forwarding is only allowed for pasta");
- }
-
- flags |= FWD_SCAN;
- continue;
- }
-
- /* Should be an exclude range */
- if (*p != '~')
- goto bad;
- p++;
-
- if (parse_port_range(p, &p, &xrange))
- goto bad;
- if (p != ep) /* Garbage after the range */
- goto bad;
-
- for (i = xrange.first; i <= xrange.last; i++)
- bitmap_set(exclude, i);
- }
-
- if (exclude_only) {
- /* Exclude ephemeral ports */
- fwd_port_map_ephemeral(exclude);
-
- conf_ports_range_except(fwd, proto, addr, ifname,
- 1, NUM_PORTS - 1, exclude,
- 1, flags | FWD_WEAK);
- return;
- }
-
- /* Now process base ranges, skipping exclusions */
- for_each_chunk(p, ep, spec, ",") {
- struct port_range orig_range, mapped_range;
-
- if (!isdigit(*p))
- /* Already parsed */
- continue;
-
- if (parse_port_range(p, &p, &orig_range))
- goto bad;
-
- if (*p == ':') { /* There's a range to map to as well */
- if (parse_port_range(p + 1, &p, &mapped_range))
- goto bad;
- if ((mapped_range.last - mapped_range.first) !=
- (orig_range.last - orig_range.first))
- goto bad;
- } else {
- mapped_range = orig_range;
- }
-
- if (p != ep) /* Garbage after the ranges */
- goto bad;
-
- conf_ports_range_except(fwd, proto, addr, ifname,
- orig_range.first, orig_range.last,
- exclude,
- mapped_range.first, flags);
- }
-
- return;
-bad:
- die("Invalid port specifier '%s'", spec);
-}
-
-/**
- * conf_ports() - Parse port configuration options, initialise UDP/TCP sockets
- * @optname: Short option name, t, T, u, or U
- * @optarg: Option argument (port specification)
- * @fwd: Forwarding table to be updated
- */
-static void conf_ports(char optname, const char *optarg, struct fwd_table *fwd)
-{
- union inany_addr addr_buf = inany_any6, *addr = &addr_buf;
- char buf[BUFSIZ], *spec, *ifname = NULL;
- uint8_t proto;
-
- if (optname == 't' || optname == 'T')
- proto = IPPROTO_TCP;
- else if (optname == 'u' || optname == 'U')
- proto = IPPROTO_UDP;
- else
- assert(0);
-
- if (!strcmp(optarg, "none")) {
- unsigned i;
-
- for (i = 0; i < fwd->count; i++) {
- if (fwd->rules[i].proto == proto) {
- die("-%c none conflicts with previous options",
- optname);
- }
- }
- return;
- }
-
- strncpy(buf, optarg, sizeof(buf) - 1);
-
- if ((spec = strchr(buf, '/'))) {
- *spec = 0;
- spec++;
-
- if (optname != 't' && optname != 'u')
- die("Listening address not allowed for -%c %s",
- optname, optarg);
-
- if ((ifname = strchr(buf, '%'))) {
- *ifname = 0;
- ifname++;
-
- /* spec is already advanced one past the '/',
- * so the length of the given ifname is:
- * (spec - ifname - 1)
- */
- if (spec - ifname - 1 >= IFNAMSIZ) {
- die("Interface name '%s' is too long (max %u)",
- ifname, IFNAMSIZ - 1);
- }
- }
-
- if (ifname == buf + 1) { /* Interface without address */
- addr = NULL;
- } else {
- char *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))
- die("Bad forwarding address '%s'", p);
- }
- } else {
- spec = buf;
-
- addr = NULL;
- }
-
- if (optname == 'T' || optname == 'U') {
- assert(!addr && !ifname);
-
- if (!(fwd->caps & FWD_CAP_IFNAME)) {
- warn(
-"SO_BINDTODEVICE unavailable, forwarding only 127.0.0.1 and ::1 for '-%c %s'",
- optname, optarg);
-
- if (fwd->caps & FWD_CAP_IPV4) {
- conf_ports_spec(fwd, proto,
- &inany_loopback4, NULL, spec);
- }
- if (fwd->caps & FWD_CAP_IPV6) {
- conf_ports_spec(fwd, proto,
- &inany_loopback6, NULL, spec);
- }
- return;
- }
-
- ifname = "lo";
- }
-
- if (ifname && !(fwd->caps & FWD_CAP_IFNAME)) {
- die(
-"Device binding for '-%c %s' unsupported (requires kernel 5.7+)",
- optname, optarg);
- }
-
- conf_ports_spec(fwd, proto, addr, ifname, spec);
-}
-
-/**
* add_dns4() - Possibly add the IPv4 address of a DNS resolver to configuration
* @c: Execution context
* @addr: Guest nameserver IPv4 address
@@ -2160,16 +1800,16 @@ void conf(struct ctx *c, int argc, char **argv)
if (name == 't') {
opt_t = true;
- conf_ports(name, optarg, c->fwd[PIF_HOST]);
+ fwd_rule_parse(name, optarg, c->fwd[PIF_HOST]);
} else if (name == 'u') {
opt_u = true;
- conf_ports(name, optarg, c->fwd[PIF_HOST]);
+ fwd_rule_parse(name, optarg, c->fwd[PIF_HOST]);
} else if (name == 'T') {
opt_T = true;
- conf_ports(name, optarg, c->fwd[PIF_SPLICE]);
+ fwd_rule_parse(name, optarg, c->fwd[PIF_SPLICE]);
} else if (name == 'U') {
opt_U = true;
- conf_ports(name, optarg, c->fwd[PIF_SPLICE]);
+ fwd_rule_parse(name, optarg, c->fwd[PIF_SPLICE]);
}
} while (name != -1);
@@ -2221,13 +1861,13 @@ void conf(struct ctx *c, int argc, char **argv)
if (c->mode == MODE_PASTA) {
if (!opt_t)
- conf_ports('t', "auto", c->fwd[PIF_HOST]);
+ fwd_rule_parse('t', "auto", c->fwd[PIF_HOST]);
if (!opt_T)
- conf_ports('T', "auto", c->fwd[PIF_SPLICE]);
+ fwd_rule_parse('T', "auto", c->fwd[PIF_SPLICE]);
if (!opt_u)
- conf_ports('u', "auto", c->fwd[PIF_HOST]);
+ fwd_rule_parse('u', "auto", c->fwd[PIF_HOST]);
if (!opt_U)
- conf_ports('U', "auto", c->fwd[PIF_SPLICE]);
+ fwd_rule_parse('U', "auto", c->fwd[PIF_SPLICE]);
}
if (!c->quiet)