diff options
Diffstat (limited to 'sys/contrib/pf/net/if_pfsync.c')
-rw-r--r-- | sys/contrib/pf/net/if_pfsync.c | 1046 |
1 files changed, 647 insertions, 399 deletions
diff --git a/sys/contrib/pf/net/if_pfsync.c b/sys/contrib/pf/net/if_pfsync.c index 1106339..0f5c344 100644 --- a/sys/contrib/pf/net/if_pfsync.c +++ b/sys/contrib/pf/net/if_pfsync.c @@ -1,4 +1,5 @@ -/* $OpenBSD: if_pfsync.c,v 1.73 2006/11/16 13:13:38 henning Exp $ */ +/* $FreeBSD$ */ +/* $OpenBSD: if_pfsync.c,v 1.46 2005/02/20 15:58:38 mcbride Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff @@ -26,27 +27,67 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#ifdef __FreeBSD__ +#include "opt_inet.h" +#include "opt_inet6.h" +#endif + +#ifndef __FreeBSD__ +#include "bpfilter.h" +#include "pfsync.h" +#elif __FreeBSD__ >= 5 +#include "opt_bpf.h" +#include "opt_pf.h" + +#ifdef DEV_BPF +#define NBPFILTER DEV_BPF +#else +#define NBPFILTER 0 +#endif + +#ifdef DEV_PFSYNC +#define NPFSYNC DEV_PFSYNC +#else +#define NPFSYNC 0 +#endif + +#endif #include <sys/param.h> +#ifdef __FreeBSD__ +#include <sys/priv.h> +#endif #include <sys/proc.h> #include <sys/systm.h> #include <sys/time.h> #include <sys/mbuf.h> #include <sys/socket.h> +#include <sys/kernel.h> +#ifdef __FreeBSD__ +#include <sys/endian.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/sockio.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> +#else #include <sys/ioctl.h> #include <sys/timeout.h> -#include <sys/kernel.h> +#endif #include <net/if.h> +#if defined(__FreeBSD__) +#include <net/if_clone.h> +#endif #include <net/if_types.h> #include <net/route.h> #include <net/bpf.h> -#include <netinet/in.h> -#include <netinet/if_ether.h> #include <netinet/tcp.h> #include <netinet/tcp_seq.h> #ifdef INET +#include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> #include <netinet/ip.h> @@ -54,19 +95,32 @@ #endif #ifdef INET6 +#ifndef INET +#include <netinet/in.h> +#endif #include <netinet6/nd6.h> #endif /* INET6 */ +#ifdef __FreeBSD__ +#include "opt_carp.h" +#ifdef DEV_CARP +#define NCARP 1 +#else +#define NCARP 0 +#endif +#else #include "carp.h" +#endif #if NCARP > 0 -#include <netinet/ip_carp.h> +extern int carp_suppress_preempt; #endif #include <net/pfvar.h> #include <net/if_pfsync.h> -#include "bpfilter.h" -#include "pfsync.h" +#ifdef __FreeBSD__ +#define PFSYNCNAME "pfsync" +#endif #define PFSYNC_MINMTU \ (sizeof(struct pfsync_header) + sizeof(struct pf_state)) @@ -78,17 +132,32 @@ int pfsyncdebug; #define DPRINTF(x) #endif -struct pfsync_softc *pfsyncif = NULL; -struct pfsyncstats pfsyncstats; +#ifndef __FreeBSD__ +struct pfsync_softc pfsyncif; +#endif +struct pfsyncstats pfsyncstats; +#ifdef __FreeBSD__ +SYSCTL_DECL(_net_inet_pfsync); +SYSCTL_STRUCT(_net_inet_pfsync, 0, stats, CTLFLAG_RW, + &pfsyncstats, pfsyncstats, + "PFSYNC statistics (struct pfsyncstats, net/if_pfsync.h)"); +/* + * Locking notes: + * Whenever we really touch/look at the state table we have to hold the + * PF_LOCK. Functions that do just the interface handling, grab the per + * softc lock instead. + * + */ + +static void pfsync_clone_destroy(struct ifnet *); +static int pfsync_clone_create(struct if_clone *, int, caddr_t params); +static void pfsync_senddef(void *); +#else void pfsyncattach(int); -int pfsync_clone_create(struct if_clone *, int); -int pfsync_clone_destroy(struct ifnet *); +#endif void pfsync_setmtu(struct pfsync_softc *, int); -int pfsync_alloc_scrub_memory(struct pfsync_state_peer *, - struct pf_state_peer *); -int pfsync_insert_net_state(struct pfsync_state *, u_int8_t); -void pfsync_update_net_tdb(struct pfsync_tdb *); +int pfsync_insert_net_state(struct pfsync_state *); int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); int pfsyncioctl(struct ifnet *, u_long, caddr_t); @@ -97,89 +166,160 @@ void pfsyncstart(struct ifnet *); struct mbuf *pfsync_get_mbuf(struct pfsync_softc *, u_int8_t, void **); int pfsync_request_update(struct pfsync_state_upd *, struct in_addr *); int pfsync_sendout(struct pfsync_softc *); -int pfsync_tdb_sendout(struct pfsync_softc *); -int pfsync_sendout_mbuf(struct pfsync_softc *, struct mbuf *); void pfsync_timeout(void *); -void pfsync_tdb_timeout(void *); void pfsync_send_bus(struct pfsync_softc *, u_int8_t); void pfsync_bulk_update(void *); void pfsync_bulkfail(void *); +#ifdef __FreeBSD__ +static void pfsync_ifdetach(void *, struct ifnet *); +#endif int pfsync_sync_ok; +#ifndef __FreeBSD__ extern int ifqmaxlen; +extern struct timeval time; +extern struct timeval mono_time; +extern int hz; +#endif -struct if_clone pfsync_cloner = - IF_CLONE_INITIALIZER("pfsync", pfsync_clone_create, pfsync_clone_destroy); +#ifdef __FreeBSD__ +static MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface"); +static LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list; +#define SCP2IFP(sc) ((sc)->sc_ifp) +IFC_SIMPLE_DECLARE(pfsync, 1); -void -pfsyncattach(int npfsync) +static void +pfsync_clone_destroy(struct ifnet *ifp) { - if_clone_attach(&pfsync_cloner); + struct pfsync_softc *sc; + + sc = ifp->if_softc; +#ifdef __FreeBSD__ + EVENTHANDLER_DEREGISTER(ifnet_departure_event, sc->sc_detachtag); +#endif + callout_stop(&sc->sc_tmo); + callout_stop(&sc->sc_bulk_tmo); + callout_stop(&sc->sc_bulkfail_tmo); + + callout_stop(&sc->sc_send_tmo); + +#if NBPFILTER > 0 + bpfdetach(ifp); +#endif + if_detach(ifp); + if_free(ifp); + LIST_REMOVE(sc, sc_next); + free(sc->sc_imo.imo_membership, M_PFSYNC); + free(sc, M_PFSYNC); } -int + +static int +#ifdef __FreeBSD__ +pfsync_clone_create(struct if_clone *ifc, int unit, caddr_t params) +#else pfsync_clone_create(struct if_clone *ifc, int unit) +#endif { + struct pfsync_softc *sc; struct ifnet *ifp; - if (unit != 0) - return (EINVAL); + MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC, + M_WAITOK|M_ZERO); + ifp = sc->sc_ifp = if_alloc(IFT_PFSYNC); + if (ifp == NULL) { + free(sc, M_PFSYNC); + return (ENOSPC); + } + +#ifdef __FreeBSD__ + sc->sc_detachtag = EVENTHANDLER_REGISTER(ifnet_departure_event, + pfsync_ifdetach, sc, EVENTHANDLER_PRI_ANY); + if (sc->sc_detachtag == NULL) { + if_free(ifp); + free(sc, M_PFSYNC); + return (ENOSPC); + } +#endif pfsync_sync_ok = 1; - if ((pfsyncif = malloc(sizeof(*pfsyncif), M_DEVBUF, M_NOWAIT)) == NULL) - return (ENOMEM); - bzero(pfsyncif, sizeof(*pfsyncif)); - pfsyncif->sc_mbuf = NULL; - pfsyncif->sc_mbuf_net = NULL; - pfsyncif->sc_mbuf_tdb = NULL; - pfsyncif->sc_statep.s = NULL; - pfsyncif->sc_statep_net.s = NULL; - pfsyncif->sc_statep_tdb.t = NULL; - pfsyncif->sc_maxupdates = 128; - pfsyncif->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP; - pfsyncif->sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP; - pfsyncif->sc_ureq_received = 0; - pfsyncif->sc_ureq_sent = 0; - pfsyncif->sc_bulk_send_next = NULL; - pfsyncif->sc_bulk_terminator = NULL; - ifp = &pfsyncif->sc_if; - snprintf(ifp->if_xname, sizeof ifp->if_xname, "pfsync%d", unit); - ifp->if_softc = pfsyncif; + sc->sc_mbuf = NULL; + sc->sc_mbuf_net = NULL; + sc->sc_statep.s = NULL; + sc->sc_statep_net.s = NULL; + sc->sc_maxupdates = 128; + sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP); + sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP); + sc->sc_ureq_received = 0; + sc->sc_ureq_sent = 0; + sc->sc_imo.imo_membership = (struct in_multi **)malloc( + (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_PFSYNC, + M_WAITOK); + sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS; + + ifp = SCP2IFP(sc); + if_initname(ifp, ifc->ifc_name, unit); ifp->if_ioctl = pfsyncioctl; ifp->if_output = pfsyncoutput; ifp->if_start = pfsyncstart; - ifp->if_type = IFT_PFSYNC; ifp->if_snd.ifq_maxlen = ifqmaxlen; ifp->if_hdrlen = PFSYNC_HDRLEN; - pfsync_setmtu(pfsyncif, ETHERMTU); - timeout_set(&pfsyncif->sc_tmo, pfsync_timeout, pfsyncif); - timeout_set(&pfsyncif->sc_tdb_tmo, pfsync_tdb_timeout, pfsyncif); - timeout_set(&pfsyncif->sc_bulk_tmo, pfsync_bulk_update, pfsyncif); - timeout_set(&pfsyncif->sc_bulkfail_tmo, pfsync_bulkfail, pfsyncif); + ifp->if_baudrate = IF_Mbps(100); + ifp->if_softc = sc; + pfsync_setmtu(sc, MCLBYTES); + callout_init(&sc->sc_tmo, NET_CALLOUT_MPSAFE); + callout_init(&sc->sc_bulk_tmo, NET_CALLOUT_MPSAFE); + callout_init(&sc->sc_bulkfail_tmo, NET_CALLOUT_MPSAFE); + callout_init(&sc->sc_send_tmo, NET_CALLOUT_MPSAFE); + sc->sc_ifq.ifq_maxlen = ifqmaxlen; + mtx_init(&sc->sc_ifq.ifq_mtx, ifp->if_xname, "pfsync send queue", + MTX_DEF); if_attach(ifp); - if_alloc_sadl(ifp); - -#if NCARP > 0 - if_addgroup(ifp, "carp"); -#endif + LIST_INSERT_HEAD(&pfsync_list, sc, sc_next); #if NBPFILTER > 0 - bpfattach(&pfsyncif->sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); + bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN); #endif return (0); } - -int -pfsync_clone_destroy(struct ifnet *ifp) +#else /* !__FreeBSD__ */ +void +pfsyncattach(int npfsync) { + struct ifnet *ifp; + + pfsync_sync_ok = 1; + bzero(&pfsyncif, sizeof(pfsyncif)); + pfsyncif.sc_mbuf = NULL; + pfsyncif.sc_mbuf_net = NULL; + pfsyncif.sc_statep.s = NULL; + pfsyncif.sc_statep_net.s = NULL; + pfsyncif.sc_maxupdates = 128; + pfsyncif.sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP; + pfsyncif.sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP; + pfsyncif.sc_ureq_received = 0; + pfsyncif.sc_ureq_sent = 0; + ifp = &pfsyncif.sc_if; + strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname); + ifp->if_softc = &pfsyncif; + ifp->if_ioctl = pfsyncioctl; + ifp->if_output = pfsyncoutput; + ifp->if_start = pfsyncstart; + ifp->if_type = IFT_PFSYNC; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = PFSYNC_HDRLEN; + pfsync_setmtu(&pfsyncif, MCLBYTES); + timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif); + timeout_set(&pfsyncif.sc_bulk_tmo, pfsync_bulk_update, &pfsyncif); + timeout_set(&pfsyncif.sc_bulkfail_tmo, pfsync_bulkfail, &pfsyncif); + if_attach(ifp); + if_alloc_sadl(ifp); + #if NBPFILTER > 0 - bpfdetach(ifp); + bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); #endif - if_detach(ifp); - free(pfsyncif, M_DEVBUF); - pfsyncif = NULL; - return (0); } +#endif /* * Start output on the pfsync interface. @@ -187,11 +327,17 @@ pfsync_clone_destroy(struct ifnet *ifp) void pfsyncstart(struct ifnet *ifp) { +#ifdef __FreeBSD__ + IF_LOCK(&ifp->if_snd); + _IF_DROP(&ifp->if_snd); + _IF_DRAIN(&ifp->if_snd); + IF_UNLOCK(&ifp->if_snd); +#else struct mbuf *m; int s; for (;;) { - s = splnet(); + s = splimp(); IF_DROP(&ifp->if_snd); IF_DEQUEUE(&ifp->if_snd, m); splx(s); @@ -201,36 +347,26 @@ pfsyncstart(struct ifnet *ifp) else m_freem(m); } +#endif } int -pfsync_alloc_scrub_memory(struct pfsync_state_peer *s, - struct pf_state_peer *d) -{ - if (s->scrub.scrub_flag && d->scrub == NULL) { - d->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT); - if (d->scrub == NULL) - return (ENOMEM); - bzero(d->scrub, sizeof(*d->scrub)); - } - - return (0); -} - -int -pfsync_insert_net_state(struct pfsync_state *sp, u_int8_t chksum_flag) +pfsync_insert_net_state(struct pfsync_state *sp) { struct pf_state *st = NULL; struct pf_rule *r = NULL; struct pfi_kif *kif; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) { printf("pfsync_insert_net_state: invalid creator id:" " %08x\n", ntohl(sp->creatorid)); return (EINVAL); } - kif = pfi_kif_get(sp->ifname); + kif = pfi_lookup_create(sp->ifname); if (kif == NULL) { if (pf_status.debug >= PF_DEBUG_MISC) printf("pfsync_insert_net_state: " @@ -240,33 +376,19 @@ pfsync_insert_net_state(struct pfsync_state *sp, u_int8_t chksum_flag) } /* - * If the ruleset checksums match, it's safe to associate the state - * with the rule of that number. + * Just use the default rule until we have infrastructure to find the + * best matching rule. */ - if (sp->rule != htonl(-1) && sp->anchor == htonl(-1) && chksum_flag) - r = pf_main_ruleset.rules[ - PF_RULESET_FILTER].active.ptr_array[ntohl(sp->rule)]; - else - r = &pf_default_rule; + r = &pf_default_rule; if (!r->max_states || r->states < r->max_states) st = pool_get(&pf_state_pl, PR_NOWAIT); if (st == NULL) { - pfi_kif_unref(kif, PFI_KIF_REF_NONE); + pfi_maybe_destroy(kif); return (ENOMEM); } bzero(st, sizeof(*st)); - /* allocate memory for scrub info */ - if (pfsync_alloc_scrub_memory(&sp->src, &st->src) || - pfsync_alloc_scrub_memory(&sp->dst, &st->dst)) { - pfi_kif_unref(kif, PFI_KIF_REF_NONE); - if (st->src.scrub) - pool_put(&pf_state_scrub_pl, st->src.scrub); - pool_put(&pf_state_pl, st); - return (ENOMEM); - } - st->rule.ptr = r; /* XXX get pointers to nat_rule and anchor */ @@ -296,14 +418,11 @@ pfsync_insert_net_state(struct pfsync_state *sp, u_int8_t chksum_flag) st->creatorid = sp->creatorid; st->sync_flags = PFSTATE_FROMSYNC; + if (pf_insert_state(kif, st)) { - pfi_kif_unref(kif, PFI_KIF_REF_NONE); + pfi_maybe_destroy(kif); /* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */ r->states--; - if (st->dst.scrub) - pool_put(&pf_state_scrub_pl, st->dst.scrub); - if (st->src.scrub) - pool_put(&pf_state_scrub_pl, st->src.scrub); pool_put(&pf_state_pl, st); return (EINVAL); } @@ -312,29 +431,34 @@ pfsync_insert_net_state(struct pfsync_state *sp, u_int8_t chksum_flag) } void +#ifdef __FreeBSD__ +pfsync_input(struct mbuf *m, __unused int off) +#else pfsync_input(struct mbuf *m, ...) +#endif { struct ip *ip = mtod(m, struct ip *); struct pfsync_header *ph; - struct pfsync_softc *sc = pfsyncif; - struct pf_state *st; - struct pf_state_cmp key; +#ifdef __FreeBSD__ + struct pfsync_softc *sc = LIST_FIRST(&pfsync_list); +#else + struct pfsync_softc *sc = &pfsyncif; +#endif + struct pf_state *st, key; struct pfsync_state *sp; struct pfsync_state_upd *up; struct pfsync_state_del *dp; struct pfsync_state_clr *cp; struct pfsync_state_upd_req *rup; struct pfsync_state_bus *bus; - struct pfsync_tdb *pt; struct in_addr src; struct mbuf *mp; int iplen, action, error, i, s, count, offp, sfail, stale = 0; - u_int8_t chksum_flag = 0; pfsyncstats.pfsyncs_ipackets++; /* verify that we have a sync interface configured */ - if (!sc || !sc->sc_sync_ifp || !pf_status.running) + if (!sc->sc_sync_ifp || !pf_status.running) /* XXX PF_LOCK? */ goto done; /* verify that the packet came in on the right interface */ @@ -383,9 +507,6 @@ pfsync_input(struct mbuf *m, ...) /* Cheaper to grab this now than having to mess with mbufs later */ src = ip->ip_src; - if (!bcmp(&ph->pf_chksum, &pf_status.pf_chksum, PF_MD5_DIGEST_LENGTH)) - chksum_flag++; - switch (action) { case PFSYNC_ACT_CLR: { struct pf_state *nexts; @@ -400,30 +521,43 @@ pfsync_input(struct mbuf *m, ...) creatorid = cp->creatorid; s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif if (cp->ifname[0] == '\0') { for (st = RB_MIN(pf_state_tree_id, &tree_id); st; st = nexts) { - nexts = RB_NEXT(pf_state_tree_id, &tree_id, st); + nexts = RB_NEXT(pf_state_tree_id, &tree_id, st); if (st->creatorid == creatorid) { - st->sync_flags |= PFSTATE_FROMSYNC; - pf_unlink_state(st); + st->timeout = PFTM_PURGE; + pf_purge_expired_state(st); } } } else { - if ((kif = pfi_kif_get(cp->ifname)) == NULL) { + kif = pfi_lookup_if(cp->ifname); + if (kif == NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync_input: PFSYNC_ACT_CLR " + "bad interface: %s\n", cp->ifname); splx(s); - return; +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif + goto done; } for (st = RB_MIN(pf_state_tree_lan_ext, &kif->pfik_lan_ext); st; st = nexts) { nexts = RB_NEXT(pf_state_tree_lan_ext, &kif->pfik_lan_ext, st); if (st->creatorid == creatorid) { - st->sync_flags |= PFSTATE_FROMSYNC; - pf_unlink_state(st); + st->timeout = PFTM_PURGE; + pf_purge_expired_state(st); } } } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; @@ -436,6 +570,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); i < count; i++, sp++) { /* check for invalid values */ @@ -451,15 +588,20 @@ pfsync_input(struct mbuf *m, ...) continue; } - if ((error = pfsync_insert_net_state(sp, - chksum_flag))) { + if ((error = pfsync_insert_net_state(sp))) { if (error == ENOMEM) { splx(s); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif goto done; } continue; } } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; case PFSYNC_ACT_UPD: @@ -470,6 +612,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); i < count; i++, sp++) { int flags = PFSYNC_FLAG_STALE; @@ -491,7 +636,7 @@ pfsync_input(struct mbuf *m, ...) st = pf_find_state_byid(&key); if (st == NULL) { /* insert the update */ - if (pfsync_insert_net_state(sp, chksum_flag)) + if (pfsync_insert_net_state(sp)) pfsyncstats.pfsyncs_badstate++; continue; } @@ -530,7 +675,7 @@ pfsync_input(struct mbuf *m, ...) */ if (st->src.state > sp->src.state) sfail = 5; - else if (st->dst.state > sp->dst.state) + else if ( st->dst.state > sp->dst.state) sfail = 6; } if (sfail) { @@ -540,7 +685,11 @@ pfsync_input(struct mbuf *m, ...) "creatorid: %08x\n", (sfail < 7 ? "ignoring" : "partial"), sfail, +#ifdef __FreeBSD__ + (unsigned long long)be64toh(st->id), +#else betoh64(st->id), +#endif ntohl(st->creatorid)); pfsyncstats.pfsyncs_badstate++; @@ -555,7 +704,6 @@ pfsync_input(struct mbuf *m, ...) } continue; } - pfsync_alloc_scrub_memory(&sp->dst, &st->dst); pf_state_peer_ntoh(&sp->src, &st->src); pf_state_peer_ntoh(&sp->dst, &st->dst); st->expire = ntohl(sp->expire) + time_second; @@ -563,6 +711,9 @@ pfsync_input(struct mbuf *m, ...) } if (stale && sc->sc_mbuf != NULL) pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; /* @@ -577,6 +728,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp); i < count; i++, sp++) { bcopy(sp->id, &key.id, sizeof(key.id)); @@ -587,9 +741,13 @@ pfsync_input(struct mbuf *m, ...) pfsyncstats.pfsyncs_badstate++; continue; } + st->timeout = PFTM_PURGE; st->sync_flags |= PFSTATE_FROMSYNC; - pf_unlink_state(st); + pf_purge_expired_state(st); } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; case PFSYNC_ACT_UPD_C: { @@ -602,6 +760,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp); i < count; i++, up++) { /* check for invalid values */ @@ -665,7 +826,11 @@ pfsync_input(struct mbuf *m, ...) printf("pfsync: ignoring stale update " "(%d) id: %016llx " "creatorid: %08x\n", sfail, +#ifdef __FreeBSD__ + (unsigned long long)be64toh(st->id), +#else betoh64(st->id), +#endif ntohl(st->creatorid)); pfsyncstats.pfsyncs_badstate++; @@ -681,7 +846,6 @@ pfsync_input(struct mbuf *m, ...) PFSYNC_FLAG_STALE); continue; } - pfsync_alloc_scrub_memory(&up->dst, &st->dst); pf_state_peer_ntoh(&up->src, &st->src); pf_state_peer_ntoh(&up->dst, &st->dst); st->expire = ntohl(up->expire) + time_second; @@ -689,6 +853,9 @@ pfsync_input(struct mbuf *m, ...) } if ((update_requested || stale) && sc->sc_mbuf) pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; } @@ -700,6 +867,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp); i < count; i++, dp++) { bcopy(dp->id, &key.id, sizeof(key.id)); @@ -710,9 +880,13 @@ pfsync_input(struct mbuf *m, ...) pfsyncstats.pfsyncs_badstate++; continue; } + st->timeout = PFTM_PURGE; st->sync_flags |= PFSTATE_FROMSYNC; - pf_unlink_state(st); + pf_purge_expired_state(st); } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; case PFSYNC_ACT_INS_F: @@ -727,6 +901,9 @@ pfsync_input(struct mbuf *m, ...) } s = splsoftnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif if (sc->sc_mbuf != NULL) pfsync_sendout(sc); for (i = 0, @@ -737,15 +914,17 @@ pfsync_input(struct mbuf *m, ...) if (key.id == 0 && key.creatorid == 0) { sc->sc_ureq_received = time_uptime; - if (sc->sc_bulk_send_next == NULL) - sc->sc_bulk_send_next = - TAILQ_FIRST(&state_list); - sc->sc_bulk_terminator = sc->sc_bulk_send_next; if (pf_status.debug >= PF_DEBUG_MISC) printf("pfsync: received " "bulk update request\n"); pfsync_send_bus(sc, PFSYNC_BUS_START); +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulk_tmo, 1 * hz, + pfsync_bulk_update, + LIST_FIRST(&pfsync_list)); +#else timeout_add(&sc->sc_bulk_tmo, 1 * hz); +#endif } else { st = pf_find_state_byid(&key); if (st == NULL) { @@ -759,6 +938,9 @@ pfsync_input(struct mbuf *m, ...) } if (sc->sc_mbuf != NULL) pfsync_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; case PFSYNC_ACT_BUS: @@ -774,9 +956,16 @@ pfsync_input(struct mbuf *m, ...) bus = (struct pfsync_state_bus *)(mp->m_data + offp); switch (bus->status) { case PFSYNC_BUS_START: +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, + pf_pool_limits[PF_LIMIT_STATES].limit / + (PFSYNC_BULKPACKETS * sc->sc_maxcount), + pfsync_bulkfail, LIST_FIRST(&pfsync_list)); +#else timeout_add(&sc->sc_bulkfail_tmo, pf_pool_limits[PF_LIMIT_STATES].limit / (PFSYNC_BULKPACKETS * sc->sc_maxcount)); +#endif if (pf_status.debug >= PF_DEBUG_MISC) printf("pfsync: received bulk " "update start\n"); @@ -787,10 +976,14 @@ pfsync_input(struct mbuf *m, ...) /* that's it, we're happy */ sc->sc_ureq_sent = 0; sc->sc_bulk_tries = 0; +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulkfail_tmo); +#else timeout_del(&sc->sc_bulkfail_tmo); -#if NCARP > 0 +#endif +#if NCARP > 0 /* XXX_IMPORT */ if (!pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, -1); + carp_suppress_preempt--; #endif pfsync_sync_ok = 1; if (pf_status.debug >= PF_DEBUG_MISC) @@ -804,18 +997,6 @@ pfsync_input(struct mbuf *m, ...) break; } break; - case PFSYNC_ACT_TDB_UPD: - if ((mp = m_pulldown(m, iplen + sizeof(*ph), - count * sizeof(*pt), &offp)) == NULL) { - pfsyncstats.pfsyncs_badlen++; - return; - } - s = splsoftnet(); - for (i = 0, pt = (struct pfsync_tdb *)(mp->m_data + offp); - i < count; i++, pt++) - pfsync_update_net_tdb(pt); - splx(s); - break; } done: @@ -835,7 +1016,9 @@ pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, int pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { +#ifndef __FreeBSD__ struct proc *p = curproc; +#endif struct pfsync_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct ip_moptions *imo = &sc->sc_imo; @@ -848,10 +1031,17 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCAIFADDR: case SIOCSIFDSTADDR: case SIOCSIFFLAGS: +#ifdef __FreeBSD__ + if (ifp->if_flags & IFF_UP) + ifp->if_drv_flags |= IFF_DRV_RUNNING; + else + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; +#else if (ifp->if_flags & IFF_UP) ifp->if_flags |= IFF_RUNNING; else ifp->if_flags &= ~IFF_RUNNING; +#endif break; case SIOCSIFMTU: if (ifr->ifr_mtu < PFSYNC_MINMTU) @@ -859,12 +1049,22 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (ifr->ifr_mtu > MCLBYTES) ifr->ifr_mtu = MCLBYTES; s = splnet(); - if (ifr->ifr_mtu < ifp->if_mtu) +#ifdef __FreeBSD__ + PF_LOCK(); +#endif + if (ifr->ifr_mtu < ifp->if_mtu) { pfsync_sendout(sc); + } pfsync_setmtu(sc, ifr->ifr_mtu); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; case SIOCGETPFSYNC: +#ifdef __FreeBSD__ + /* XXX: read unlocked */ +#endif bzero(&pfsyncr, sizeof(pfsyncr)); if (sc->sc_sync_ifp) strlcpy(pfsyncr.pfsyncr_syncdev, @@ -875,19 +1075,31 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) return (error); break; case SIOCSETPFSYNC: +#ifdef __FreeBSD__ + if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0) +#else if ((error = suser(p, p->p_acflag)) != 0) +#endif return (error); if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr)))) return (error); if (pfsyncr.pfsyncr_syncpeer.s_addr == 0) +#ifdef __FreeBSD__ + sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP); +#else sc->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP; +#endif else sc->sc_sync_peer.s_addr = pfsyncr.pfsyncr_syncpeer.s_addr; if (pfsyncr.pfsyncr_maxupdates > 255) return (EINVAL); +#ifdef __FreeBSD__ + callout_drain(&sc->sc_send_tmo); + PF_LOCK(); +#endif sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates; if (pfsyncr.pfsyncr_syncdev[0] == 0) { @@ -904,21 +1116,36 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); imo->imo_multicast_ifp = NULL; } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif break; } - if ((sifp = ifunit(pfsyncr.pfsyncr_syncdev)) == NULL) + if ((sifp = ifunit(pfsyncr.pfsyncr_syncdev)) == NULL) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif return (EINVAL); + } s = splnet(); +#ifdef __FreeBSD__ + if (sifp->if_mtu < SCP2IFP(sc)->if_mtu || +#else if (sifp->if_mtu < sc->sc_if.if_mtu || +#endif (sc->sc_sync_ifp != NULL && sifp->if_mtu < sc->sc_sync_ifp->if_mtu) || sifp->if_mtu < MCLBYTES - sizeof(struct ip)) pfsync_sendout(sc); sc->sc_sync_ifp = sifp; +#ifdef __FreeBSD__ + pfsync_setmtu(sc, SCP2IFP(sc)->if_mtu); +#else pfsync_setmtu(sc, sc->sc_if.if_mtu); +#endif if (imo->imo_num_memberships > 0) { in_delmulti(imo->imo_membership[--imo->imo_num_memberships]); @@ -926,16 +1153,27 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } if (sc->sc_sync_ifp && +#ifdef __FreeBSD__ + sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { +#else sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) { +#endif struct in_addr addr; if (!(sc->sc_sync_ifp->if_flags & IFF_MULTICAST)) { sc->sc_sync_ifp = NULL; +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); return (EADDRNOTAVAIL); } - +#ifdef __FreeBSD__ + PF_UNLOCK(); /* addmulti mallocs w/ WAITOK */ + addr.s_addr = htonl(INADDR_PFSYNC_GROUP); +#else addr.s_addr = INADDR_PFSYNC_GROUP; +#endif if ((imo->imo_membership[0] = in_addmulti(&addr, sc->sc_sync_ifp)) == NULL) { @@ -947,27 +1185,45 @@ pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) imo->imo_multicast_ifp = sc->sc_sync_ifp; imo->imo_multicast_ttl = PFSYNC_DFLTTL; imo->imo_multicast_loop = 0; +#ifdef __FreeBSD__ + PF_LOCK(); +#endif } if (sc->sc_sync_ifp || +#ifdef __FreeBSD__ + sc->sc_sendaddr.s_addr != htonl(INADDR_PFSYNC_GROUP)) { +#else sc->sc_sendaddr.s_addr != INADDR_PFSYNC_GROUP) { +#endif /* Request a full state table update. */ sc->sc_ureq_sent = time_uptime; #if NCARP > 0 if (pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, 1); + carp_suppress_preempt++; #endif pfsync_sync_ok = 0; if (pf_status.debug >= PF_DEBUG_MISC) printf("pfsync: requesting bulk update\n"); +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, + pfsync_bulkfail, LIST_FIRST(&pfsync_list)); +#else timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); +#endif error = pfsync_request_update(NULL, NULL); if (error == ENOMEM) { +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); return (ENOMEM); } pfsync_sendout(sc); } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); break; @@ -993,8 +1249,13 @@ pfsync_setmtu(struct pfsync_softc *sc, int mtu_req) sizeof(struct pfsync_state); if (sc->sc_maxcount > 254) sc->sc_maxcount = 254; +#ifdef __FreeBSD__ + SCP2IFP(sc)->if_mtu = sizeof(struct pfsync_header) + + sc->sc_maxcount * sizeof(struct pfsync_state); +#else sc->sc_if.if_mtu = sizeof(struct pfsync_header) + sc->sc_maxcount * sizeof(struct pfsync_state); +#endif } struct mbuf * @@ -1004,9 +1265,16 @@ pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp) struct mbuf *m; int len; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { +#ifdef __FreeBSD__ + SCP2IFP(sc)->if_oerrors++; +#else sc->sc_if.if_oerrors++; +#endif return (NULL); } @@ -1031,10 +1299,6 @@ pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp) len = sizeof(struct pfsync_header) + sizeof(struct pfsync_state_bus); break; - case PFSYNC_ACT_TDB_UPD: - len = (sc->sc_maxcount * sizeof(struct pfsync_tdb)) + - sizeof(struct pfsync_header); - break; default: len = (sc->sc_maxcount * sizeof(struct pfsync_state)) + sizeof(struct pfsync_header); @@ -1045,7 +1309,11 @@ pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp) MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); +#ifdef __FreeBSD__ + SCP2IFP(sc)->if_oerrors++; +#else sc->sc_if.if_oerrors++; +#endif return (NULL); } m->m_data += (MCLBYTES - len) &~ (sizeof(long) - 1); @@ -1059,23 +1327,26 @@ pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp) h->af = 0; h->count = 0; h->action = action; - if (action != PFSYNC_ACT_TDB_UPD) - bcopy(&pf_status.pf_chksum, &h->pf_chksum, - PF_MD5_DIGEST_LENGTH); *sp = (void *)((char *)h + PFSYNC_HDRLEN); - if (action == PFSYNC_ACT_TDB_UPD) - timeout_add(&sc->sc_tdb_tmo, hz); - else - timeout_add(&sc->sc_tmo, hz); +#ifdef __FreeBSD__ + callout_reset(&sc->sc_tmo, hz, pfsync_timeout, + LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_tmo, hz); +#endif return (m); } int pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags) { - struct ifnet *ifp = NULL; - struct pfsync_softc *sc = pfsyncif; +#ifdef __FreeBSD__ + struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list)); +#else + struct ifnet *ifp = &pfsyncif.sc_if; +#endif + struct pfsync_softc *sc = ifp->if_softc; struct pfsync_header *h, *h_net; struct pfsync_state *sp = NULL; struct pfsync_state_upd *up = NULL; @@ -1085,16 +1356,19 @@ pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags) int s, ret = 0; u_int8_t i = 255, newaction = 0; - if (sc == NULL) - return (0); - ifp = &sc->sc_if; - +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif /* * If a packet falls in the forest and there's nobody around to * hear, does it make a sound? */ if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL && +#ifdef __FreeBSD__ + sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) { +#else sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) { +#endif /* Don't leave any stale pfsync packets hanging around. */ if (sc->sc_mbuf != NULL) { m_freem(sc->sc_mbuf); @@ -1151,6 +1425,8 @@ pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags) secs = time_second; st->pfsync_time = time_uptime; + TAILQ_REMOVE(&state_updates, st, u.s.entry_updates); + TAILQ_INSERT_TAIL(&state_updates, st, u.s.entry_updates); if (sp == NULL) { /* not a "duplicate" update */ @@ -1172,10 +1448,10 @@ pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags) bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); sp->creation = htonl(secs - st->creation); - pf_state_counter_hton(st->packets[0], sp->packets[0]); - pf_state_counter_hton(st->packets[1], sp->packets[1]); - pf_state_counter_hton(st->bytes[0], sp->bytes[0]); - pf_state_counter_hton(st->bytes[1], sp->bytes[1]); + sp->packets[0] = htonl(st->packets[0]); + sp->packets[1] = htonl(st->packets[1]); + sp->bytes[0] = htonl(st->bytes[0]); + sp->bytes[1] = htonl(st->bytes[1]); if ((r = st->rule.ptr) == NULL) sp->rule = htonl(-1); else @@ -1274,16 +1550,19 @@ pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags) int pfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src) { - struct ifnet *ifp = NULL; +#ifdef __FreeBSD__ + struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list)); +#else + struct ifnet *ifp = &pfsyncif.sc_if; +#endif struct pfsync_header *h; - struct pfsync_softc *sc = pfsyncif; + struct pfsync_softc *sc = ifp->if_softc; struct pfsync_state_upd_req *rup; int ret = 0; - if (sc == NULL) - return (0); - - ifp = &sc->sc_if; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif if (sc->sc_mbuf == NULL) { if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ, (void *)&sc->sc_statep.s)) == NULL) @@ -1320,16 +1599,19 @@ pfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src) int pfsync_clear_states(u_int32_t creatorid, char *ifname) { - struct ifnet *ifp = NULL; - struct pfsync_softc *sc = pfsyncif; +#ifdef __FreeBSD__ + struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list)); +#else + struct ifnet *ifp = &pfsyncif.sc_if; +#endif + struct pfsync_softc *sc = ifp->if_softc; struct pfsync_state_clr *cp; int s, ret; - if (sc == NULL) - return (0); - - ifp = &sc->sc_if; s = splnet(); +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif if (sc->sc_mbuf != NULL) pfsync_sendout(sc); if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR, @@ -1355,18 +1637,13 @@ pfsync_timeout(void *v) int s; s = splnet(); +#ifdef __FreeBSD__ + PF_LOCK(); +#endif pfsync_sendout(sc); - splx(s); -} - -void -pfsync_tdb_timeout(void *v) -{ - struct pfsync_softc *sc = v; - int s; - - s = splnet(); - pfsync_tdb_sendout(sc); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif splx(s); } @@ -1376,6 +1653,9 @@ pfsync_send_bus(struct pfsync_softc *sc, u_int8_t status) { struct pfsync_state_bus *bus; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); +#endif if (sc->sc_mbuf != NULL) pfsync_sendout(sc); @@ -1398,6 +1678,9 @@ pfsync_bulk_update(void *v) int s, i = 0; struct pf_state *state; +#ifdef __FreeBSD__ + PF_LOCK(); +#endif s = splnet(); if (sc->sc_mbuf != NULL) pfsync_sendout(sc); @@ -1406,43 +1689,44 @@ pfsync_bulk_update(void *v) * Grab at most PFSYNC_BULKPACKETS worth of states which have not * been sent since the latest request was made. */ - state = sc->sc_bulk_send_next; - if (state) - do { - /* send state update if syncable and not already sent */ - if (!state->sync_flags - && state->timeout < PFTM_MAX - && state->pfsync_time <= sc->sc_ureq_received) { + while ((state = TAILQ_FIRST(&state_updates)) != NULL && + ++i < (sc->sc_maxcount * PFSYNC_BULKPACKETS)) { + if (state->pfsync_time > sc->sc_ureq_received) { + /* we're done */ + pfsync_send_bus(sc, PFSYNC_BUS_END); + sc->sc_ureq_received = 0; +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulk_tmo); +#else + timeout_del(&sc->sc_bulk_tmo); +#endif + if (pf_status.debug >= PF_DEBUG_MISC) + printf("pfsync: bulk update complete\n"); + break; + } else { + /* send an update and move to end of list */ + if (!state->sync_flags) pfsync_pack_state(PFSYNC_ACT_UPD, state, 0); - i++; - } - - /* figure next state to send */ - state = TAILQ_NEXT(state, u.s.entry_list); - - /* wrap to start of list if we hit the end */ - if (!state) - state = TAILQ_FIRST(&state_list); - } while (i < sc->sc_maxcount * PFSYNC_BULKPACKETS && - state != sc->sc_bulk_terminator); - - if (!state || state == sc->sc_bulk_terminator) { - /* we're done */ - pfsync_send_bus(sc, PFSYNC_BUS_END); - sc->sc_ureq_received = 0; - sc->sc_bulk_send_next = NULL; - sc->sc_bulk_terminator = NULL; - timeout_del(&sc->sc_bulk_tmo); - if (pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync: bulk update complete\n"); - } else { - /* look again for more in a bit */ - timeout_add(&sc->sc_bulk_tmo, 1); - sc->sc_bulk_send_next = state; + state->pfsync_time = time_uptime; + TAILQ_REMOVE(&state_updates, state, u.s.entry_updates); + TAILQ_INSERT_TAIL(&state_updates, state, + u.s.entry_updates); + + /* look again for more in a bit */ +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulk_tmo, 1, pfsync_timeout, + LIST_FIRST(&pfsync_list)); +#else + timeout_add(&sc->sc_bulk_tmo, 1); +#endif + } } if (sc->sc_mbuf != NULL) pfsync_sendout(sc); splx(s); +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif } void @@ -1451,9 +1735,17 @@ pfsync_bulkfail(void *v) struct pfsync_softc *sc = v; int s, error; +#ifdef __FreeBSD__ + PF_LOCK(); +#endif if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) { /* Try again in a bit */ +#ifdef __FreeBSD__ + callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulkfail, + LIST_FIRST(&pfsync_list)); +#else timeout_add(&sc->sc_bulkfail_tmo, 5 * hz); +#endif s = splnet(); error = pfsync_request_update(NULL, NULL); if (error == ENOMEM) { @@ -1469,26 +1761,43 @@ pfsync_bulkfail(void *v) sc->sc_bulk_tries = 0; #if NCARP > 0 if (!pfsync_sync_ok) - carp_group_demote_adj(&sc->sc_if, -1); + carp_suppress_preempt--; #endif pfsync_sync_ok = 1; if (pf_status.debug >= PF_DEBUG_MISC) printf("pfsync: failed to receive " "bulk update status\n"); +#ifdef __FreeBSD__ + callout_stop(&sc->sc_bulkfail_tmo); +#else timeout_del(&sc->sc_bulkfail_tmo); +#endif } +#ifdef __FreeBSD__ + PF_UNLOCK(); +#endif } /* This must be called in splnet() */ int -pfsync_sendout(struct pfsync_softc *sc) +pfsync_sendout(sc) + struct pfsync_softc *sc; { #if NBPFILTER > 0 - struct ifnet *ifp = &sc->sc_if; +# ifdef __FreeBSD__ + struct ifnet *ifp = SCP2IFP(sc); +# else + struct ifnet *ifp = &sc->if_sc; +# endif #endif struct mbuf *m; +#ifdef __FreeBSD__ + PF_ASSERT(MA_OWNED); + callout_stop(&sc->sc_tmo); +#else timeout_del(&sc->sc_tmo); +#endif if (sc->sc_mbuf == NULL) return (0); @@ -1496,9 +1805,16 @@ pfsync_sendout(struct pfsync_softc *sc) sc->sc_mbuf = NULL; sc->sc_statep.s = NULL; +#ifdef __FreeBSD__ + KASSERT(m != NULL, ("pfsync_sendout: null mbuf")); +#endif #if NBPFILTER > 0 +#ifdef __FreeBSD__ + BPF_MTAP(ifp, m); +#else if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); + bpf_mtap(ifp->if_bpf, m); +#endif #endif if (sc->sc_mbuf_net) { @@ -1508,41 +1824,15 @@ pfsync_sendout(struct pfsync_softc *sc) sc->sc_statep_net.s = NULL; } - return pfsync_sendout_mbuf(sc, m); -} - -int -pfsync_tdb_sendout(struct pfsync_softc *sc) -{ -#if NBPFILTER > 0 - struct ifnet *ifp = &sc->sc_if; -#endif - struct mbuf *m; - - timeout_del(&sc->sc_tdb_tmo); - - if (sc->sc_mbuf_tdb == NULL) - return (0); - m = sc->sc_mbuf_tdb; - sc->sc_mbuf_tdb = NULL; - sc->sc_statep_tdb.t = NULL; - -#if NBPFILTER > 0 - if (ifp->if_bpf) - bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#ifdef __FreeBSD__ + if (sc->sc_sync_ifp || + sc->sc_sync_peer.s_addr != htonl(INADDR_PFSYNC_GROUP)) { +#else + if (sc->sc_sync_ifp ||sc->sc_sync_peer.s_addr != INADDR_PFSYNC_GROUP) { #endif + struct ip *ip; + struct sockaddr sa; - return pfsync_sendout_mbuf(sc, m); -} - -int -pfsync_sendout_mbuf(struct pfsync_softc *sc, struct mbuf *m) -{ - struct sockaddr sa; - struct ip *ip; - - if (sc->sc_sync_ifp || - sc->sc_sync_peer.s_addr != INADDR_PFSYNC_GROUP) { M_PREPEND(m, sizeof(struct ip), M_DONTWAIT); if (m == NULL) { pfsyncstats.pfsyncs_onomem++; @@ -1552,9 +1842,17 @@ pfsync_sendout_mbuf(struct pfsync_softc *sc, struct mbuf *m) ip->ip_v = IPVERSION; ip->ip_hl = sizeof(*ip) >> 2; ip->ip_tos = IPTOS_LOWDELAY; +#ifdef __FreeBSD__ + ip->ip_len = m->m_pkthdr.len; +#else ip->ip_len = htons(m->m_pkthdr.len); +#endif ip->ip_id = htons(ip_randomid()); +#ifdef __FreeBSD__ + ip->ip_off = IP_DF; +#else ip->ip_off = htons(IP_DF); +#endif ip->ip_ttl = PFSYNC_DFLTTL; ip->ip_p = IPPROTO_PFSYNC; ip->ip_sum = 0; @@ -1562,168 +1860,118 @@ pfsync_sendout_mbuf(struct pfsync_softc *sc, struct mbuf *m) bzero(&sa, sizeof(sa)); ip->ip_src.s_addr = INADDR_ANY; +#ifdef __FreeBSD__ + if (sc->sc_sendaddr.s_addr == htonl(INADDR_PFSYNC_GROUP)) +#else if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP) +#endif m->m_flags |= M_MCAST; ip->ip_dst = sc->sc_sendaddr; sc->sc_sendaddr.s_addr = sc->sc_sync_peer.s_addr; pfsyncstats.pfsyncs_opackets++; - +#ifdef __FreeBSD__ + if (!IF_HANDOFF(&sc->sc_ifq, m, NULL)) + pfsyncstats.pfsyncs_oerrors++; + callout_reset(&sc->sc_send_tmo, 1, pfsync_senddef, sc); +#else if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) pfsyncstats.pfsyncs_oerrors++; +#endif } else m_freem(m); return (0); } -/* Update an in-kernel tdb. Silently fail if no tdb is found. */ -void -pfsync_update_net_tdb(struct pfsync_tdb *pt) +#ifdef __FreeBSD__ +static void +pfsync_ifdetach(void *arg, struct ifnet *ifp) { - struct tdb *tdb; - int s; - - /* check for invalid values */ - if (ntohl(pt->spi) <= SPI_RESERVED_MAX || - (pt->dst.sa.sa_family != AF_INET && - pt->dst.sa.sa_family != AF_INET6)) - goto bad; - - s = spltdb(); - tdb = gettdb(pt->spi, &pt->dst, pt->sproto); - if (tdb) { - pt->rpl = ntohl(pt->rpl); - pt->cur_bytes = betoh64(pt->cur_bytes); - - /* Neither replay nor byte counter should ever decrease. */ - if (pt->rpl < tdb->tdb_rpl || - pt->cur_bytes < tdb->tdb_cur_bytes) { - splx(s); - goto bad; - } + struct pfsync_softc *sc = (struct pfsync_softc *)arg; + struct ip_moptions *imo; - tdb->tdb_rpl = pt->rpl; - tdb->tdb_cur_bytes = pt->cur_bytes; + if (sc == NULL || sc->sc_sync_ifp != ifp) + return; /* not for us; unlocked read */ + + PF_LOCK(); + + /* Deal with a member interface going away from under us. */ + sc->sc_sync_ifp = NULL; + if (sc->sc_mbuf_net != NULL) { + m_freem(sc->sc_mbuf_net); + sc->sc_mbuf_net = NULL; + sc->sc_statep_net.s = NULL; } - splx(s); - return; - - bad: - if (pf_status.debug >= PF_DEBUG_MISC) - printf("pfsync_insert: PFSYNC_ACT_TDB_UPD: " - "invalid value\n"); - pfsyncstats.pfsyncs_badstate++; - return; + imo = &sc->sc_imo; + if (imo->imo_num_memberships > 0) { + KASSERT(imo->imo_num_memberships == 1, + ("%s: imo_num_memberships != 1", __func__)); + /* + * Our event handler is always called after protocol + * domains have been detached from the underlying ifnet. + * Do not call in_delmulti(); we held a single reference + * which the protocol domain has purged in in_purgemaddrs(). + */ + imo->imo_membership[--imo->imo_num_memberships] = NULL; + imo->imo_multicast_ifp = NULL; + } + + PF_UNLOCK(); } -/* One of our local tdbs have been updated, need to sync rpl with others */ -int -pfsync_update_tdb(struct tdb *tdb, int output) +static void +pfsync_senddef(void *arg) { - struct ifnet *ifp = NULL; - struct pfsync_softc *sc = pfsyncif; - struct pfsync_header *h; - struct pfsync_tdb *pt = NULL; - int s, i, ret; - - if (sc == NULL) - return (0); + struct pfsync_softc *sc = (struct pfsync_softc *)arg; + struct mbuf *m; - ifp = &sc->sc_if; - if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL && - sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) { - /* Don't leave any stale pfsync packets hanging around. */ - if (sc->sc_mbuf_tdb != NULL) { - m_freem(sc->sc_mbuf_tdb); - sc->sc_mbuf_tdb = NULL; - sc->sc_statep_tdb.t = NULL; + for(;;) { + IF_DEQUEUE(&sc->sc_ifq, m); + if (m == NULL) + break; + /* Deal with a member interface going away from under us. */ + if (sc->sc_sync_ifp == NULL) { + pfsyncstats.pfsyncs_oerrors++; + m_freem(m); + continue; } - return (0); + if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL)) + pfsyncstats.pfsyncs_oerrors++; } +} - s = splnet(); - if (sc->sc_mbuf_tdb == NULL) { - if ((sc->sc_mbuf_tdb = pfsync_get_mbuf(sc, PFSYNC_ACT_TDB_UPD, - (void *)&sc->sc_statep_tdb.t)) == NULL) { - splx(s); - return (ENOMEM); - } - h = mtod(sc->sc_mbuf_tdb, struct pfsync_header *); - } else { - h = mtod(sc->sc_mbuf_tdb, struct pfsync_header *); - if (h->action != PFSYNC_ACT_TDB_UPD) { - /* - * XXX will never happen as long as there's - * only one "TDB action". - */ - pfsync_tdb_sendout(sc); - sc->sc_mbuf_tdb = pfsync_get_mbuf(sc, - PFSYNC_ACT_TDB_UPD, (void *)&sc->sc_statep_tdb.t); - if (sc->sc_mbuf_tdb == NULL) { - splx(s); - return (ENOMEM); - } - h = mtod(sc->sc_mbuf_tdb, struct pfsync_header *); - } else if (sc->sc_maxupdates) { - /* - * If it's an update, look in the packet to see if - * we already have an update for the state. - */ - struct pfsync_tdb *u = - (void *)((char *)h + PFSYNC_HDRLEN); - - for (i = 0; !pt && i < h->count; i++) { - if (tdb->tdb_spi == u->spi && - tdb->tdb_sproto == u->sproto && - !bcmp(&tdb->tdb_dst, &u->dst, - SA_LEN(&u->dst.sa))) { - pt = u; - pt->updates++; - } - u++; - } - } - } +static int +pfsync_modevent(module_t mod, int type, void *data) +{ + int error = 0; - if (pt == NULL) { - /* not a "duplicate" update */ - pt = sc->sc_statep_tdb.t++; - sc->sc_mbuf_tdb->m_pkthdr.len = - sc->sc_mbuf_tdb->m_len += sizeof(struct pfsync_tdb); - h->count++; - bzero(pt, sizeof(*pt)); + switch (type) { + case MOD_LOAD: + LIST_INIT(&pfsync_list); + if_clone_attach(&pfsync_cloner); + break; + + case MOD_UNLOAD: + if_clone_detach(&pfsync_cloner); + break; - pt->spi = tdb->tdb_spi; - memcpy(&pt->dst, &tdb->tdb_dst, sizeof pt->dst); - pt->sproto = tdb->tdb_sproto; + default: + error = EINVAL; + break; } - /* - * When a failover happens, the master's rpl is probably above - * what we see here (we may be up to a second late), so - * increase it a bit for outbound tdbs to manage most such - * situations. - * - * For now, just add an offset that is likely to be larger - * than the number of packets we can see in one second. The RFC - * just says the next packet must have a higher seq value. - * - * XXX What is a good algorithm for this? We could use - * a rate-determined increase, but to know it, we would have - * to extend struct tdb. - * XXX pt->rpl can wrap over MAXINT, but if so the real tdb - * will soon be replaced anyway. For now, just don't handle - * this edge case. - */ -#define RPL_INCR 16384 - pt->rpl = htonl(tdb->tdb_rpl + (output ? RPL_INCR : 0)); - pt->cur_bytes = htobe64(tdb->tdb_cur_bytes); + return error; +} - if (h->count == sc->sc_maxcount || - (sc->sc_maxupdates && (pt->updates >= sc->sc_maxupdates))) - ret = pfsync_tdb_sendout(sc); +static moduledata_t pfsync_mod = { + "pfsync", + pfsync_modevent, + 0 +}; - splx(s); - return (ret); -} +#define PFSYNC_MODVER 1 + +DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); +MODULE_VERSION(pfsync, PFSYNC_MODVER); +#endif /* __FreeBSD__ */ |