git: 813c5b75e680 - main - pfsync: prepare code to accommodate AF_INET6 family

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 09 Nov 2022 21:06:37 UTC
The branch main has been updated by kp:

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

commit 813c5b75e68073a724fdc032b24a12baeab8f6d9
Author:     Luiz Amaral <email@luiz.eng.br>
AuthorDate: 2022-11-09 11:40:43 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-11-09 20:06:07 +0000

    pfsync: prepare code to accommodate AF_INET6 family
    
    Work is ongoing to add support for pfsync over IPv6. This required some
    changes to allow for differentiating between the two families in a more
    generic way.
    
    This patch converts the relevant ioctls to using nvlists, making future
    extensions (such as supporting IPv6 addresses) easier.
    
    Sponsored by:   InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D36277
---
 sbin/ifconfig/ifpfsync.c    | 322 ++++++++++++++++++++++++++---------
 sys/conf/files              |   1 +
 sys/modules/pfsync/Makefile |   2 +-
 sys/net/if_pfsync.h         |  15 ++
 sys/netpfil/pf/if_pfsync.c  | 404 +++++++++++++++++++++++++++++++-------------
 sys/netpfil/pf/pfsync_nv.c  | 150 ++++++++++++++++
 sys/netpfil/pf/pfsync_nv.h  |  42 +++++
 7 files changed, 737 insertions(+), 199 deletions(-)

diff --git a/sbin/ifconfig/ifpfsync.c b/sbin/ifconfig/ifpfsync.c
index e3b5e6a30bbc..90646ff00223 100644
--- a/sbin/ifconfig/ifpfsync.c
+++ b/sbin/ifconfig/ifpfsync.c
@@ -29,7 +29,9 @@
  */
 
 #include <sys/param.h>
+#include <sys/errno.h>
 #include <sys/ioctl.h>
+#include <sys/nv.h>
 #include <sys/socket.h>
 
 #include <net/if.h>
@@ -57,71 +59,202 @@ void setpfsync_maxupd(const char *, int, int, const struct afswtch *);
 void setpfsync_defer(const char *, int, int, const struct afswtch *);
 void pfsync_status(int);
 
+static int
+pfsync_do_ioctl(int s, uint cmd, nvlist_t **nvl)
+{
+	void *data;
+	size_t nvlen;
+
+	data = nvlist_pack(*nvl, &nvlen);
+
+	ifr.ifr_cap_nv.buffer = malloc(IFR_CAP_NV_MAXBUFSIZE);
+	memcpy(ifr.ifr_cap_nv.buffer, data, nvlen);
+	ifr.ifr_cap_nv.buf_length = IFR_CAP_NV_MAXBUFSIZE;
+	ifr.ifr_cap_nv.length = nvlen;
+	free(data);
+
+	if (ioctl(s, cmd, (caddr_t)&ifr) == -1) {
+		free(ifr.ifr_cap_nv.buffer);
+		return -1;
+	}
+
+	nvlist_destroy(*nvl);
+	*nvl = NULL;
+
+	*nvl = nvlist_unpack(ifr.ifr_cap_nv.buffer, ifr.ifr_cap_nv.length, 0);
+	if (*nvl == NULL) {
+		free(ifr.ifr_cap_nv.buffer);
+		return (EIO);
+	}
+
+	free(ifr.ifr_cap_nv.buffer);
+	return (errno);
+}
+
+static nvlist_t *
+pfsync_sockaddr_to_syncpeer_nvlist(struct sockaddr_storage *sa)
+{
+	nvlist_t *nvl;
+
+	nvl = nvlist_create(0);
+	if (nvl == NULL) {
+		return (nvl);
+	}
+
+	switch (sa->ss_family) {
+#ifdef INET
+	case AF_INET: {
+		struct sockaddr_in *in = (struct sockaddr_in *)sa;
+		nvlist_add_number(nvl, "af", in->sin_family);
+		nvlist_add_binary(nvl, "address", in, sizeof(*in));
+		break;
+	}
+#endif
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
+		nvlist_add_number(nvl, "af", in6->sin6_family);
+		nvlist_add_binary(nvl, "address", in6, sizeof(*in6));
+		break;
+	}
+#endif
+	default:
+		nvlist_add_number(nvl, "af", AF_UNSPEC);
+		nvlist_add_binary(nvl, "address", sa, sizeof(*sa));
+		break;
+	}
+
+	return (nvl);
+}
+
+static int
+pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *nvl,
+    struct sockaddr_storage *sa)
+{
+	int af;
+
+	if (!nvlist_exists_number(nvl, "af"))
+		return (EINVAL);
+	if (!nvlist_exists_binary(nvl, "address"))
+		return (EINVAL);
+
+	af = nvlist_get_number(nvl, "af");
+
+	switch (af) {
+#ifdef INET
+	case AF_INET: {
+		struct sockaddr_in *in = (struct sockaddr_in *)sa;
+		size_t len;
+		const void *addr = nvlist_get_binary(nvl, "address", &len);
+		in->sin_family = af;
+		if (len != sizeof(*in))
+			return (EINVAL);
+
+		memcpy(in, addr, sizeof(*in));
+		break;
+	}
+#endif
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
+		size_t len;
+		const void *addr = nvlist_get_binary(nvl, "address", &len);
+		if (len != sizeof(*in6))
+			return (EINVAL);
+
+		memcpy(in6, addr, sizeof(*in6));
+		break;
+	}
+#endif
+	default:
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
 void
 setpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
+	nvlist_t *nvl = nvlist_create(0);
+
+	if (strlen(val) > IFNAMSIZ)
+		errx(1, "interface name %s is too long", val);
 
-	bzero((char *)&preq, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	if (nvlist_exists_string(nvl, "syncdev"))
+		nvlist_free_string(nvl, "syncdev");
 
-	strlcpy(preq.pfsyncr_syncdev, val, sizeof(preq.pfsyncr_syncdev));
+	nvlist_add_string(nvl, "syncdev", val);
 
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
 }
 
 /* ARGSUSED */
 void
 unsetpfsync_syncdev(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
+	nvlist_t *nvl = nvlist_create(0);
 
-	bzero((char *)&preq, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	if (nvlist_exists_string(nvl, "syncdev"))
+		nvlist_free_string(nvl, "syncdev");
 
-	bzero((char *)&preq.pfsyncr_syncdev, sizeof(preq.pfsyncr_syncdev));
+	nvlist_add_string(nvl, "syncdev", "");
 
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
 }
 
 /* ARGSUSED */
 void
 setpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
-	struct addrinfo hints, *peerres;
+	struct addrinfo *peerres;
+	struct sockaddr_storage addr;
 	int ecode;
 
-	bzero((char *)&preq, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
-
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	nvlist_t *nvl = nvlist_create(0);
 
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = AF_INET;
-	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	if ((ecode = getaddrinfo(val, NULL, &hints, &peerres)) != 0)
+	if ((ecode = getaddrinfo(val, NULL, NULL, &peerres)) != 0)
 		errx(1, "error in parsing address string: %s",
 		    gai_strerror(ecode));
 
-	if (peerres->ai_addr->sa_family != AF_INET)
-		errx(1, "only IPv4 addresses supported for the syncpeer");
+	switch (peerres->ai_family) {
+#ifdef INET
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)
+					      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
+	default:
+		errx(1, "syncpeer address %s not supported", val);
+	}
+
+	if (nvlist_exists_nvlist(nvl, "syncpeer"))
+		nvlist_free_nvlist(nvl, "syncpeer");
+
+	nvlist_add_nvlist(nvl, "syncpeer",
+	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
 
-	preq.pfsyncr_syncpeer.s_addr = ((struct sockaddr_in *)
-	    peerres->ai_addr)->sin_addr.s_addr;
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
 
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	nvlist_destroy(nvl);
 	freeaddrinfo(peerres);
 }
 
@@ -129,88 +262,123 @@ setpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp)
 void
 unsetpfsync_syncpeer(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
+	struct sockaddr_storage addr;
+	memset(&addr, 0, sizeof(addr));
 
-	bzero((char *)&preq, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	nvlist_t *nvl = nvlist_create(0);
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	preq.pfsyncr_syncpeer.s_addr = 0;
+	if (nvlist_exists_nvlist(nvl, "syncpeer"))
+		nvlist_free_nvlist(nvl, "syncpeer");
 
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	nvlist_add_nvlist(nvl, "syncpeer",
+	    pfsync_sockaddr_to_syncpeer_nvlist(&addr));
+
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
+
+	nvlist_destroy(nvl);
 }
 
 /* ARGSUSED */
 void
 setpfsync_maxupd(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
 	int maxupdates;
+	nvlist_t *nvl = nvlist_create(0);
 
 	maxupdates = atoi(val);
 	if ((maxupdates < 0) || (maxupdates > 255))
 		errx(1, "maxupd %s: out of range", val);
 
-	memset((char *)&preq, 0, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	nvlist_free_number(nvl, "maxupdates");
+	nvlist_add_number(nvl, "maxupdates", maxupdates);
 
-	preq.pfsyncr_maxupdates = maxupdates;
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
 
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	nvlist_destroy(nvl);
 }
 
 /* ARGSUSED */
 void
 setpfsync_defer(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	struct pfsyncreq preq;
+	nvlist_t *nvl = nvlist_create(0);
+
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCGETPFSYNCNV");
 
-	memset((char *)&preq, 0, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	nvlist_free_number(nvl, "flags");
+	nvlist_add_number(nvl, "flags", d ? PFSYNCF_DEFER : 0);
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCGETPFSYNC");
+	if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1)
+		err(1, "SIOCSETPFSYNCNV");
 
-	preq.pfsyncr_defer = d ? PFSYNCF_DEFER : 0;
-	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
-		err(1, "SIOCSETPFSYNC");
+	nvlist_destroy(nvl);
 }
 
 void
 pfsync_status(int s)
 {
-	struct pfsyncreq preq;
+	nvlist_t *nvl;
+	char syncdev[IFNAMSIZ];
+	char syncpeer_str[NI_MAXHOST];
+	struct sockaddr_storage syncpeer;
+	int maxupdates;
+	int flags;
+	int error;
 
-	bzero((char *)&preq, sizeof(struct pfsyncreq));
-	ifr.ifr_data = (caddr_t)&preq;
+	nvl = nvlist_create(0);
 
-	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+	if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1) {
+		nvlist_destroy(nvl);
 		return;
+	}
+
+	memset((char *)&syncdev, 0, IFNAMSIZ);
+	if (nvlist_exists_string(nvl, "syncdev"))
+		strlcpy(syncdev, nvlist_get_string(nvl, "syncdev"),
+		    IFNAMSIZ);
+	if (nvlist_exists_number(nvl, "maxupdates"))
+		maxupdates = nvlist_get_number(nvl, "maxupdates");
+	if (nvlist_exists_number(nvl, "flags"))
+		flags = nvlist_get_number(nvl, "flags");
+	if (nvlist_exists_nvlist(nvl, "syncpeer")) {
+		pfsync_syncpeer_nvlist_to_sockaddr(nvlist_get_nvlist(nvl,
+							     "syncpeer"),
+		    &syncpeer);
+	}
+
+	nvlist_destroy(nvl);
+
+	if (syncdev[0] != '\0' || syncpeer.ss_family != AF_UNSPEC)
+		printf("\t");
 
-	if (preq.pfsyncr_syncdev[0] != '\0' ||
-	    preq.pfsyncr_syncpeer.s_addr != htonl(INADDR_PFSYNC_GROUP))
-			printf("\t");
-
-	if (preq.pfsyncr_syncdev[0] != '\0')
-		printf("pfsync: syncdev: %s ", preq.pfsyncr_syncdev);
-	if (preq.pfsyncr_syncpeer.s_addr != htonl(INADDR_PFSYNC_GROUP))
-		printf("syncpeer: %s ", inet_ntoa(preq.pfsyncr_syncpeer));
-
-	if (preq.pfsyncr_syncdev[0] != '\0' ||
-	    preq.pfsyncr_syncpeer.s_addr != htonl(INADDR_PFSYNC_GROUP)) {
-		printf("maxupd: %d ", preq.pfsyncr_maxupdates);
-		printf("defer: %s\n",
-		    (preq.pfsyncr_defer & PFSYNCF_DEFER) ? "on" : "off");
-		printf("\tsyncok: %d\n",
-		    (preq.pfsyncr_defer & PFSYNCF_OK) ? 1 : 0);
+	if (syncdev[0] != '\0')
+		printf("syncdev: %s ", syncdev);
+
+	if (syncpeer.ss_family == AF_INET &&
+	    ((struct sockaddr_in *)&syncpeer)->sin_addr.s_addr !=
+		htonl(INADDR_PFSYNC_GROUP)) {
+
+		struct sockaddr *syncpeer_sa =
+		    (struct sockaddr *)&syncpeer;
+		if ((error = getnameinfo(syncpeer_sa, syncpeer_sa->sa_len,
+			 syncpeer_str, sizeof(syncpeer_str), NULL, 0,
+			 NI_NUMERICHOST)) != 0)
+			errx(1, "getnameinfo: %s", gai_strerror(error));
+		printf("syncpeer: %s ", syncpeer_str);
 	}
+
+	printf("maxupd: %d ", maxupdates);
+	printf("defer: %s\n", (flags & PFSYNCF_DEFER) ? "on" : "off");
+	printf("\tsyncok: %d\n", (flags & PFSYNCF_OK) ? 1 : 0);
 }
 
 static struct cmd pfsync_cmds[] = {
diff --git a/sys/conf/files b/sys/conf/files
index 30ed2416987e..9aec63c6969e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -4530,6 +4530,7 @@ netpfil/pf/pf_osfp.c		optional pf inet
 netpfil/pf/pf_ruleset.c		optional pf inet
 netpfil/pf/pf_syncookies.c	optional pf inet
 netpfil/pf/pf_table.c		optional pf inet
+netpfil/pf/pfsync_nv.c		optional pfsync pf inet
 netpfil/pf/in4_cksum.c		optional pf inet
 netsmb/smb_conn.c		optional netsmb
 netsmb/smb_crypt.c		optional netsmb
diff --git a/sys/modules/pfsync/Makefile b/sys/modules/pfsync/Makefile
index 1c1191d95b14..aae850fa2d20 100644
--- a/sys/modules/pfsync/Makefile
+++ b/sys/modules/pfsync/Makefile
@@ -3,7 +3,7 @@
 .PATH: ${SRCTOP}/sys/netpfil/pf
 
 KMOD=	pfsync
-SRCS=	if_pfsync.c \
+SRCS=	if_pfsync.c pfsync_nv.c \
 	opt_pf.h opt_inet.h opt_inet6.h opt_global.h
 SRCS+=	bus_if.h device_if.h
 
diff --git a/sys/net/if_pfsync.h b/sys/net/if_pfsync.h
index 8c4626f3f6f7..9670baf9c9e7 100644
--- a/sys/net/if_pfsync.h
+++ b/sys/net/if_pfsync.h
@@ -247,8 +247,23 @@ struct pfsyncreq {
 	int		 pfsyncr_defer;
 };
 
+struct pfsync_kstatus {
+	char		 	syncdev[IFNAMSIZ];
+	struct sockaddr_storage	syncpeer;
+	int		 	maxupdates;
+	int		 	flags;
+};
+
+struct pfsyncioc_nv {
+	void            *data;
+	size_t           len;   /* The length of the nvlist data. */
+	size_t           size;  /* The total size of the data buffer. */
+};
+
 #define	SIOCSETPFSYNC   _IOW('i', 247, struct ifreq)
 #define	SIOCGETPFSYNC   _IOWR('i', 248, struct ifreq)
+#define	SIOCSETPFSYNCNV _IOW('i', 249, struct ifreq)
+#define	SIOCGETPFSYNCNV _IOWR('i', 250, struct ifreq)
 
 #ifdef _KERNEL
 
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index 9cc25c83bd06..f8cca8a4460b 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/mbuf.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
+#include <sys/nv.h>
 #include <sys/priv.h>
 #include <sys/smp.h>
 #include <sys/socket.h>
@@ -101,13 +102,19 @@ __FBSDID("$FreeBSD$");
 #include <netinet/tcp_fsm.h>
 #include <netinet/tcp_seq.h>
 
+#include <netpfil/pf/pfsync_nv.h>
+
+struct pfsync_bucket;
+
+union inet_template {
+	struct ip      ipv4;
+};
+
 #define PFSYNC_MINPKT ( \
-	sizeof(struct ip) + \
+	sizeof(union inet_template) + \
 	sizeof(struct pfsync_header) + \
 	sizeof(struct pfsync_subheader) )
 
-struct pfsync_bucket;
-
 static int	pfsync_upd_tcp(struct pf_kstate *, struct pfsync_state_peer *,
 		    struct pfsync_state_peer *);
 static int	pfsync_in_clr(struct mbuf *, int, int, int);
@@ -206,10 +213,10 @@ struct pfsync_softc {
 	struct ifnet		*sc_ifp;
 	struct ifnet		*sc_sync_if;
 	struct ip_moptions	sc_imo;
-	struct in_addr		sc_sync_peer;
+	struct sockaddr_storage	sc_sync_peer;
 	uint32_t		sc_flags;
 	uint8_t			sc_maxupdates;
-	struct ip		sc_template;
+	union inet_template     sc_template;
 	struct mtx		sc_mtx;
 
 	/* Queued data */
@@ -302,6 +309,12 @@ static void	pfsync_bulk_update(void *);
 static void	pfsync_bulk_fail(void *);
 
 static void	pfsync_detach_ifnet(struct ifnet *);
+
+static int pfsync_pfsyncreq_to_kstatus(struct pfsyncreq *,
+    struct pfsync_kstatus *);
+static int pfsync_kstatus_to_softc(struct pfsync_kstatus *,
+    struct pfsync_softc *);
+
 #ifdef IPSEC
 static void	pfsync_update_net_tdb(struct pfsync_tdb *);
 #endif
@@ -617,6 +630,7 @@ cleanup_state:	/* pf_state_insert() frees the state keys. */
 	return (error);
 }
 
+#ifdef INET
 static int
 pfsync_input(struct mbuf **mp, int *offp __unused, int proto __unused)
 {
@@ -716,6 +730,7 @@ done:
 	m_freem(m);
 	return (IPPROTO_DONE);
 }
+#endif
 
 static int
 pfsync_in_clr(struct mbuf *m, int offset, int count, int flags)
@@ -1308,6 +1323,7 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 	struct pfsync_softc *sc = ifp->if_softc;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct pfsyncreq pfsyncr;
+	size_t nvbuflen;
 	int error;
 	int c;
 
@@ -1346,18 +1362,56 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 			strlcpy(pfsyncr.pfsyncr_syncdev,
 			    sc->sc_sync_if->if_xname, IFNAMSIZ);
 		}
-		pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer;
+		pfsyncr.pfsyncr_syncpeer = ((struct sockaddr_in *)&sc->sc_sync_peer)->sin_addr;
 		pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
 		pfsyncr.pfsyncr_defer = sc->sc_flags;
 		PFSYNC_UNLOCK(sc);
 		return (copyout(&pfsyncr, ifr_data_get_ptr(ifr),
 		    sizeof(pfsyncr)));
 
+	case SIOCGETPFSYNCNV:
+	    {
+		nvlist_t *nvl_syncpeer;
+		nvlist_t *nvl = nvlist_create(0);
+
+		if (nvl == NULL)
+			return (ENOMEM);
+
+		if (sc->sc_sync_if)
+			nvlist_add_string(nvl, "syncdev", sc->sc_sync_if->if_xname);
+		nvlist_add_number(nvl, "maxupdates", sc->sc_maxupdates);
+		nvlist_add_number(nvl, "flags", sc->sc_flags);
+		if ((nvl_syncpeer = pfsync_sockaddr_to_syncpeer_nvlist(&sc->sc_sync_peer)) != NULL)
+			nvlist_add_nvlist(nvl, "syncpeer", nvl_syncpeer);
+
+		void *packed = NULL;
+		packed = nvlist_pack(nvl, &nvbuflen);
+		if (packed == NULL) {
+			free(packed, M_NVLIST);
+			nvlist_destroy(nvl);
+			return (ENOMEM);
+		}
+
+		if (nvbuflen > ifr->ifr_cap_nv.buf_length) {
+			ifr->ifr_cap_nv.length = nvbuflen;
+			ifr->ifr_cap_nv.buffer = NULL;
+			free(packed, M_NVLIST);
+			nvlist_destroy(nvl);
+			return (EFBIG);
+		}
+
+		ifr->ifr_cap_nv.length = nvbuflen;
+		error = copyout(packed, ifr->ifr_cap_nv.buffer, nvbuflen);
+
+		nvlist_destroy(nvl);
+		nvlist_destroy(nvl_syncpeer);
+		free(packed, M_NVLIST);
+		break;
+	    }
+
 	case SIOCSETPFSYNC:
 	    {
-		struct in_mfilter *imf = NULL;
-		struct ifnet *sifp;
-		struct ip *ip;
+		struct pfsync_kstatus status;
 
 		if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0)
 			return (error);
@@ -1365,101 +1419,44 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 		    sizeof(pfsyncr))))
 			return (error);
 
-		if (pfsyncr.pfsyncr_maxupdates > 255)
-			return (EINVAL);
+		memset((char *)&status, 0, sizeof(struct pfsync_kstatus));
+		pfsync_pfsyncreq_to_kstatus(&pfsyncr, &status);
 
-		if (pfsyncr.pfsyncr_syncdev[0] == 0)
-			sifp = NULL;
-		else if ((sifp = ifunit_ref(pfsyncr.pfsyncr_syncdev)) == NULL)
-			return (EINVAL);
+		error = pfsync_kstatus_to_softc(&status, sc);
+		return (error);
+	    }
+	case SIOCSETPFSYNCNV:
+	    {
+		struct pfsync_kstatus status;
+		void *data;
+		nvlist_t *nvl;
 
-		if (sifp != NULL && (
-		    pfsyncr.pfsyncr_syncpeer.s_addr == 0 ||
-		    pfsyncr.pfsyncr_syncpeer.s_addr ==
-		    htonl(INADDR_PFSYNC_GROUP)))
-			imf = ip_mfilter_alloc(M_WAITOK, 0, 0);
+		if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0)
+			return (error);
+		if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE)
+			return (EINVAL);
 
-		PFSYNC_LOCK(sc);
-		if (pfsyncr.pfsyncr_syncpeer.s_addr == 0)
-			sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP);
-		else
-			sc->sc_sync_peer.s_addr =
-			    pfsyncr.pfsyncr_syncpeer.s_addr;
-
-		sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
-		if (pfsyncr.pfsyncr_defer & PFSYNCF_DEFER) {
-			sc->sc_flags |= PFSYNCF_DEFER;
-			V_pfsync_defer_ptr = pfsync_defer;
-		} else {
-			sc->sc_flags &= ~PFSYNCF_DEFER;
-			V_pfsync_defer_ptr = NULL;
-		}
+		data = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK);
 
-		if (sifp == NULL) {
-			if (sc->sc_sync_if)
-				if_rele(sc->sc_sync_if);
-			sc->sc_sync_if = NULL;
-			pfsync_multicast_cleanup(sc);
-			PFSYNC_UNLOCK(sc);
-			break;
+		if ((error = copyin(ifr->ifr_cap_nv.buffer, data,
+		    ifr->ifr_cap_nv.length)) != 0) {
+			free(data, M_TEMP);
+			return (error);
 		}
 
-		for (c = 0; c < pfsync_buckets; c++) {
-			PFSYNC_BUCKET_LOCK(&sc->sc_buckets[c]);
-			if (sc->sc_buckets[c].b_len > PFSYNC_MINPKT &&
-			    (sifp->if_mtu < sc->sc_ifp->if_mtu ||
-			    (sc->sc_sync_if != NULL &&
-			    sifp->if_mtu < sc->sc_sync_if->if_mtu) ||
-			    sifp->if_mtu < MCLBYTES - sizeof(struct ip)))
-				pfsync_sendout(1, c);
-			PFSYNC_BUCKET_UNLOCK(&sc->sc_buckets[c]);
+		if ((nvl = nvlist_unpack(data, ifr->ifr_cap_nv.length, 0)) == NULL) {
+			free(data, M_TEMP);
+			return (EINVAL);
 		}
 
-		pfsync_multicast_cleanup(sc);
+		memset((char *)&status, 0, sizeof(struct pfsync_kstatus));
+		pfsync_nvstatus_to_kstatus(nvl, &status);
 
-		if (sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) {
-			error = pfsync_multicast_setup(sc, sifp, imf);
-			if (error) {
-				if_rele(sifp);
-				ip_mfilter_free(imf);
-				PFSYNC_UNLOCK(sc);
-				return (error);
-			}
-		}
-		if (sc->sc_sync_if)
-			if_rele(sc->sc_sync_if);
-		sc->sc_sync_if = sifp;
-
-		ip = &sc->sc_template;
-		bzero(ip, sizeof(*ip));
-		ip->ip_v = IPVERSION;
-		ip->ip_hl = sizeof(sc->sc_template) >> 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->sc_sync_peer.s_addr;
-
-		/* Request a full state table update. */
-		if ((sc->sc_flags & PFSYNCF_OK) && carp_demote_adj_p)
-			(*carp_demote_adj_p)(V_pfsync_carp_adj,
-			    "pfsync bulk start");
-		sc->sc_flags &= ~PFSYNCF_OK;
-		if (V_pf_status.debug >= PF_DEBUG_MISC)
-			printf("pfsync: requesting bulk update\n");
-		PFSYNC_UNLOCK(sc);
-		PFSYNC_BUCKET_LOCK(&sc->sc_buckets[0]);
-		pfsync_request_update(0, 0);
-		PFSYNC_BUCKET_UNLOCK(&sc->sc_buckets[0]);
-		PFSYNC_BLOCK(sc);
-		sc->sc_ureq_sent = time_uptime;
-		callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulk_fail,
-		    sc);
-		PFSYNC_BUNLOCK(sc);
+		nvlist_destroy(nvl);
+		free(data, M_TEMP);
 
-		break;
+		error = pfsync_kstatus_to_softc(&status, sc);
+		return (error);
 	    }
 	default:
 		return (ENOTTY);
@@ -1548,13 +1545,12 @@ pfsync_sendout(int schedswi, int c)
 	struct pfsync_softc *sc = V_pfsyncif;
 	struct ifnet *ifp = sc->sc_ifp;
 	struct mbuf *m;
-	struct ip *ip;
 	struct pfsync_header *ph;
 	struct pfsync_subheader *subh;
 	struct pf_kstate *st, *st_next;
 	struct pfsync_upd_req_item *ur;
 	struct pfsync_bucket *b = &sc->sc_buckets[c];
-	int offset;
+	int aflen, offset;
 	int q, count = 0;
 
 	KASSERT(sc != NULL, ("%s: null sc", __func__));
@@ -1577,12 +1573,25 @@ pfsync_sendout(int schedswi, int c)
 	m->m_len = m->m_pkthdr.len = b->b_len;
 
 	/* build the ip header */
-	ip = (struct ip *)m->m_data;
-	bcopy(&sc->sc_template, ip, sizeof(*ip));
-	offset = sizeof(*ip);
+	switch (sc->sc_sync_peer.ss_family) {
+#ifdef INET
+	case AF_INET:
+	    {
+		struct ip *ip;
+
+		ip = mtod(m, struct ip *);
+		bcopy(&sc->sc_template.ipv4, ip, sizeof(*ip));
+		aflen = offset = sizeof(*ip);
+
+		ip->ip_len = htons(m->m_pkthdr.len);
+		ip_fillid(ip);
+		break;
+	    }
+#endif
+	default:
+		return;
+	}
 
-	ip->ip_len = htons(m->m_pkthdr.len);
-	ip_fillid(ip);
 
 	/* build the pfsync header */
 	ph = (struct pfsync_header *)(m->m_data + offset);
@@ -1590,7 +1599,7 @@ pfsync_sendout(int schedswi, int c)
 	offset += sizeof(*ph);
 
 	ph->version = PFSYNC_VERSION;
-	ph->len = htons(b->b_len - sizeof(*ip));
+	ph->len = htons(b->b_len - aflen);
 	bcopy(V_pf_status.pf_chksum, ph->pfcksum, PF_MD5_DIGEST_LENGTH);
 
 	/* walk the queues */
@@ -1663,10 +1672,10 @@ pfsync_sendout(int schedswi, int c)
 
 	/* we're done, let's put it on the wire */
 	if (ifp->if_bpf) {
-		m->m_data += sizeof(*ip);
-		m->m_len = m->m_pkthdr.len = b->b_len - sizeof(*ip);
+		m->m_data += aflen;
+		m->m_len = m->m_pkthdr.len = b->b_len - aflen;
 		BPF_MTAP(ifp, m);
-		m->m_data -= sizeof(*ip);
+		m->m_data -= aflen;
 		m->m_len = m->m_pkthdr.len = b->b_len;
 	}
 
@@ -1819,7 +1828,13 @@ pfsync_defer_tmo(void *arg)
 		free(pd, M_PFSYNC);
 	PFSYNC_BUCKET_UNLOCK(b);
 
-	ip_output(m, NULL, NULL, 0, NULL, NULL);
+	switch (sc->sc_sync_peer.ss_family) {
+#ifdef INET
+	case AF_INET:
+		ip_output(m, NULL, NULL, 0, NULL, NULL);
+		break;
+#endif
+	}
 
 	pf_release_state(st);
 
@@ -2309,7 +2324,7 @@ pfsyncintr(void *arg)
 	struct pfsync_softc *sc = arg;
 	struct pfsync_bucket *b;
 	struct mbuf *m, *n;
-	int c;
+	int c, error;
 
 	NET_EPOCH_ENTER(et);
 	CURVNET_SET(sc->sc_ifp->if_vnet);
@@ -2334,10 +2349,21 @@ pfsyncintr(void *arg)
 			 * own pfsync packet based on M_SKIP_FIREWALL
 			 * flag. This is XXX.
 			 */
-			if (m->m_flags & M_SKIP_FIREWALL)
-				ip_output(m, NULL, NULL, 0, NULL, NULL);
-			else if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo,
-			    NULL) == 0)
+			switch (sc->sc_sync_peer.ss_family) {
+#ifdef INET
+			case AF_INET:
+				if (m->m_flags & M_SKIP_FIREWALL) {
+					error = ip_output(m, NULL, NULL, 0,
+					    NULL, NULL);
+				} else {
+					error = ip_output(m, NULL, NULL,
+					    IP_RAWOUTPUT, &sc->sc_imo, NULL);
+				}
+				break;
+#endif
+			}
+
+			if (error == 0)
 				V_pfsyncstats.pfsyncs_opackets++;
 			else
 				V_pfsyncstats.pfsyncs_oerrors++;
@@ -2357,17 +2383,24 @@ pfsync_multicast_setup(struct pfsync_softc *sc, struct ifnet *ifp,
 	if (!(ifp->if_flags & IFF_MULTICAST))
 		return (EADDRNOTAVAIL);
 
-	imo->imo_multicast_vif = -1;
-
-	if ((error = in_joingroup(ifp, &sc->sc_sync_peer, NULL,
-	    &imf->imf_inm)) != 0)
-		return (error);
+	switch (sc->sc_sync_peer.ss_family) {
+#ifdef INET
+	case AF_INET:
+	    {
+		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)
+			return (error);
 
-	ip_mfilter_init(&imo->imo_head);
-	ip_mfilter_insert(&imo->imo_head, imf);
-	imo->imo_multicast_ifp = ifp;
-	imo->imo_multicast_ttl = PFSYNC_DFLTTL;
-	imo->imo_multicast_loop = 0;
+		ip_mfilter_insert(&imo->imo_head, imf);
+		imo->imo_multicast_ifp = ifp;
+		imo->imo_multicast_ttl = PFSYNC_DFLTTL;
+		imo->imo_multicast_loop = 0;
+		break;
+	    }
+#endif
+	}
 
 	return (0);
 }
@@ -2409,6 +2442,135 @@ pfsync_detach_ifnet(struct ifnet *ifp)
 	PFSYNC_UNLOCK(sc);
 }
 
+static int
+pfsync_pfsyncreq_to_kstatus(struct pfsyncreq *pfsyncr, struct pfsync_kstatus *status)
+{
+	struct sockaddr_storage sa;
+	status->maxupdates = pfsyncr->pfsyncr_maxupdates;
+	status->flags = pfsyncr->pfsyncr_defer;
+
+	strlcpy(status->syncdev, pfsyncr->pfsyncr_syncdev, IFNAMSIZ);
+
+	memset(&sa, 0, sizeof(sa));
+	if (pfsyncr->pfsyncr_syncpeer.s_addr != 0) {
+		struct sockaddr_in *in = (struct sockaddr_in *)&sa;
+		in->sin_family = AF_INET;
+		in->sin_len = sizeof(*in);
+		in->sin_addr.s_addr = pfsyncr->pfsyncr_syncpeer.s_addr;
+	}
+	status->syncpeer = sa;
+
+	return 0;
+}
+
+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;
+	int error;
+	int c;
+
+	if ((status->maxupdates < 0) || (status->maxupdates > 255))
+		return (EINVAL);
+
+	if (status->syncdev[0] == '\0')
+		sifp = NULL;
+	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);
+
+	PFSYNC_LOCK(sc);
+	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);
*** 285 LINES SKIPPED ***