git: c4a08ef2af6f - main - pf: handle duplicate rules gracefully

From: Mateusz Guzik <mjg_at_FreeBSD.org>
Date: Fri, 01 Apr 2022 18:02:09 UTC
The branch main has been updated by mjg:

URL: https://cgit.FreeBSD.org/src/commit/?id=c4a08ef2af6fe0b0d1b03f7ec4f20bed96de20e8

commit c4a08ef2af6fe0b0d1b03f7ec4f20bed96de20e8
Author:     Mateusz Guzik <mjg@FreeBSD.org>
AuthorDate: 2022-04-01 15:04:03 +0000
Commit:     Mateusz Guzik <mjg@FreeBSD.org>
CommitDate: 2022-04-01 18:01:48 +0000

    pf: handle duplicate rules gracefully
    
    Reviewed by:    kp
    Reported by:    dch
    PR:             262971
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c   |  2 ++
 sbin/pfctl/pfctl.c        | 19 +++++++++++++++++--
 sys/netpfil/pf/pf_ioctl.c |  5 +++--
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 8696ef1ace25..991d3fce9780 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -961,6 +961,8 @@ pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor,
 	nv.size = nv.len;
 
 	ret = ioctl(dev, DIOCADDRULENV, &nv);
+	if (ret == -1)
+		ret = errno;
 
 	free(nv.data);
 	nvlist_destroy(nvl);
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 67358a325f77..13e8e825c1ab 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1846,6 +1846,8 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
 	u_int32_t		ticket;
 	char			anchor[PF_ANCHOR_NAME_SIZE];
 	int			len = strlen(path);
+	int			error;
+	bool			was_present;
 
 	/* set up anchor before adding to path for anchor_call */
 	if ((pf->opts & PF_OPT_NOACTION) == 0)
@@ -1867,12 +1869,23 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
 	} else
 		name = "";
 
+	was_present = false;
 	if ((pf->opts & PF_OPT_NOACTION) == 0) {
 		if (pfctl_add_pool(pf, &r->rpool, r->af))
 			return (1);
-		if (pfctl_add_rule(pf->dev, r, anchor, name, ticket,
-		    pf->paddr.ticket))
+		error = pfctl_add_rule(pf->dev, r, anchor, name, ticket,
+		    pf->paddr.ticket);
+		switch (error) {
+		case 0:
+			/* things worked, do nothing */
+			break;
+		case EEXIST:
+			/* an identical rule is already present */
+			was_present = true;
+			break;
+		default:
 			err(1, "DIOCADDRULENV");
+		}
 	}
 
 	if (pf->opts & PF_OPT_VERBOSE) {
@@ -1880,6 +1893,8 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
 		print_rule(r, r->anchor ? r->anchor->name : "",
 		    pf->opts & PF_OPT_VERBOSE2,
 		    pf->opts & PF_OPT_NUMERIC);
+		if (was_present)
+			printf(" -- rule was already present");
 	}
 	path[len] = '\0';
 	pfctl_clear_pool(&r->rpool);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index eae7b3bf1fa0..c170a270454b 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2240,10 +2240,11 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
 	pf_hash_rule(rule);
 	if (RB_INSERT(pf_krule_global, ruleset->rules[rs_num].inactive.tree, rule) != NULL) {
 		PF_RULES_WLOCK();
+		TAILQ_REMOVE(ruleset->rules[rs_num].inactive.ptr, rule, entries);
+		ruleset->rules[rs_num].inactive.rcount--;
 		pf_free_rule(rule);
 		rule = NULL;
-		error = EINVAL;
-		ERROUT(error);
+		ERROUT(EEXIST);
 	}
 	PF_CONFIG_UNLOCK();