aboutgitcodebugslistschat
path: root/fwd_rule.c
diff options
context:
space:
mode:
Diffstat (limited to 'fwd_rule.c')
-rw-r--r--fwd_rule.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/fwd_rule.c b/fwd_rule.c
new file mode 100644
index 0000000..47d8df1
--- /dev/null
+++ b/fwd_rule.c
@@ -0,0 +1,139 @@
+// 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 <david@gibson.dropbear.id.au>
+ */
+
+#include <stdio.h>
+
+#include "fwd_rule.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_fmt() - Prettily 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
+ */
+#if defined(__GNUC__) && __GNUC__ < 15
+/* Workaround bug in gcc 12, 13 & 14 (at least) which gives a false positive
+ * -Wformat-overflow message if this function is inlined.
+ */
+__attribute__((noinline))
+#endif
+const char *fwd_rule_fmt(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_rules_info() - Print forwarding rules for debugging
+ * @fwd: Table to print
+ */
+void fwd_rules_info(const struct fwd_rule *rules, size_t count)
+{
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ char buf[FWD_RULE_STRLEN];
+
+ info(" %s", fwd_rule_fmt(&rules[i], buf, sizeof(buf)));
+ }
+}
+
+/**
+ * fwd_rule_conflicts() - Test if two rules conflict with each other
+ * @a, @b: Rules to test
+ */
+static bool fwd_rule_conflicts(const struct fwd_rule *a, const struct fwd_rule *b)
+{
+ if (a->proto != b->proto)
+ /* Non-conflicting protocols */
+ return false;
+
+ if (!inany_matches(fwd_rule_addr(a), fwd_rule_addr(b)))
+ /* Non-conflicting addresses */
+ return false;
+
+ assert(a->first <= a->last && b->first <= b->last);
+ if (a->last < b->first || b->last < a->first)
+ /* Port ranges don't overlap */
+ return false;
+
+ return true;
+}
+
+/**
+ * fwd_rule_conflict_check() - Die if given rule conflicts with any in list
+ * @new: New rule
+ * @rules: Existing rules against which to test
+ * @count: Number of rules in @rules
+ */
+void fwd_rule_conflict_check(const struct fwd_rule *new,
+ const struct fwd_rule *rules, size_t count)
+{
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ char newstr[FWD_RULE_STRLEN], rulestr[FWD_RULE_STRLEN];
+
+ if (!fwd_rule_conflicts(new, &rules[i]))
+ continue;
+
+ die("Forwarding configuration conflict: %s versus %s",
+ fwd_rule_fmt(new, newstr, sizeof(newstr)),
+ fwd_rule_fmt(&rules[i], rulestr, sizeof(rulestr)));
+ }
+}