Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 13 Jul 2023 09:02:04 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 6fc7fc2dbb2b - main - pfsync: transport over IPv6
Message-ID:  <202307130902.36D924RY079824@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

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

commit 6fc7fc2dbb2b246f529013807fdfa1ffb8364dc1
Author:     Luiz Amaral <email@luiz.eng.br>
AuthorDate: 2023-07-13 06:06:24 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-07-13 06:09:42 +0000

    pfsync: transport over IPv6
    
    Implement pfsync over IPv6.
    
    Submitted by:   Luiz Amaral <email@luiz.eng.br>
    Submitted by:   Naman Sood <naman@freebsdfoundation.org>
    Reviewed by:    kp
    Sponsored by:   InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D40102
---
 sbin/ifconfig/ifpfsync.c       |  15 +-
 sys/netpfil/pf/if_pfsync.c     | 331 ++++++++++++++++++++++++++++++++++++-----
 sys/netpfil/pf/pfsync_nv.c     |  12 ++
 tests/sys/netpfil/pf/pfsync.sh | 126 ++++++++++++++++
 4 files changed, 440 insertions(+), 44 deletions(-)

diff --git a/sbin/ifconfig/ifpfsync.c b/sbin/ifconfig/ifpfsync.c
index 3e880c96a97c..1bd17c5376eb 100644
--- a/sbin/ifconfig/ifpfsync.c
+++ b/sbin/ifconfig/ifpfsync.c
@@ -227,12 +227,17 @@ setpfsync_syncpeer(if_ctx *ctx, const char *val, int dummy __unused)
 	case AF_INET: {
 		struct sockaddr_in *sin = satosin(peerres->ai_addr);
 
-		if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
-			errx(1, "syncpeer address cannot be multicast");
-
 		memcpy(&addr, sin, sizeof(*sin));
 		break;
 	}
+#endif
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = satosin6(peerres->ai_addr);
+
+		memcpy(&addr, sin6, sizeof(*sin6));
+		break;
+	}
 #endif
 	default:
 		errx(1, "syncpeer address %s not supported", val);
@@ -377,9 +382,9 @@ pfsync_status(if_ctx *ctx)
 	if (syncdev[0] != '\0')
 		printf("syncdev: %s ", syncdev);
 
-	if (syncpeer.ss_family == AF_INET &&
+	if ((syncpeer.ss_family == AF_INET &&
 	    ((struct sockaddr_in *)&syncpeer)->sin_addr.s_addr !=
-		htonl(INADDR_PFSYNC_GROUP)) {
+	    htonl(INADDR_PFSYNC_GROUP)) || syncpeer.ss_family == AF_INET6) {
 
 		struct sockaddr *syncpeer_sa =
 		    (struct sockaddr *)&syncpeer;
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index 4fe916c77ab8..5df51d20e05f 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -91,12 +91,15 @@ __FBSDID("$FreeBSD$");
 #include <net/if_types.h>
 #include <net/vnet.h>
 #include <net/pfvar.h>
+#include <net/route.h>
 #include <net/if_pfsync.h>
 
 #include <netinet/if_ether.h>
 #include <netinet/in.h>
 #include <netinet/in_var.h>
+#include <netinet6/in6_var.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/ip_carp.h>
 #include <netinet/ip_var.h>
 #include <netinet/tcp.h>
@@ -105,6 +108,7 @@ __FBSDID("$FreeBSD$");
 
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
 
 #include <netpfil/pf/pfsync_nv.h>
 
@@ -112,7 +116,8 @@ struct pfsync_bucket;
 struct pfsync_softc;
 
 union inet_template {
-	struct ip      ipv4;
+	struct ip	ipv4;
+	struct ip6_hdr	ipv6;
 };
 
 #define PFSYNC_MINPKT ( \
@@ -247,6 +252,7 @@ struct pfsync_softc {
 	struct ifnet		*sc_ifp;
 	struct ifnet		*sc_sync_if;
 	struct ip_moptions	sc_imo;
+	struct ip6_moptions	sc_im6o;
 	struct sockaddr_storage	sc_sync_peer;
 	uint32_t		sc_flags;
 	uint8_t			sc_maxupdates;
@@ -303,7 +309,7 @@ static void	pfsync_push(struct pfsync_bucket *);
 static void	pfsync_push_all(struct pfsync_softc *);
 static void	pfsyncintr(void *);
 static int	pfsync_multicast_setup(struct pfsync_softc *, struct ifnet *,
-		    struct in_mfilter *imf);
+		    struct in_mfilter *, struct in6_mfilter *);
 static void	pfsync_multicast_cleanup(struct pfsync_softc *);
 static void	pfsync_pointers_init(void);
 static void	pfsync_pointers_uninit(void);
@@ -368,6 +374,9 @@ static struct pfsync_bucket	*pfsync_get_bucket(struct pfsync_softc *,
 VNET_DEFINE(struct if_clone *, pfsync_cloner);
 #define	V_pfsync_cloner	VNET(pfsync_cloner)
 
+const struct in6_addr in6addr_linklocal_pfsync_group =
+	{{{ 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 }}};
 static int
 pfsync_clone_create(struct if_clone *ifc, int unit, caddr_t param)
 {
@@ -842,6 +851,109 @@ done:
 }
 #endif
 
+#ifdef INET6
+static int
+pfsync6_input(struct mbuf **mp, int *offp __unused, int proto __unused)
+{
+	struct pfsync_softc *sc = V_pfsyncif;
+	struct mbuf *m = *mp;
+	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+	struct pfsync_header *ph;
+	struct pfsync_subheader subh;
+
+	int offset, len, flags = 0;
+	int rv;
+	uint16_t count;
+
+	PF_RULES_RLOCK_TRACKER;
+
+	*mp = NULL;
+	V_pfsyncstats.pfsyncs_ipackets++;
+
+	/* Verify that we have a sync interface configured. */
+	if (!sc || !sc->sc_sync_if || !V_pf_status.running ||
+	    (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		goto done;
+
+	/* verify that the packet came in on the right interface */
+	if (sc->sc_sync_if != m->m_pkthdr.rcvif) {
+		V_pfsyncstats.pfsyncs_badif++;
+		goto done;
+	}
+
+	if_inc_counter(sc->sc_ifp, IFCOUNTER_IPACKETS, 1);
+	if_inc_counter(sc->sc_ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
+	/* verify that the IP TTL is 255. */
+	if (ip6->ip6_hlim != PFSYNC_DFLTTL) {
+		V_pfsyncstats.pfsyncs_badttl++;
+		goto done;
+	}
+
+
+	offset = sizeof(*ip6);
+	if (m->m_pkthdr.len < offset + sizeof(*ph)) {
+		V_pfsyncstats.pfsyncs_hdrops++;
+		goto done;
+	}
+
+	if (offset + sizeof(*ph) > m->m_len) {
+		if (m_pullup(m, offset + sizeof(*ph)) == NULL) {
+			V_pfsyncstats.pfsyncs_hdrops++;
+			return (IPPROTO_DONE);
+		}
+		ip6 = mtod(m, struct ip6_hdr *);
+	}
+	ph = (struct pfsync_header *)((char *)ip6 + offset);
+
+	/* verify the version */
+	if (ph->version != PFSYNC_VERSION) {
+		V_pfsyncstats.pfsyncs_badver++;
+		goto done;
+	}
+
+	len = ntohs(ph->len) + offset;
+	if (m->m_pkthdr.len < len) {
+		V_pfsyncstats.pfsyncs_badlen++;
+		goto done;
+	}
+
+	/*
+	 * Trusting pf_chksum during packet processing, as well as seeking
+	 * in interface name tree, require holding PF_RULES_RLOCK().
+	 */
+	PF_RULES_RLOCK();
+	if (!bcmp(&ph->pfcksum, &V_pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH))
+		flags = PFSYNC_SI_CKSUM;
+
+	offset += sizeof(*ph);
+	while (offset <= len - sizeof(subh)) {
+		m_copydata(m, offset, sizeof(subh), (caddr_t)&subh);
+		offset += sizeof(subh);
+
+		if (subh.action >= PFSYNC_ACT_MAX) {
+			V_pfsyncstats.pfsyncs_badact++;
+			PF_RULES_RUNLOCK();
+			goto done;
+		}
+
+		count = ntohs(subh.count);
+		V_pfsyncstats.pfsyncs_iacts[subh.action] += count;
+		rv = (*pfsync_acts[subh.action])(m, offset, count, flags, subh.action);
+		if (rv == -1) {
+			PF_RULES_RUNLOCK();
+			return (IPPROTO_DONE);
+		}
+
+		offset += rv;
+	}
+	PF_RULES_RUNLOCK();
+
+done:
+	m_freem(m);
+	return (IPPROTO_DONE);
+}
+#endif
+
 static int
 pfsync_in_clr(struct mbuf *m, int offset, int count, int flags, int action)
 {
@@ -1701,6 +1813,19 @@ pfsync_sendout(int schedswi, int c)
 		ip_fillid(ip);
 		break;
 	    }
+#endif
+#ifdef INET6
+	case AF_INET6:
+		{
+		struct ip6_hdr *ip6;
+
+		ip6 = mtod(m, struct ip6_hdr *);
+		bcopy(&sc->sc_template.ipv6, ip6, sizeof(*ip6));
+		aflen = offset = sizeof(*ip6);
+
+		ip6->ip6_plen = htons(m->m_pkthdr.len);
+		break;
+		}
 #endif
 	default:
 		m_freem(m);
@@ -2512,10 +2637,8 @@ pfsync_tx(struct pfsync_softc *sc, struct mbuf *m)
 			error = ip6_output(m, NULL, NULL, 0,
 			    NULL, NULL, NULL);
 		} else {
-			MPASS(false);
-			/* We don't support pfsync over IPv6. */
-			/*error = ip6_output(m, NULL, NULL,
-			    IP_RAWOUTPUT, &sc->sc_imo6, NULL);*/
+			error = ip6_output(m, NULL, NULL, 0,
+				&sc->sc_im6o, NULL, NULL);
 		}
 		break;
 #endif
@@ -2564,10 +2687,12 @@ pfsyncintr(void *arg)
 
 static int
 pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp,
-    struct in_mfilter *imf)
+    struct in_mfilter* imf, struct in6_mfilter* im6f)
 {
 	struct ip_moptions *imo = &sc->sc_imo;
+	struct ip6_moptions *im6o = &sc->sc_im6o;
 	int error;
+	struct sockaddr_in6 *syncpeer_sa6 = NULL;
 
 	if (!(ifp->if_flags & IFF_MULTICAST))
 		return (EADDRNOTAVAIL);
@@ -2578,9 +2703,12 @@ pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp,
 	    {
 		ip_mfilter_init(&imo->imo_head);
 		imo->imo_multicast_vif = -1;
-		if ((error = in_joingroup(ifp, &((struct sockaddr_in *)&sc->sc_sync_peer)->sin_addr, NULL,
-		    &imf->imf_inm)) != 0)
+		if ((error = in_joingroup(ifp,
+		    &(((struct sockaddr_in *)&sc->sc_sync_peer)->sin_addr),
+		    NULL, &imf->imf_inm)) != 0)
+		{
 			return (error);
+		}
 
 		ip_mfilter_insert(&imo->imo_head, imf);
 		imo->imo_multicast_ifp = ifp;
@@ -2589,7 +2717,29 @@ pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp,
 		break;
 	    }
 #endif
+#ifdef INET6
+	case AF_INET6:
+	    {
+		syncpeer_sa6 = (struct sockaddr_in6 *)&sc->sc_sync_peer;
+		if ((error = in6_setscope(&syncpeer_sa6->sin6_addr, ifp, NULL)))
+		{
+			return (error);
+		}
+		ip6_mfilter_init(&im6o->im6o_head);
+		if ((error = in6_joingroup(ifp, &syncpeer_sa6->sin6_addr, NULL,
+		    &(im6f->im6f_in6m), 0)) != 0)
+		{
+			return (error);
+		}
+
+		ip6_mfilter_insert(&im6o->im6o_head, im6f);
+		im6o->im6o_multicast_ifp = ifp;
+		im6o->im6o_multicast_hlim = PFSYNC_DFLTTL;
+		im6o->im6o_multicast_loop = 0;
+		break;
+	    }
 	}
+#endif
 
 	return (0);
 }
@@ -2598,7 +2748,9 @@ static void
 pfsync_multicast_cleanup(struct pfsync_softc *sc)
 {
 	struct ip_moptions *imo = &sc->sc_imo;
+	struct ip6_moptions *im6o = &sc->sc_im6o;
 	struct in_mfilter *imf;
+	struct in6_mfilter *im6f;
 
 	while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
 		ip_mfilter_remove(&imo->imo_head, imf);
@@ -2606,6 +2758,13 @@ pfsync_multicast_cleanup(struct pfsync_softc *sc)
 		ip_mfilter_free(imf);
 	}
 	imo->imo_multicast_ifp = NULL;
+
+	while ((im6f = ip6_mfilter_first(&im6o->im6o_head)) != NULL) {
+		ip6_mfilter_remove(&im6o->im6o_head, im6f);
+		in6_leavegroup(im6f->im6f_in6m, NULL);
+		ip6_mfilter_free(im6f);
+	}
+	im6o->im6o_multicast_ifp = NULL;
 }
 
 void
@@ -2625,6 +2784,7 @@ pfsync_detach_ifnet(struct ifnet *ifp)
 		 */
 		ip_mfilter_init(&sc->sc_imo.imo_head);
 		sc->sc_imo.imo_multicast_ifp = NULL;
+		sc->sc_im6o.im6o_multicast_ifp = NULL;
 		sc->sc_sync_if = NULL;
 	}
 
@@ -2655,9 +2815,11 @@ pfsync_pfsyncreq_to_kstatus(struct pfsyncreq *pfsyncr, struct pfsync_kstatus *st
 static int
 pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 {
-	struct in_mfilter *imf = NULL;
 	struct ifnet *sifp;
-	struct ip *ip;
+	struct in_mfilter *imf = NULL;
+	struct in6_mfilter *im6f = NULL;
+	struct sockaddr_in *status_sin;
+	struct sockaddr_in6 *status_sin6;
 	int error;
 	int c;
 
@@ -2669,12 +2831,45 @@ pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 	else if ((sifp = ifunit_ref(status->syncdev)) == NULL)
 		return (EINVAL);
 
-	struct sockaddr_in *status_sin =
-	    (struct sockaddr_in *)&(status->syncpeer);
-	if (sifp != NULL && (status_sin->sin_addr.s_addr == 0 ||
-				status_sin->sin_addr.s_addr ==
-				    htonl(INADDR_PFSYNC_GROUP)))
-		imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
+	switch (status->syncpeer.ss_family) {
+	case AF_UNSPEC:
+	case AF_INET: {
+		status_sin = (struct sockaddr_in *)&(status->syncpeer);
+		if (sifp != NULL) {
+			if (status_sin->sin_addr.s_addr == 0 ||
+			    status_sin->sin_addr.s_addr ==
+			    htonl(INADDR_PFSYNC_GROUP)) {
+				status_sin->sin_family = AF_INET;
+				status_sin->sin_len = sizeof(*status_sin);
+				status_sin->sin_addr.s_addr =
+				    htonl(INADDR_PFSYNC_GROUP);
+			}
+
+			if (IN_MULTICAST(ntohl(status_sin->sin_addr.s_addr))) {
+				imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
+			}
+		}
+		break;
+	}
+	case AF_INET6: {
+		status_sin6 = (struct sockaddr_in6*)&(status->syncpeer);
+		if (sifp != NULL) {
+			if (IN6_IS_ADDR_UNSPECIFIED(&status_sin6->sin6_addr) ||
+			    IN6_ARE_ADDR_EQUAL(&status_sin6->sin6_addr,
+				&in6addr_linklocal_pfsync_group)) {
+				status_sin6->sin6_family = AF_INET6;
+				status_sin6->sin6_len = sizeof(*status_sin6);
+				status_sin6->sin6_addr =
+				    in6addr_linklocal_pfsync_group;
+			}
+
+			if (IN6_IS_ADDR_MULTICAST(&status_sin6->sin6_addr)) {
+				im6f = ip6_mfilter_alloc(M_WAITOK, 0, 0);
+			}
+		}
+		break;
+	}
+	}
 
 	PFSYNC_LOCK(sc);
 
@@ -2691,13 +2886,31 @@ pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 			return (EINVAL);
 	}
 
-	struct sockaddr_in *sc_sin = (struct sockaddr_in *)&sc->sc_sync_peer;
-	sc_sin->sin_family = AF_INET;
-	sc_sin->sin_len = sizeof(*sc_sin);
-	if (status_sin->sin_addr.s_addr == 0) {
-		sc_sin->sin_addr.s_addr = htonl(INADDR_PFSYNC_GROUP);
-	} else {
-		sc_sin->sin_addr.s_addr = status_sin->sin_addr.s_addr;
+	switch (status->syncpeer.ss_family) {
+	case AF_INET: {
+		struct sockaddr_in *status_sin = (struct sockaddr_in *)&(status->syncpeer);
+		struct sockaddr_in *sc_sin = (struct sockaddr_in *)&sc->sc_sync_peer;
+		sc_sin->sin_family = AF_INET;
+		sc_sin->sin_len = sizeof(*sc_sin);
+		if (status_sin->sin_addr.s_addr == 0) {
+			sc_sin->sin_addr.s_addr = htonl(INADDR_PFSYNC_GROUP);
+		} else {
+			sc_sin->sin_addr.s_addr = status_sin->sin_addr.s_addr;
+		}
+		break;
+	}
+	case AF_INET6: {
+		struct sockaddr_in6 *status_sin = (struct sockaddr_in6 *)&(status->syncpeer);
+		struct sockaddr_in6 *sc_sin = (struct sockaddr_in6 *)&sc->sc_sync_peer;
+		sc_sin->sin6_family = AF_INET6;
+		sc_sin->sin6_len = sizeof(*sc_sin);
+		if(IN6_IS_ADDR_UNSPECIFIED(&status_sin->sin6_addr)) {
+			sc_sin->sin6_addr = in6addr_linklocal_pfsync_group;
+		} else {
+			sc_sin->sin6_addr = status_sin->sin6_addr;
+		}
+		break;
+	}
 	}
 
 	sc->sc_maxupdates = status->maxupdates;
@@ -2731,12 +2944,20 @@ pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 
 	pfsync_multicast_cleanup(sc);
 
-	if (sc_sin->sin_addr.s_addr == htonl(INADDR_PFSYNC_GROUP)) {
-		error = pfsync_multicast_setup(sc, sifp, imf);
+	if (((sc->sc_sync_peer.ss_family == AF_INET) &&
+	    IN_MULTICAST(ntohl(((struct sockaddr_in *)
+	        &sc->sc_sync_peer)->sin_addr.s_addr))) ||
+	    ((sc->sc_sync_peer.ss_family == AF_INET6) &&
+	    IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)
+	        &sc->sc_sync_peer)->sin6_addr))) {
+		error = pfsync_multicast_setup(sc, sifp, imf, im6f);
 		if (error) {
 			if_rele(sifp);
-			ip_mfilter_free(imf);
 			PFSYNC_UNLOCK(sc);
+			if (imf != NULL)
+				ip_mfilter_free(imf);
+			if (im6f != NULL)
+				ip6_mfilter_free(im6f);
 			return (error);
 		}
 	}
@@ -2744,17 +2965,39 @@ pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 		if_rele(sc->sc_sync_if);
 	sc->sc_sync_if = sifp;
 
-	ip = &sc->sc_template.ipv4;
-	bzero(ip, sizeof(*ip));
-	ip->ip_v = IPVERSION;
-	ip->ip_hl = sizeof(sc->sc_template.ipv4) >> 2;
-	ip->ip_tos = IPTOS_LOWDELAY;
-	/* len and id are set later. */
-	ip->ip_off = htons(IP_DF);
-	ip->ip_ttl = PFSYNC_DFLTTL;
-	ip->ip_p = IPPROTO_PFSYNC;
-	ip->ip_src.s_addr = INADDR_ANY;
-	ip->ip_dst.s_addr = sc_sin->sin_addr.s_addr;
+	switch (sc->sc_sync_peer.ss_family) {
+	case AF_INET: {
+		struct ip *ip;
+		ip = &sc->sc_template.ipv4;
+		bzero(ip, sizeof(*ip));
+		ip->ip_v = IPVERSION;
+		ip->ip_hl = sizeof(sc->sc_template.ipv4) >> 2;
+		ip->ip_tos = IPTOS_LOWDELAY;
+		/* len and id are set later. */
+		ip->ip_off = htons(IP_DF);
+		ip->ip_ttl = PFSYNC_DFLTTL;
+		ip->ip_p = IPPROTO_PFSYNC;
+		ip->ip_src.s_addr = INADDR_ANY;
+		ip->ip_dst = ((struct sockaddr_in *)&sc->sc_sync_peer)->sin_addr;
+		break;
+	}
+	case AF_INET6: {
+		struct ip6_hdr *ip6;
+		ip6 = &sc->sc_template.ipv6;
+		bzero(ip6, sizeof(*ip6));
+		ip6->ip6_vfc = IPV6_VERSION;
+		ip6->ip6_hlim = PFSYNC_DFLTTL;
+		ip6->ip6_nxt = IPPROTO_PFSYNC;
+		ip6->ip6_dst = ((struct sockaddr_in6 *)&sc->sc_sync_peer)->sin6_addr;
+
+		struct epoch_tracker et;
+		NET_EPOCH_ENTER(et);
+		in6_selectsrc_addr(if_getfib(sc->sc_sync_if), &ip6->ip6_dst, 0,
+		    sc->sc_sync_if, &ip6->ip6_src, NULL);
+		NET_EPOCH_EXIT(et);
+		break;
+	}
+	}
 
 	/* Request a full state table update. */
 	if ((sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p)
@@ -2841,15 +3084,22 @@ VNET_SYSUNINIT(vnet_pfsync_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_FOURTH,
 static int
 pfsync_init(void)
 {
-#ifdef INET
 	int error;
 
 	pfsync_detach_ifnet_ptr = pfsync_detach_ifnet;
 
+#ifdef INET
 	error = ipproto_register(IPPROTO_PFSYNC, pfsync_input, NULL);
 	if (error)
 		return (error);
 #endif
+#ifdef INET6
+	error = ip6proto_register(IPPROTO_PFSYNC, pfsync6_input, NULL);
+	if (error) {
+		ipproto_unregister(IPPROTO_PFSYNC);
+		return (error);
+	}
+#endif
 
 	return (0);
 }
@@ -2862,6 +3112,9 @@ pfsync_uninit(void)
 #ifdef INET
 	ipproto_unregister(IPPROTO_PFSYNC);
 #endif
+#ifdef INET6
+	ip6proto_unregister(IPPROTO_PFSYNC);
+#endif
 }
 
 static int
diff --git a/sys/netpfil/pf/pfsync_nv.c b/sys/netpfil/pf/pfsync_nv.c
index d4a839581332..1a461d138fc7 100644
--- a/sys/netpfil/pf/pfsync_nv.c
+++ b/sys/netpfil/pf/pfsync_nv.c
@@ -35,6 +35,11 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/errno.h>
 
+#include <netinet/in.h>
+
+#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
+
 #include <netpfil/pf/pfsync_nv.h>
 
 int
@@ -42,6 +47,7 @@ pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *nvl,
     struct sockaddr_storage *sa)
 {
 	int af;
+	int error;
 
 	if (!nvlist_exists_number(nvl, "af"))
 		return (EINVAL);
@@ -74,6 +80,11 @@ pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *nvl,
 			return (EINVAL);
 
 		memcpy(in6, addr, sizeof(*in6));
+
+		error = sa6_embedscope(in6, V_ip6_use_defzone);
+		if (error)
+			return (error);
+
 		break;
 	}
 #endif
@@ -106,6 +117,7 @@ pfsync_sockaddr_to_syncpeer_nvlist(struct sockaddr_storage *sa)
 #ifdef INET6
 	case AF_INET6: {
 		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
+		sa6_recoverscope(in6);
 		nvlist_add_number(nvl, "af", in6->sin6_family);
 		nvlist_add_binary(nvl, "address", in6, sizeof(*in6));
 		break;
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
index 1b61ec4f03a0..6ee2038c6eee 100644
--- a/tests/sys/netpfil/pf/pfsync.sh
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -702,6 +702,130 @@ timeout_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "basic_ipv6_unicast" "cleanup"
+basic_ipv6_unicast_head()
+{
+	atf_set descr 'Basic pfsync test (IPv6)'
+	atf_set require.user root
+}
+
+basic_ipv6_unicast_body()
+{
+	pfsynct_init
+
+	epair_sync=$(vnet_mkepair)
+	epair_one=$(vnet_mkepair)
+	epair_two=$(vnet_mkepair)
+
+	vnet_mkjail one ${epair_one}a ${epair_sync}a
+	vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+	# pfsync interface
+	jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up
+	jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up
+	jexec one ifconfig pfsync0 \
+		syncdev ${epair_sync}a \
+		syncpeer fd2c::2 \
+		maxupd 1 \
+		up
+	jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up
+	jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up
+	jexec two ifconfig pfsync0 \
+		syncdev ${epair_sync}b \
+		syncpeer fd2c::1 \
+		maxupd 1 \
+		up
+
+	# Enable pf!
+	jexec one pfctl -e
+	pft_set_rules one \
+		"block on ${epair_sync}a inet" \
+		"pass out keep state"
+	jexec two pfctl -e
+	pft_set_rules two \
+		"block on ${epair_sync}b inet" \
+		"pass out keep state"
+
+	ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up
+
+	ping6 -c 1 -S fd2b::f0 fd2b::1
+
+	# Give pfsync time to do its thing
+	sleep 2
+
+	if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \
+	    grep fd2b::f0 ; then
+		atf_fail "state not found on synced host"
+	fi
+}
+
+basic_ipv6_unicast_cleanup()
+{
+	pfsynct_cleanup
+}
+
+atf_test_case "basic_ipv6" "cleanup"
+basic_ipv6_head()
+{
+	atf_set descr 'Basic pfsync test (IPv6)'
+	atf_set require.user root
+}
+
+basic_ipv6_body()
+{
+	pfsynct_init
+
+	epair_sync=$(vnet_mkepair)
+	epair_one=$(vnet_mkepair)
+	epair_two=$(vnet_mkepair)
+
+	vnet_mkjail one ${epair_one}a ${epair_sync}a
+	vnet_mkjail two ${epair_two}a ${epair_sync}b
+
+	# pfsync interface
+	jexec one ifconfig ${epair_sync}a inet6 fd2c::1/64 no_dad up
+	jexec one ifconfig ${epair_one}a inet6 fd2b::1/64 no_dad up
+	jexec one ifconfig pfsync0 \
+		syncdev ${epair_sync}a \
+		syncpeer ff12::f0 \
+		maxupd 1 \
+		up
+	jexec two ifconfig ${epair_two}a inet6 fd2b::2/64 no_dad up
+	jexec two ifconfig ${epair_sync}b inet6 fd2c::2/64 no_dad up
+	jexec two ifconfig pfsync0 \
+		syncdev ${epair_sync}b \
+		syncpeer ff12::f0 \
+		maxupd 1 \
+		up
+
+	# Enable pf!
+	jexec one pfctl -e
+	pft_set_rules one \
+		"block on ${epair_sync}a inet" \
+		"pass out keep state"
+	jexec two pfctl -e
+	pft_set_rules two \
+		"block on ${epair_sync}b inet" \
+		"pass out keep state"
+
+	ifconfig ${epair_one}b inet6 fd2b::f0/64 no_dad up
+
+	ping6 -c 1 -S fd2b::f0 fd2b::1
+
+	# Give pfsync time to do its thing
+	sleep 2
+
+	if ! jexec two pfctl -s states | grep icmp | grep fd2b::1 | \
+	    grep fd2b::f0 ; then
+		atf_fail "state not found on synced host"
+	fi
+}
+
+basic_ipv6_cleanup()
+{
+	pfsynct_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "basic"
@@ -712,4 +836,6 @@ atf_init_test_cases()
 	atf_add_test_case "pfsync_pbr"
 	atf_add_test_case "ipsec"
 	atf_add_test_case "timeout"
+	atf_add_test_case "basic_ipv6_unicast"
+	atf_add_test_case "basic_ipv6"
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202307130902.36D924RY079824>