summaryrefslogtreecommitdiff
path: root/regress/sbin/pfctl/changerule.c
diff options
context:
space:
mode:
Diffstat (limited to 'regress/sbin/pfctl/changerule.c')
-rw-r--r--regress/sbin/pfctl/changerule.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/regress/sbin/pfctl/changerule.c b/regress/sbin/pfctl/changerule.c
new file mode 100644
index 00000000000..8efd47d9a36
--- /dev/null
+++ b/regress/sbin/pfctl/changerule.c
@@ -0,0 +1,221 @@
+/* $OpenBSD: changerule.c,v 1.1 2021/11/11 12:49:53 sashan Exp $ */
+/*
+ * Copyright (c) 2021 Alexandr Nedvedicky <sashan@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * changerule - simple tool to test DIOCCHANGERULE functionality (see pf(4))
+ * Tool reads firewall rules from stdin only. If more rules are passed, then
+ * only the first one is being used. Examples:
+ * echo 'pass all' | changerule -a test -i 0
+ * inserts a rule to the first position in ruleset test
+ *
+ * echo 'pass all' | changerule -a test -i -1
+ * inserts a rule to the last position in ruleset test
+ *
+ * echo 'pass all' | changerule -a test -i 3
+ * inserts a rule before existing No. 3 rule (rules numbering
+ * starts with 0) in ruleset test
+ *
+ * echo 'pass all' | changerule -a test -I 3
+ * inserts a rule after existing No. 3 rule (rules numbering
+ * starts with 0) in ruleset test
+ *
+ * changerule -a test -r 3
+ * removes existing No. 3 rule from ruleset test
+ *
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <libgen.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void changerule_usage(void);
+int do_chng_cmd(char *, int, int);
+
+extern int dev;
+extern char *anchoropt;
+extern char *pf_device;
+
+__dead void
+changerule_usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s", __progname);
+ fprintf(stderr, "[-a anchor] [ -i ruleNo ] [ -I ruleNo ]\n");
+ exit(1);
+}
+
+int
+do_chng_cmd(char *anchorname, int cmd, int rule_no)
+{
+ struct pfctl pf;
+ struct pf_anchor rs_anchor;
+ struct pf_ruleset *rs = &rs_anchor.ruleset;
+ struct pfioc_rule pcr;
+
+ memset(&pf, 0, sizeof(pf));
+ memset(&rs_anchor, 0, sizeof(rs_anchor));
+ pf.anchor = &rs_anchor;
+ pf_init_ruleset(rs);
+
+ if (strlcpy(pf.anchor->path, anchorname,
+ sizeof(pf.anchor->path)) >= sizeof (pf.anchor->path))
+ errx(1, "%s: strlcpy\n", __func__);
+
+ pf.astack[0] = pf.anchor;
+ pf.asd = 0;
+ pf.dev = dev;
+
+ memset(&pcr, 0, sizeof(pcr));
+ strlcpy(pcr.anchor, anchorname, sizeof(pcr.anchor));
+ pcr.action = PF_CHANGE_GET_TICKET;
+ if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0)
+ errx(1, "ioctl(ticket) @ %s", __func__);
+
+ pcr.action = cmd;
+ pcr.nr = rule_no;
+ if (cmd != PF_CHANGE_REMOVE) {
+ if (parse_config("-", &pf) < 0) {
+ errx(1, "Syntax error in rule");
+ return (1);
+ }
+
+ if (TAILQ_FIRST(rs->rules.active.ptr) != NULL)
+ memcpy(&pcr.rule, TAILQ_FIRST(rs->rules.active.ptr),
+ sizeof(pcr.rule));
+ else
+ errx(1, "no rule");
+ }
+
+ if (ioctl(dev, DIOCCHANGERULE, &pcr) < 0) {
+ errx(1, "ioctl(commit) @ %s", __func__);
+ }
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char anchorname[PATH_MAX];
+ const char *errstr;
+ int ch;
+ int rule_no;
+ int chng_cmd;
+ int after = 0;
+
+ if (argc < 2)
+ changerule_usage();
+
+ while ((ch = getopt(argc, argv, "a:i:I:r:")) != -1) {
+ switch (ch) {
+ case 'a':
+ anchoropt = optarg;
+ break;
+ case 'I':
+ after = 1;
+ /* FALLTHROUGH */
+ case 'i':
+ rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr);
+ if (errstr != NULL) {
+ warnx("Rule number outside of range <%d, %d\n",
+ -1, 0x7fffffff);
+ exit(1);
+ }
+ switch (rule_no) {
+ case 0:
+ chng_cmd = PF_CHANGE_ADD_HEAD;
+ break;
+ case -1:
+ chng_cmd = PF_CHANGE_ADD_TAIL;
+ break;
+ default:
+ if (after)
+ chng_cmd = PF_CHANGE_ADD_AFTER;
+ else
+ chng_cmd = PF_CHANGE_ADD_BEFORE;
+ }
+ break;
+ case 'r':
+ rule_no = strtonum(optarg, -1, 0x7fffffff, &errstr);
+ if (errstr != NULL) {
+ warnx("Rule number outside of range <%d, %d\n",
+ -1, 0x7fffffff);
+ exit(1);
+ }
+ chng_cmd = PF_CHANGE_REMOVE;
+ break;
+ default:
+ changerule_usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (argc != optind) {
+ warnx("unknown command line argument: %s ...", argv[optind]);
+ changerule_usage();
+ /* NOTREACHED */
+ }
+
+ memset(anchorname, 0, sizeof(anchorname));
+ if (anchoropt != NULL) {
+ if (anchoropt[0] == '\0')
+ errx(1, "anchor name must not be empty");
+
+ if (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL)
+ errx(1, "anchor names beginning with '_' cannot "
+ "be modified from the command line");
+ int len = strlen(anchoropt);
+
+ if (anchoropt[len - 1] == '*') {
+ warnx("wildcard anchors not supported\n");
+ changerule_usage();
+ }
+ if (strlcpy(anchorname, anchoropt,
+ sizeof(anchorname)) >= sizeof(anchorname))
+ errx(1, "anchor name '%s' too long",
+ anchoropt);
+ }
+
+ dev = open(pf_device, O_RDWR);
+ if (dev == -1)
+ err(1, "/dev/pf");
+
+ return (do_chng_cmd(anchoropt, chng_cmd, rule_no));
+}