From 813c5b75e68073a724fdc032b24a12baeab8f6d9 Mon Sep 17 00:00:00 2001 From: Luiz Amaral Date: Wed, 9 Nov 2022 12:40:43 +0100 Subject: [PATCH] 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 | 324 +++++++++++++++++++++------- sys/conf/files | 1 + sys/modules/pfsync/Makefile | 2 +- sys/net/if_pfsync.h | 15 ++ sys/netpfil/pf/if_pfsync.c | 412 +++++++++++++++++++++++++----------- sys/netpfil/pf/pfsync_nv.c | 150 +++++++++++++ sys/netpfil/pf/pfsync_nv.h | 42 ++++ 7 files changed, 742 insertions(+), 204 deletions(-) create mode 100644 sys/netpfil/pf/pfsync_nv.c create mode 100644 sys/netpfil/pf/pfsync_nv.h 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 +#include #include +#include #include #include @@ -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); - bzero((char *)&preq, sizeof(struct pfsyncreq)); - ifr.ifr_data = (caddr_t)&preq; + if (strlen(val) > IFNAMSIZ) + errx(1, "interface name %s is too long", val); - if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1) - err(1, "SIOCGETPFSYNC"); + if (pfsync_do_ioctl(s, SIOCGETPFSYNCNV, &nvl) == -1) + err(1, "SIOCGETPFSYNCNV"); - strlcpy(preq.pfsyncr_syncdev, val, sizeof(preq.pfsyncr_syncdev)); + if (nvlist_exists_string(nvl, "syncdev")) + nvlist_free_string(nvl, "syncdev"); - if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) - err(1, "SIOCSETPFSYNC"); + nvlist_add_string(nvl, "syncdev", val); + + 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; + 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"); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; /*dummy*/ - - 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; - preq.pfsyncr_syncpeer.s_addr = ((struct sockaddr_in *) - peerres->ai_addr)->sin_addr.s_addr; + if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) + errx(1, "syncpeer address cannot be multicast"); - if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) - err(1, "SIOCSETPFSYNC"); + 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)); + + if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1) + err(1, "SIOCSETPFSYNCNV"); + + 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); - 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, "flags"); + nvlist_add_number(nvl, "flags", d ? PFSYNCF_DEFER : 0); - preq.pfsyncr_defer = d ? PFSYNCF_DEFER : 0; - if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1) - err(1, "SIOCSETPFSYNC"); + if (pfsync_do_ioctl(s, SIOCSETPFSYNCNV, &nvl) == -1) + err(1, "SIOCSETPFSYNCNV"); + + 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; - - 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); } + + 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 (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 #include #include +#include #include #include #include @@ -101,13 +102,19 @@ __FBSDID("$FreeBSD$"); #include #include -#define PFSYNC_MINPKT ( \ - sizeof(struct ip) + \ - sizeof(struct pfsync_header) + \ - sizeof(struct pfsync_subheader) ) +#include struct pfsync_bucket; +union inet_template { + struct ip ipv4; +}; + +#define PFSYNC_MINPKT ( \ + sizeof(union inet_template) + \ + sizeof(struct pfsync_header) + \ + sizeof(struct pfsync_subheader) ) + 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 @@ pfsync_state_import(struct pfsync_state *sp, int flags) return (error); } +#ifdef INET static int pfsync_input(struct mbuf **mp, int *offp __unused, int proto __unused) { @@ -716,6 +730,7 @@ pfsync_input(struct mbuf **mp, int *offp __unused, int proto __unused) 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) + memset((char *)&status, 0, sizeof(struct pfsync_kstatus)); + pfsync_pfsyncreq_to_kstatus(&pfsyncr, &status); + + error = pfsync_kstatus_to_softc(&status, sc); + return (error); + } + case SIOCSETPFSYNCNV: + { + struct pfsync_kstatus status; + void *data; + nvlist_t *nvl; + + if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0) + return (error); + if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE) return (EINVAL); - if (pfsyncr.pfsyncr_syncdev[0] == 0) - sifp = NULL; - else if ((sifp = ifunit_ref(pfsyncr.pfsyncr_syncdev)) == NULL) + data = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK); + + if ((error = copyin(ifr->ifr_cap_nv.buffer, data, + ifr->ifr_cap_nv.length)) != 0) { + free(data, M_TEMP); + return (error); + } + + if ((nvl = nvlist_unpack(data, ifr->ifr_cap_nv.length, 0)) == NULL) { + free(data, M_TEMP); return (EINVAL); - - 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); - - 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; } - 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; - } + memset((char *)&status, 0, sizeof(struct pfsync_kstatus)); + pfsync_nvstatus_to_kstatus(nvl, &status); - 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]); - } + nvlist_destroy(nvl); + free(data, M_TEMP); - pfsync_multicast_cleanup(sc); - - 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); - - 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; + 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); - if ((error = in_joingroup(ifp, &sc->sc_sync_peer, 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); + } else { + sc_sin->sin_addr.s_addr = status_sin->sin_addr.s_addr; + } + + sc->sc_maxupdates = status->maxupdates; + if (status->flags & PFSYNCF_DEFER) { + sc->sc_flags |= PFSYNCF_DEFER; + V_pfsync_defer_ptr = pfsync_defer; + } else { + sc->sc_flags &= ~PFSYNCF_DEFER; + V_pfsync_defer_ptr = NULL; + } + + 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); + return (0); + } + + 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]); + } + + pfsync_multicast_cleanup(sc); + + if (sc_sin->sin_addr.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.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; + + /* 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); + return (0); +} + static void pfsync_pointers_init(void) { diff --git a/sys/netpfil/pf/pfsync_nv.c b/sys/netpfil/pf/pfsync_nv.c new file mode 100644 index 000000000000..a6a27c9461fe --- /dev/null +++ b/sys/netpfil/pf/pfsync_nv.c @@ -0,0 +1,150 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 InnoGames GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include +#include + +#include + +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); + in6->sin6_family = af; + if (len != sizeof(*in6)) + return (EINVAL); + + memcpy(in6, addr, sizeof(*in6)); + break; + } +#endif + default: + return (EINVAL); + } + + return (0); +} + +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: + return NULL; + } + + return (nvl); +} + +int +pfsync_nvstatus_to_kstatus(const nvlist_t *nvl, struct pfsync_kstatus *status) +{ + struct sockaddr_storage addr; + int error; + + if (!nvlist_exists_number(nvl, "maxupdates")) + return (EINVAL); + if (!nvlist_exists_number(nvl, "flags")) + return (EINVAL); + + status->maxupdates = nvlist_get_number(nvl, "maxupdates"); + status->flags = nvlist_get_number(nvl, "flags"); + + if (nvlist_exists_string(nvl, "syncdev")) + strlcpy(status->syncdev, nvlist_get_string(nvl, "syncdev"), + IFNAMSIZ); + + if (nvlist_exists_nvlist(nvl, "syncpeer")) { + memset(&addr, 0, sizeof(addr)); + if ((error = pfsync_syncpeer_nvlist_to_sockaddr(nvlist_get_nvlist(nvl, "syncpeer"), &addr)) != 0) + return (error); + + status->syncpeer = addr; + } else { + memset(&status->syncpeer, 0, sizeof(status->syncpeer)); + } + + return (0); +} diff --git a/sys/netpfil/pf/pfsync_nv.h b/sys/netpfil/pf/pfsync_nv.h new file mode 100644 index 000000000000..3464e363e3a7 --- /dev/null +++ b/sys/netpfil/pf/pfsync_nv.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 InnoGames GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _PFSYNC_NV_H +#define _PFSYNC_NV_H + +#include +#include + +#include +#include +#include + +int pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *, struct sockaddr_storage *); +nvlist_t *pfsync_sockaddr_to_syncpeer_nvlist(struct sockaddr_storage *); +int pfsync_nvstatus_to_kstatus(const nvlist_t *, struct pfsync_kstatus *); +#endif /* !_PFSYNC_NV_H */