// SPDX-License-Identifier: GPL-2.0-or-later /* PASST - Plug A Simple Socket Transport * for qemu/UNIX domain socket mode * * PASTA - Pack A Subtle Tap Abstraction * for network namespace/tap device mode * * PESTO - Programmable Extensible Socket Translation Orchestrator * front-end for passt(1) and pasta(1) forwarding configuration * * fwd_rule.c - Helpers for working with forwarding rule specifications * * Copyright Red Hat * Author: David Gibson */ #include #include "serialise.h" #include "ports.h" #include "fwd_rule.h" #include "log.h" /** * fwd_rule_addr() - Return match address for a rule * @rule: Forwarding rule * * Return: matching address for rule, NULL if it matches all addresses */ const union inany_addr *fwd_rule_addr(const struct fwd_rule *rule) { if (rule->flags & FWD_DUAL_STACK_ANY) return NULL; return &rule->addr; } /** * fwd_rule_ntop() - Format forwarding rule as a string * @rule: Rule to format * @dst: Buffer to store output (should have FWD_RULE_STRLEN bytes) * @size: Size of @dst */ const char *fwd_rule_ntop(const struct fwd_rule *rule, char *dst, size_t size) { const char *percent = *rule->ifname ? "%" : ""; const char *weak = "", *scan = ""; char addr[INANY_ADDRSTRLEN]; int len; inany_ntop(fwd_rule_addr(rule), addr, sizeof(addr)); if (rule->flags & FWD_WEAK) weak = " (best effort)"; if (rule->flags & FWD_SCAN) scan = " (auto-scan)"; if (rule->first == rule->last) { len = snprintf(dst, size, "%s [%s]%s%s:%hu => %hu %s%s", ipproto_name(rule->proto), addr, percent, rule->ifname, rule->first, rule->to, weak, scan); } else { in_port_t tolast = rule->last - rule->first + rule->to; len = snprintf(dst, size, "%s [%s]%s%s:%hu-%hu => %hu-%hu %s%s", ipproto_name(rule->proto), addr, percent, rule->ifname, rule->first, rule->last, rule->to, tolast, weak, scan); } if (len < 0 || (size_t)len >= size) return NULL; return dst; } /** * fwd_rule_add() - Add a rule to a forwarding table * @fwd: Table to add to * @proto: Protocol to forward * @flags: Flags for this entry * @addr: Our address to forward (NULL for both 0.0.0.0 and ::) * @ifname: Only forward from this interface name, if non-empty * @first: First port number to forward * @last: Last port number to forward * @to: First port of target port range to map to */ void fwd_rule_add(struct fwd_table *fwd, uint8_t proto, uint8_t flags, const union inany_addr *addr, const char *ifname, in_port_t first, in_port_t last, in_port_t to) { /* Flags which can be set from the caller */ const uint8_t allowed_flags = FWD_WEAK | FWD_SCAN; unsigned num = (unsigned)last - first + 1; struct fwd_rule_state *new; unsigned i, port; assert(!(flags & ~allowed_flags)); if (fwd->count >= ARRAY_SIZE(fwd->rules)) die("Too many port forwarding ranges"); if ((fwd->sock_count + num) > ARRAY_SIZE(fwd->socks)) die("Too many listening sockets"); /* Check for any conflicting entries */ for (i = 0; i < fwd->count; i++) { char newstr[INANY_ADDRSTRLEN], rulestr[INANY_ADDRSTRLEN]; struct fwd_rule_state *rule = &fwd->rules[i]; if (proto != rule->rule.proto) /* Non-conflicting protocols */ continue; if (!inany_matches(addr, fwd_rule_addr(&rule->rule))) /* Non-conflicting addresses */ continue; if (last < rule->rule.first || rule->rule.last < first) /* Port ranges don't overlap */ continue; die("Forwarding configuration conflict: %s/%u-%u versus %s/%u-%u", inany_ntop(addr, newstr, sizeof(newstr)), first, last, inany_ntop(fwd_rule_addr(&rule->rule), rulestr, sizeof(rulestr)), rule->rule.first, rule->rule.last); } new = &fwd->rules[fwd->count++]; new->rule.proto = proto; new->rule.flags = flags; if (addr) { new->rule.addr = *addr; } else { new->rule.addr = inany_any6; new->rule.flags |= FWD_DUAL_STACK_ANY; } memset(new->rule.ifname, 0, sizeof(new->rule.ifname)); if (ifname) { int ret; ret = snprintf(new->rule.ifname, sizeof(new->rule.ifname), "%s", ifname); if (ret <= 0 || (size_t)ret >= sizeof(new->rule.ifname)) die("Invalid interface name: %s", ifname); } assert(first <= last); new->rule.first = first; new->rule.last = last; new->rule.to = to; new->socks = &fwd->socks[fwd->sock_count]; fwd->sock_count += num; for (port = new->rule.first; port <= new->rule.last; port++) new->socks[port - new->rule.first] = -1; } /** * fwd_rule_seread() - Read erialised rule from an fd * @fd: fd to serialise to * @rule: Buffer to store rule into * * Return: 0 on success, -1 on error (with errno set) */ int fwd_rule_seread(int fd, struct fwd_rule *rule) { if (seread_var(fd, rule)) return -1; /* Byteswap for host */ rule->first = ntohs(rule->first); rule->last = ntohs(rule->last); rule->to = htons(rule->to); return 0; } /** * fwd_rule_sewrite() - Serialise rule to an fd * @fd: fd to serialise to * @rule: Rule to send * * Return: 0 on success, -1 on error (with errno set) */ int fwd_rule_sewrite(int fd, const struct fwd_rule *rule) { struct fwd_rule tmp = *rule; /* Byteswap for transport */ tmp.first = htons(tmp.first); tmp.last = htons(tmp.last); tmp.to = htons(tmp.to); return sewrite_var(fd, &tmp); }