git: 7d381d0a5b50 - main - pf: exclude link local address from the dynamic interface address pool

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 19 Sep 2024 20:21:26 UTC
The branch main has been updated by kp:

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

commit 7d381d0a5b50539638a2460c9ae2213af5c3fad8
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-09-05 15:38:20 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-09-19 20:20:14 +0000

    pf: exclude link local address from the dynamic interface address pool
    
    so that rules like "pass out on vr1 inet6 nat-to (vr1)" won't map
    to the non routable ipv6 link local address; with suggestions and
    ok claudio, henning
    
    Reviewed by:    zlei
    Obtained from:  OpenBSD, mikeb <mikeb@openbsd.org>, e41548933f
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D46594
---
 sys/net/pfvar.h           |  4 +++-
 sys/netpfil/pf/pf_lb.c    | 17 +++++++++++++----
 sys/netpfil/pf/pf_table.c |  9 ++++++++-
 3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 8335fbfaedb8..a5a0ed257ef3 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1228,6 +1228,7 @@ typedef void		pfsync_clear_states_t(u_int32_t, const char *);
 typedef int		pfsync_defer_t(struct pf_kstate *, struct mbuf *);
 typedef void		pfsync_detach_ifnet_t(struct ifnet *);
 typedef void		pflow_export_state_t(const struct pf_kstate *);
+typedef bool		pf_addr_filter_func_t(const sa_family_t, const struct pf_addr *);
 
 VNET_DECLARE(pfsync_state_import_t *, pfsync_state_import_ptr);
 #define V_pfsync_state_import_ptr	VNET(pfsync_state_import_ptr)
@@ -2429,7 +2430,8 @@ void	pfr_cleanup(void);
 int	pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
 void	pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t,
 	    u_int64_t, int, int, int);
-int	pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, sa_family_t);
+int	pfr_pool_get(struct pfr_ktable *, int *, struct pf_addr *, sa_family_t,
+	    pf_addr_filter_func_t);
 void	pfr_dynaddr_update(struct pfr_ktable *, struct pfi_dynaddr *);
 struct pfr_ktable *
 	pfr_attach_table(struct pf_kruleset *, char *);
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index 6541a42aa236..b322bd65cfd3 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -71,6 +71,7 @@ static int pf_get_sport(sa_family_t, uint8_t, struct pf_krule *,
     struct pf_addr *, uint16_t, struct pf_addr *, uint16_t, struct pf_addr *,
     uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
     struct pf_udp_mapping **);
+static bool		 pf_islinklocal(const sa_family_t, const struct pf_addr *);
 
 #define mix(a,b,c) \
 	do {					\
@@ -401,6 +402,14 @@ failed:
 	return (1);					/* none available */
 }
 
+static bool
+pf_islinklocal(const sa_family_t af, const struct pf_addr *addr)
+{
+	if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&addr->v6))
+		return (true);
+	return (false);
+}
+
 static int
 pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
     struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
@@ -585,11 +594,11 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 
 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 			if (!pfr_pool_get(rpool->cur->addr.p.tbl,
-			    &rpool->tblidx, &rpool->counter, af))
+			    &rpool->tblidx, &rpool->counter, af, NULL))
 				goto get_addr;
 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
-			    &rpool->tblidx, &rpool->counter, af))
+			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal))
 				goto get_addr;
 		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
 			goto get_addr;
@@ -602,7 +611,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 			rpool->tblidx = -1;
 			if (pfr_pool_get(rpool->cur->addr.p.tbl,
-			    &rpool->tblidx, &rpool->counter, af)) {
+			    &rpool->tblidx, &rpool->counter, af, NULL)) {
 				/* table contains no address of type 'af' */
 				if (rpool->cur != acur)
 					goto try_next;
@@ -612,7 +621,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			rpool->tblidx = -1;
 			if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
-			    &rpool->tblidx, &rpool->counter, af)) {
+			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal)) {
 				/* table contains no address of type 'af' */
 				if (rpool->cur != acur)
 					goto try_next;
diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c
index 4d248e40443d..690cb6d9ab90 100644
--- a/sys/netpfil/pf/pf_table.c
+++ b/sys/netpfil/pf/pf_table.c
@@ -2240,7 +2240,7 @@ pfr_detach_table(struct pfr_ktable *kt)
 
 int
 pfr_pool_get(struct pfr_ktable *kt, int *pidx, struct pf_addr *counter,
-    sa_family_t af)
+    sa_family_t af, pf_addr_filter_func_t filter)
 {
 	struct pf_addr		 addr, cur, mask, umask_addr;
 	union sockaddr_union	 uaddr, umask;
@@ -2299,6 +2299,10 @@ _next_block:
 
 	if (!KENTRY_NETWORK(ke)) {
 		/* this is a single IP address - no possible nested block */
+		if (filter && filter(af, &addr)) {
+			idx++;
+			goto _next_block;
+		}
 		PF_ACPY(counter, &addr, af);
 		*pidx = idx;
 		pfr_kstate_counter_add(&kt->pfrkt_match, 1);
@@ -2319,12 +2323,15 @@ _next_block:
 		/* no need to check KENTRY_RNF_ROOT() here */
 		if (ke2 == ke) {
 			/* lookup return the same block - perfect */
+			if (filter && filter(af, &addr))
+				goto _next_entry;
 			PF_ACPY(counter, &addr, af);
 			*pidx = idx;
 			pfr_kstate_counter_add(&kt->pfrkt_match, 1);
 			return (0);
 		}
 
+_next_entry:
 		/* we need to increase the counter past the nested block */
 		pfr_prepare_network(&umask, AF_INET, ke2->pfrke_net);
 		pfr_sockaddr_to_pf_addr(&umask, &umask_addr);