if_pfsync.c revision 138666
133965Sjdp/*	$FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 138666 2004-12-10 17:42:47Z mlaier $	*/
2218822Sdim/*	$OpenBSD: if_pfsync.c,v 1.26 2004/03/28 18:14:20 mcbride Exp $	*/
3218822Sdim
460484Sobrien/*
533965Sjdp * Copyright (c) 2002 Michael Shalayeff
633965Sjdp * All rights reserved.
733965Sjdp *
833965Sjdp * Redistribution and use in source and binary forms, with or without
933965Sjdp * modification, are permitted provided that the following conditions
1033965Sjdp * are met:
1133965Sjdp * 1. Redistributions of source code must retain the above copyright
1233965Sjdp *    notice, this list of conditions and the following disclaimer.
1333965Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1433965Sjdp *    notice, this list of conditions and the following disclaimer in the
1533965Sjdp *    documentation and/or other materials provided with the distribution.
1633965Sjdp *
1733965Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1833965Sjdp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1933965Sjdp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2033965Sjdp * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
21218822Sdim * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22218822Sdim * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2333965Sjdp * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2433965Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2560484Sobrien * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2633965Sjdp * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2733965Sjdp * THE POSSIBILITY OF SUCH DAMAGE.
2860484Sobrien */
2933965Sjdp
3033965Sjdp#ifdef __FreeBSD__
3133965Sjdp#include "opt_inet.h"
3233965Sjdp#include "opt_inet6.h"
3333965Sjdp#endif
3489857Sobrien
3533965Sjdp#ifndef __FreeBSD__
3633965Sjdp#include "bpfilter.h"
3789857Sobrien#include "pfsync.h"
3833965Sjdp#elif __FreeBSD__ >= 5
3933965Sjdp#include "opt_bpf.h"
4033965Sjdp#include "opt_pf.h"
4133965Sjdp#define	NBPFILTER	DEV_BPF
4233965Sjdp#define	NPFSYNC		DEV_PFSYNC
4333965Sjdp#endif
4433965Sjdp
4533965Sjdp#include <sys/param.h>
4633965Sjdp#include <sys/proc.h>
4733965Sjdp#include <sys/systm.h>
4833965Sjdp#include <sys/time.h>
4933965Sjdp#include <sys/mbuf.h>
5033965Sjdp#include <sys/socket.h>
5133965Sjdp#ifdef __FreeBSD__
5233965Sjdp#include <sys/kernel.h>
5333965Sjdp#include <sys/malloc.h>
5433965Sjdp#include <sys/module.h>
5533965Sjdp#include <sys/sockio.h>
5633965Sjdp#include <sys/lock.h>
57218822Sdim#include <sys/mutex.h>
58218822Sdim#else
59218822Sdim#include <sys/ioctl.h>
60218822Sdim#include <sys/timeout.h>
6133965Sjdp#endif
62218822Sdim
63218822Sdim#include <net/if.h>
64218822Sdim#if defined(__FreeBSD__)
65218822Sdim#include <net/if_clone.h>
66218822Sdim#endif
67218822Sdim#include <net/if_types.h>
68218822Sdim#include <net/route.h>
6933965Sjdp#include <net/bpf.h>
70218822Sdim
71218822Sdim#ifdef	INET
72218822Sdim#include <netinet/in.h>
73218822Sdim#include <netinet/in_systm.h>
74218822Sdim#include <netinet/in_var.h>
75218822Sdim#include <netinet/ip.h>
76218822Sdim#include <netinet/ip_var.h>
77218822Sdim#endif
7833965Sjdp
79218822Sdim#ifdef INET6
80218822Sdim#ifndef INET
81218822Sdim#include <netinet/in.h>
82218822Sdim#endif
83218822Sdim#include <netinet6/nd6.h>
84218822Sdim#endif /* INET6 */
85218822Sdim
86218822Sdim#include <net/pfvar.h>
87218822Sdim#include <net/if_pfsync.h>
88218822Sdim
89218822Sdim#ifdef __FreeBSD__
90218822Sdim#define	PFSYNCNAME	"pfsync"
91218822Sdim#endif
92218822Sdim
93218822Sdim#define PFSYNC_MINMTU	\
94218822Sdim    (sizeof(struct pfsync_header) + sizeof(struct pf_state))
95218822Sdim
96218822Sdim#ifdef PFSYNCDEBUG
97218822Sdim#define DPRINTF(x)    do { if (pfsyncdebug) printf x ; } while (0)
98218822Sdimint pfsyncdebug;
99218822Sdim#else
100218822Sdim#define DPRINTF(x)
101218822Sdim#endif
102218822Sdim
103218822Sdim#ifndef __FreeBSD__
104218822Sdimstruct pfsync_softc	pfsyncif;
105218822Sdim#endif
106218822Sdimint			pfsync_sync_ok;
107218822Sdimstruct pfsyncstats	pfsyncstats;
108218822Sdim
109218822Sdim#ifdef __FreeBSD__
110218822Sdim
111218822Sdim/*
112218822Sdim * Locking notes:
113218822Sdim * Whenever we really touch/look at the state table we have to hold the
114218822Sdim * PF_LOCK. Functions that do just the interface handling, grab the per
115218822Sdim * softc lock instead.
116218822Sdim *
117218822Sdim */
118218822Sdim
119218822Sdimstatic void	pfsync_clone_destroy(struct ifnet *);
120218822Sdimstatic int	pfsync_clone_create(struct if_clone *, int);
121218822Sdim#else
122218822Sdimvoid	pfsyncattach(int);
123218822Sdim#endif
124218822Sdimvoid	pfsync_setmtu(struct pfsync_softc *, int);
125218822Sdimint	pfsync_insert_net_state(struct pfsync_state *);
126218822Sdimint	pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
127218822Sdim	    struct rtentry *);
128218822Sdimint	pfsyncioctl(struct ifnet *, u_long, caddr_t);
129218822Sdimvoid	pfsyncstart(struct ifnet *);
130218822Sdim
131218822Sdimstruct mbuf *pfsync_get_mbuf(struct pfsync_softc *, u_int8_t, void **);
132218822Sdimint	pfsync_request_update(struct pfsync_state_upd *, struct in_addr *);
133218822Sdimint	pfsync_sendout(struct pfsync_softc *);
134218822Sdimvoid	pfsync_timeout(void *);
135218822Sdimvoid	pfsync_send_bus(struct pfsync_softc *, u_int8_t);
136218822Sdimvoid	pfsync_bulk_update(void *);
137218822Sdimvoid	pfsync_bulkfail(void *);
138218822Sdim
139218822Sdim#ifndef __FreeBSD__
140218822Sdimextern int ifqmaxlen;
141218822Sdimextern struct timeval time;
142218822Sdimextern struct timeval mono_time;
143218822Sdimextern int hz;
144218822Sdim#endif
145218822Sdim
146218822Sdim#ifdef __FreeBSD__
147218822Sdimstatic MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface");
148218822Sdimstatic LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list;
149218822SdimIFC_SIMPLE_DECLARE(pfsync, 1);
150218822Sdim
151218822Sdimstatic void
152218822Sdimpfsync_clone_destroy(struct ifnet *ifp)
153218822Sdim{
154218822Sdim        struct pfsync_softc *sc;
155218822Sdim
156218822Sdim	sc = ifp->if_softc;
157218822Sdim	callout_stop(&sc->sc_tmo);
158218822Sdim	callout_stop(&sc->sc_bulk_tmo);
159218822Sdim	callout_stop(&sc->sc_bulkfail_tmo);
160218822Sdim
161218822Sdim#if NBPFILTER > 0
162218822Sdim        bpfdetach(ifp);
163218822Sdim#endif
164218822Sdim        if_detach(ifp);
165218822Sdim        LIST_REMOVE(sc, sc_next);
166218822Sdim        free(sc, M_PFSYNC);
167218822Sdim}
168218822Sdim
169218822Sdimstatic int
170218822Sdimpfsync_clone_create(struct if_clone *ifc, int unit)
171218822Sdim{
172218822Sdim	struct pfsync_softc *sc;
173218822Sdim	struct ifnet *ifp;
174218822Sdim
175218822Sdim	MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC,
176218822Sdim	    M_WAITOK|M_ZERO);
177218822Sdim
178218822Sdim	pfsync_sync_ok = 1;
179218822Sdim	sc->sc_mbuf = NULL;
180218822Sdim	sc->sc_mbuf_net = NULL;
181218822Sdim	sc->sc_statep.s = NULL;
182218822Sdim	sc->sc_statep_net.s = NULL;
183218822Sdim	sc->sc_maxupdates = 128;
184218822Sdim	sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP);
185218822Sdim	sc->sc_ureq_received = 0;
186218822Sdim	sc->sc_ureq_sent = 0;
187218822Sdim
188218822Sdim	ifp = &sc->sc_if;
189218822Sdim	if_initname(ifp, ifc->ifc_name, unit);
190218822Sdim	ifp->if_ioctl = pfsyncioctl;
191218822Sdim	ifp->if_output = pfsyncoutput;
192218822Sdim	ifp->if_start = pfsyncstart;
193218822Sdim	ifp->if_type = IFT_PFSYNC;
194218822Sdim	ifp->if_snd.ifq_maxlen = ifqmaxlen;
195218822Sdim	ifp->if_hdrlen = PFSYNC_HDRLEN;
19633965Sjdp	ifp->if_baudrate = IF_Mbps(100);
197218822Sdim	ifp->if_softc = sc;
19833965Sjdp	pfsync_setmtu(sc, MCLBYTES);
19933965Sjdp	/*
20033965Sjdp	 * XXX
20133965Sjdp	 *  The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE
20233965Sjdp	 * if Gaint lock is removed from the network stack.
20333965Sjdp	 */
20460484Sobrien	callout_init(&sc->sc_tmo, 0);
20533965Sjdp	callout_init(&sc->sc_bulk_tmo, 0);
206218822Sdim	callout_init(&sc->sc_bulkfail_tmo, 0);
20733965Sjdp	if_attach(&sc->sc_if);
20833965Sjdp
20933965Sjdp	LIST_INSERT_HEAD(&pfsync_list, sc, sc_next);
21033965Sjdp#if NBPFILTER > 0
21133965Sjdp	bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN);
21233965Sjdp#endif
21333965Sjdp
214218822Sdim	return (0);
21533965Sjdp}
21633965Sjdp#else /* !__FreeBSD__ */
21733965Sjdpvoid
21833965Sjdppfsyncattach(int npfsync)
21933965Sjdp{
22033965Sjdp	struct ifnet *ifp;
22133965Sjdp
222218822Sdim	pfsync_sync_ok = 1;
22333965Sjdp	bzero(&pfsyncif, sizeof(pfsyncif));
22433965Sjdp	pfsyncif.sc_mbuf = NULL;
22533965Sjdp	pfsyncif.sc_mbuf_net = NULL;
22633965Sjdp	pfsyncif.sc_statep.s = NULL;
22733965Sjdp	pfsyncif.sc_statep_net.s = NULL;
22833965Sjdp	pfsyncif.sc_maxupdates = 128;
22933965Sjdp	pfsyncif.sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP;
230218822Sdim	pfsyncif.sc_ureq_received = 0;
23133965Sjdp	pfsyncif.sc_ureq_sent = 0;
23233965Sjdp	ifp = &pfsyncif.sc_if;
23333965Sjdp	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
234218822Sdim	ifp->if_softc = &pfsyncif;
23533965Sjdp	ifp->if_ioctl = pfsyncioctl;
23633965Sjdp	ifp->if_output = pfsyncoutput;
23733965Sjdp	ifp->if_start = pfsyncstart;
23833965Sjdp	ifp->if_type = IFT_PFSYNC;
239218822Sdim	ifp->if_snd.ifq_maxlen = ifqmaxlen;
24033965Sjdp	ifp->if_hdrlen = PFSYNC_HDRLEN;
24133965Sjdp	pfsync_setmtu(&pfsyncif, MCLBYTES);
24233965Sjdp	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
243218822Sdim	timeout_set(&pfsyncif.sc_bulk_tmo, pfsync_bulk_update, &pfsyncif);
24433965Sjdp	timeout_set(&pfsyncif.sc_bulkfail_tmo, pfsync_bulkfail, &pfsyncif);
24533965Sjdp	if_attach(ifp);
24633965Sjdp	if_alloc_sadl(ifp);
247218822Sdim
24833965Sjdp#if NBPFILTER > 0
24933965Sjdp	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
25033965Sjdp#endif
25133965Sjdp}
252218822Sdim#endif
25333965Sjdp
25433965Sjdp/*
25533965Sjdp * Start output on the pfsync interface.
25633965Sjdp */
257218822Sdimvoid
25833965Sjdppfsyncstart(struct ifnet *ifp)
25933965Sjdp{
26033965Sjdp#ifdef __FreeBSD__
26133965Sjdp	IF_LOCK(&ifp->if_snd);
262218822Sdim	_IF_DROP(&ifp->if_snd);
26333965Sjdp	_IF_DRAIN(&ifp->if_snd);
26433965Sjdp	IF_UNLOCK(&ifp->if_snd);
26533965Sjdp#else
26633965Sjdp	struct mbuf *m;
267218822Sdim	int s;
26833965Sjdp
26933965Sjdp	for (;;) {
27033965Sjdp		s = splimp();
27133965Sjdp		IF_DROP(&ifp->if_snd);
272218822Sdim		IF_DEQUEUE(&ifp->if_snd, m);
27333965Sjdp		splx(s);
27433965Sjdp
27533965Sjdp		if (m == NULL)
276218822Sdim			return;
27733965Sjdp		else
27833965Sjdp			m_freem(m);
27933965Sjdp	}
28033965Sjdp#endif
28133965Sjdp}
28233965Sjdp
28333965Sjdpint
28433965Sjdppfsync_insert_net_state(struct pfsync_state *sp)
285218822Sdim{
28633965Sjdp	struct pf_state	*st = NULL;
28733965Sjdp	struct pf_rule *r = NULL;
28833965Sjdp	struct pfi_kif	*kif;
28933965Sjdp
29033965Sjdp#ifdef __FreeBSD__
291218822Sdim	PF_ASSERT(MA_OWNED);
29233965Sjdp#endif
29333965Sjdp	if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) {
29433965Sjdp		printf("pfsync_insert_net_state: invalid creator id:"
29533965Sjdp		    " %08x\n", ntohl(sp->creatorid));
296218822Sdim		return (EINVAL);
29733965Sjdp	}
29860484Sobrien
29933965Sjdp	kif = pfi_lookup_create(sp->ifname);
30033965Sjdp	if (kif == NULL) {
301218822Sdim		if (pf_status.debug >= PF_DEBUG_MISC)
30233965Sjdp			printf("pfsync_insert_net_state: "
30333965Sjdp			    "unknown interface: %s\n", sp->ifname);
30433965Sjdp		/* skip this state */
30533965Sjdp		return (0);
30633965Sjdp	}
30733965Sjdp
30833965Sjdp	/*
30933965Sjdp	 * Just use the default rule until we have infrastructure to find the
310218822Sdim	 * best matching rule.
31133965Sjdp	 */
31233965Sjdp	r = &pf_default_rule;
31333965Sjdp
314218822Sdim	if (!r->max_states || r->states < r->max_states)
31533965Sjdp		st = pool_get(&pf_state_pl, PR_NOWAIT);
31633965Sjdp	if (st == NULL) {
31760484Sobrien		pfi_maybe_destroy(kif);
31860484Sobrien		return (ENOMEM);
319218822Sdim	}
32060484Sobrien	bzero(st, sizeof(*st));
32160484Sobrien
32260484Sobrien	st->rule.ptr = r;
323218822Sdim	/* XXX get pointers to nat_rule and anchor */
32460484Sobrien
32560484Sobrien	/* fill in the rest of the state entry */
32660484Sobrien	pf_state_host_ntoh(&sp->lan, &st->lan);
32760484Sobrien	pf_state_host_ntoh(&sp->gwy, &st->gwy);
32833965Sjdp	pf_state_host_ntoh(&sp->ext, &st->ext);
32933965Sjdp
33060484Sobrien	pf_state_peer_ntoh(&sp->src, &st->src);
33133965Sjdp	pf_state_peer_ntoh(&sp->dst, &st->dst);
33233965Sjdp
333218822Sdim	bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr));
33433965Sjdp#ifdef __FreeBSD__
335218822Sdim	st->creation = ntohl(sp->creation) + time_second;
33633965Sjdp	st->expire = ntohl(sp->expire) + time_second;
33733965Sjdp#else
33833965Sjdp	st->creation = ntohl(sp->creation) + time.tv_sec;
339218822Sdim	st->expire = ntohl(sp->expire) + time.tv_sec;
34033965Sjdp#endif
34133965Sjdp
34233965Sjdp	st->af = sp->af;
34333965Sjdp	st->proto = sp->proto;
344218822Sdim	st->direction = sp->direction;
34533965Sjdp	st->log = sp->log;
34633965Sjdp	st->timeout = sp->timeout;
34733965Sjdp	st->allow_opts = sp->allow_opts;
34833965Sjdp
349218822Sdim	bcopy(sp->id, &st->id, sizeof(st->id));
35033965Sjdp	st->creatorid = sp->creatorid;
35133965Sjdp	st->sync_flags = sp->sync_flags | PFSTATE_FROMSYNC;
35233965Sjdp
35333965Sjdp
354218822Sdim	if (pf_insert_state(kif, st)) {
35533965Sjdp		pfi_maybe_destroy(kif);
35633965Sjdp		pool_put(&pf_state_pl, st);
35733965Sjdp		return (EINVAL);
35833965Sjdp	}
359218822Sdim
36033965Sjdp	return (0);
36133965Sjdp}
36233965Sjdp
36333965Sjdpvoid
36433965Sjdp#ifdef __FreeBSD__
365218822Sdimpfsync_input(struct mbuf *m, __unused int off)
36633965Sjdp#else
36733965Sjdppfsync_input(struct mbuf *m, ...)
36833965Sjdp#endif
36933965Sjdp{
37033965Sjdp	struct ip *ip = mtod(m, struct ip *);
37133965Sjdp	struct pfsync_header *ph;
372218822Sdim#ifdef __FreeBSD__
37333965Sjdp	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
37433965Sjdp#else
37533965Sjdp	struct pfsync_softc *sc = &pfsyncif;
37633965Sjdp#endif
377218822Sdim	struct pf_state *st, key;
37833965Sjdp	struct pfsync_state *sp;
37933965Sjdp	struct pfsync_state_upd *up;
38033965Sjdp	struct pfsync_state_del *dp;
38133965Sjdp	struct pfsync_state_clr *cp;
382218822Sdim	struct pfsync_state_upd_req *rup;
38333965Sjdp	struct pfsync_state_bus *bus;
38433965Sjdp	struct in_addr src;
38533965Sjdp	struct mbuf *mp;
38633965Sjdp	int iplen, action, error, i, s, count, offp;
38733965Sjdp
38833965Sjdp	pfsyncstats.pfsyncs_ipackets++;
38933965Sjdp
39033965Sjdp	/* verify that we have a sync interface configured */
39160484Sobrien	if (!sc->sc_sync_ifp || !pf_status.running) /* XXX PF_LOCK? */
39260484Sobrien		goto done;
39360484Sobrien
39460484Sobrien	/* verify that the packet came in on the right interface */
39560484Sobrien	if (sc->sc_sync_ifp != m->m_pkthdr.rcvif) {
39660484Sobrien		pfsyncstats.pfsyncs_badif++;
39760484Sobrien		goto done;
39860484Sobrien	}
39933965Sjdp
40033965Sjdp	/* verify that the IP TTL is 255.  */
40133965Sjdp	if (ip->ip_ttl != PFSYNC_DFLTTL) {
40233965Sjdp		pfsyncstats.pfsyncs_badttl++;
40360484Sobrien		goto done;
40433965Sjdp	}
40533965Sjdp
40633965Sjdp	iplen = ip->ip_hl << 2;
40733965Sjdp
408218822Sdim	if (m->m_pkthdr.len < iplen + sizeof(*ph)) {
40933965Sjdp		pfsyncstats.pfsyncs_hdrops++;
41033965Sjdp		goto done;
41133965Sjdp	}
41233965Sjdp
41333965Sjdp	if (iplen + sizeof(*ph) > m->m_len) {
41433965Sjdp		if ((m = m_pullup(m, iplen + sizeof(*ph))) == NULL) {
41533965Sjdp			pfsyncstats.pfsyncs_hdrops++;
41633965Sjdp			goto done;
41733965Sjdp		}
41833965Sjdp		ip = mtod(m, struct ip *);
41933965Sjdp	}
42033965Sjdp	ph = (struct pfsync_header *)((char *)ip + iplen);
42133965Sjdp
42233965Sjdp	/* verify the version */
42333965Sjdp	if (ph->version != PFSYNC_VERSION) {
42460484Sobrien		pfsyncstats.pfsyncs_badver++;
42533965Sjdp		goto done;
42633965Sjdp	}
42733965Sjdp
42833965Sjdp	action = ph->action;
42933965Sjdp	count = ph->count;
43033965Sjdp
43133965Sjdp	/* make sure it's a valid action code */
43233965Sjdp	if (action >= PFSYNC_ACT_MAX) {
43333965Sjdp		pfsyncstats.pfsyncs_badact++;
43433965Sjdp		goto done;
43533965Sjdp	}
43633965Sjdp
43733965Sjdp	/* Cheaper to grab this now than having to mess with mbufs later */
43833965Sjdp	src = ip->ip_src;
43933965Sjdp
44033965Sjdp	switch (action) {
44133965Sjdp	case PFSYNC_ACT_CLR: {
44233965Sjdp		struct pfi_kif	*kif;
44333965Sjdp		u_int32_t creatorid;
44433965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
44533965Sjdp		    sizeof(*cp), &offp)) == NULL) {
44633965Sjdp			pfsyncstats.pfsyncs_badlen++;
44733965Sjdp			return;
44833965Sjdp		}
44933965Sjdp		cp = (struct pfsync_state_clr *)(mp->m_data + offp);
45033965Sjdp		creatorid = cp->creatorid;
45133965Sjdp
45233965Sjdp		s = splsoftnet();
45333965Sjdp#ifdef __FreeBSD__
45433965Sjdp		PF_LOCK();
45533965Sjdp#endif
45633965Sjdp		if (cp->ifname[0] == '\0') {
45733965Sjdp			RB_FOREACH(st, pf_state_tree_id, &tree_id) {
45833965Sjdp				if (st->creatorid == creatorid)
45933965Sjdp					st->timeout = PFTM_PURGE;
46033965Sjdp			}
46133965Sjdp		} else {
46233965Sjdp			kif = pfi_lookup_if(cp->ifname);
46333965Sjdp			if (kif == NULL) {
46433965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
46533965Sjdp					printf("pfsync_input: PFSYNC_ACT_CLR "
46633965Sjdp					    "bad interface: %s\n", cp->ifname);
46733965Sjdp				splx(s);
468218822Sdim#ifdef __FreeBSD__
46933965Sjdp				PF_UNLOCK();
470218822Sdim#endif
471218822Sdim				goto done;
47233965Sjdp			}
47333965Sjdp			RB_FOREACH(st, pf_state_tree_lan_ext,
47433965Sjdp			    &kif->pfik_lan_ext) {
475130561Sobrien				if (st->creatorid == creatorid)
47633965Sjdp					st->timeout = PFTM_PURGE;
47733965Sjdp			}
47833965Sjdp		}
479130561Sobrien		pf_purge_expired_states();
480130561Sobrien#ifdef __FreeBSD__
481130561Sobrien		PF_UNLOCK();
48233965Sjdp#endif
48333965Sjdp		splx(s);
48433965Sjdp
48591041Sobrien		break;
48633965Sjdp	}
48791041Sobrien	case PFSYNC_ACT_INS:
48833965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
48933965Sjdp		    count * sizeof(*sp), &offp)) == NULL) {
49089857Sobrien			pfsyncstats.pfsyncs_badlen++;
49189857Sobrien			return;
49233965Sjdp		}
49333965Sjdp
49433965Sjdp		s = splsoftnet();
49533965Sjdp#ifdef __FreeBSD__
49633965Sjdp		PF_LOCK();
49733965Sjdp#endif
49833965Sjdp		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
49933965Sjdp		    i < count; i++, sp++) {
50033965Sjdp			/* check for invalid values */
50133965Sjdp			if (sp->timeout >= PFTM_MAX ||
50233965Sjdp			    sp->src.state > PF_TCPS_PROXY_DST ||
50333965Sjdp			    sp->dst.state > PF_TCPS_PROXY_DST ||
50433965Sjdp			    sp->direction > PF_OUT ||
50533965Sjdp			    (sp->af != AF_INET && sp->af != AF_INET6)) {
50633965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
50733965Sjdp					printf("pfsync_insert: PFSYNC_ACT_INS: "
50833965Sjdp					    "invalid value\n");
50933965Sjdp				pfsyncstats.pfsyncs_badstate++;
51033965Sjdp				continue;
51133965Sjdp			}
51233965Sjdp
51333965Sjdp			if ((error = pfsync_insert_net_state(sp))) {
51433965Sjdp				if (error == ENOMEM) {
51533965Sjdp					splx(s);
51633965Sjdp#ifdef __FreeBSD__
51733965Sjdp					PF_UNLOCK();
51833965Sjdp#endif
51933965Sjdp					goto done;
52033965Sjdp				}
52133965Sjdp				continue;
52233965Sjdp			}
52333965Sjdp		}
52433965Sjdp#ifdef __FreeBSD__
52533965Sjdp		PF_UNLOCK();
52633965Sjdp#endif
52733965Sjdp		splx(s);
52833965Sjdp		break;
52933965Sjdp	case PFSYNC_ACT_UPD:
53033965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
53133965Sjdp		    count * sizeof(*sp), &offp)) == NULL) {
53233965Sjdp			pfsyncstats.pfsyncs_badlen++;
53333965Sjdp			return;
53433965Sjdp		}
53533965Sjdp
53633965Sjdp		s = splsoftnet();
53733965Sjdp#ifdef __FreeBSD__
53833965Sjdp		PF_LOCK();
53933965Sjdp#endif
54033965Sjdp		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
54133965Sjdp		    i < count; i++, sp++) {
54233965Sjdp			/* check for invalid values */
54333965Sjdp			if (sp->timeout >= PFTM_MAX ||
54433965Sjdp			    sp->src.state > PF_TCPS_PROXY_DST ||
54533965Sjdp			    sp->dst.state > PF_TCPS_PROXY_DST) {
54633965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
54733965Sjdp					printf("pfsync_insert: PFSYNC_ACT_UPD: "
54833965Sjdp					    "invalid value\n");
54933965Sjdp				pfsyncstats.pfsyncs_badstate++;
55033965Sjdp				continue;
55133965Sjdp			}
55233965Sjdp
55333965Sjdp			bcopy(sp->id, &key.id, sizeof(key.id));
55433965Sjdp			key.creatorid = sp->creatorid;
55560484Sobrien
55633965Sjdp			st = pf_find_state_byid(&key);
55733965Sjdp			if (st == NULL) {
55833965Sjdp				/* insert the update */
55933965Sjdp				if (pfsync_insert_net_state(sp))
56033965Sjdp					pfsyncstats.pfsyncs_badstate++;
56133965Sjdp				continue;
56233965Sjdp			}
56333965Sjdp			pf_state_peer_ntoh(&sp->src, &st->src);
56433965Sjdp			pf_state_peer_ntoh(&sp->dst, &st->dst);
56533965Sjdp#ifdef __FreeBSD__
56633965Sjdp			st->expire = ntohl(sp->expire) + time_second;
56733965Sjdp#else
56860484Sobrien			st->expire = ntohl(sp->expire) + time.tv_sec;
56933965Sjdp#endif
57033965Sjdp			st->timeout = sp->timeout;
57133965Sjdp
57233965Sjdp		}
57333965Sjdp#ifdef __FreeBSD__
57433965Sjdp		PF_UNLOCK();
57533965Sjdp#endif
57633965Sjdp		splx(s);
57733965Sjdp		break;
57833965Sjdp	/*
57933965Sjdp	 * It's not strictly necessary for us to support the "uncompressed"
58033965Sjdp	 * delete action, but it's relatively simple and maintains consistency.
58133965Sjdp	 */
58233965Sjdp	case PFSYNC_ACT_DEL:
58333965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
58433965Sjdp		    count * sizeof(*sp), &offp)) == NULL) {
58533965Sjdp			pfsyncstats.pfsyncs_badlen++;
58633965Sjdp			return;
58733965Sjdp		}
58833965Sjdp
58933965Sjdp		s = splsoftnet();
59033965Sjdp#ifdef __FreeBSD__
59133965Sjdp		PF_LOCK();
59233965Sjdp#endif
59333965Sjdp		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
59433965Sjdp		    i < count; i++, sp++) {
59533965Sjdp			bcopy(sp->id, &key.id, sizeof(key.id));
59633965Sjdp			key.creatorid = sp->creatorid;
59733965Sjdp
59833965Sjdp			st = pf_find_state_byid(&key);
59933965Sjdp			if (st == NULL) {
60033965Sjdp				pfsyncstats.pfsyncs_badstate++;
60133965Sjdp				continue;
60233965Sjdp			}
60333965Sjdp			/*
60433965Sjdp			 * XXX
60533965Sjdp			 * pf_purge_expired_states() is expensive,
60633965Sjdp			 * we really want to purge the state directly.
60733965Sjdp			 */
60833965Sjdp			st->timeout = PFTM_PURGE;
60933965Sjdp			st->sync_flags |= PFSTATE_FROMSYNC;
61033965Sjdp		}
61133965Sjdp		pf_purge_expired_states();
61233965Sjdp#ifdef __FreeBSD__
61333965Sjdp		PF_UNLOCK();
61433965Sjdp#endif
61533965Sjdp		splx(s);
61633965Sjdp		break;
61733965Sjdp	case PFSYNC_ACT_UPD_C: {
61833965Sjdp		int update_requested = 0;
61933965Sjdp
62033965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
62133965Sjdp		    count * sizeof(*up), &offp)) == NULL) {
62233965Sjdp			pfsyncstats.pfsyncs_badlen++;
62333965Sjdp			return;
62433965Sjdp		}
62533965Sjdp
62633965Sjdp		s = splsoftnet();
62733965Sjdp#ifdef __FreeBSD__
62833965Sjdp		PF_LOCK();
62933965Sjdp#endif
63033965Sjdp		for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp);
63133965Sjdp		    i < count; i++, up++) {
63233965Sjdp			/* check for invalid values */
63333965Sjdp			if (up->timeout >= PFTM_MAX ||
63433965Sjdp			    up->src.state > PF_TCPS_PROXY_DST ||
63533965Sjdp			    up->dst.state > PF_TCPS_PROXY_DST) {
63633965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
63733965Sjdp					printf("pfsync_insert: "
63833965Sjdp					    "PFSYNC_ACT_UPD_C: "
63933965Sjdp					    "invalid value\n");
64033965Sjdp				pfsyncstats.pfsyncs_badstate++;
64133965Sjdp				continue;
64233965Sjdp			}
64333965Sjdp
64433965Sjdp			bcopy(up->id, &key.id, sizeof(key.id));
64533965Sjdp			key.creatorid = up->creatorid;
64633965Sjdp
64733965Sjdp			st = pf_find_state_byid(&key);
64833965Sjdp			if (st == NULL) {
64933965Sjdp				/* We don't have this state. Ask for it. */
65033965Sjdp				pfsync_request_update(up, &src);
65133965Sjdp				update_requested = 1;
65233965Sjdp				pfsyncstats.pfsyncs_badstate++;
65333965Sjdp				continue;
65433965Sjdp			}
65533965Sjdp			pf_state_peer_ntoh(&up->src, &st->src);
65633965Sjdp			pf_state_peer_ntoh(&up->dst, &st->dst);
65733965Sjdp#ifdef __FreeBSD__
65833965Sjdp			st->expire = ntohl(up->expire) + time_second;
65933965Sjdp#else
66033965Sjdp			st->expire = ntohl(up->expire) + time.tv_sec;
66133965Sjdp#endif
66233965Sjdp			st->timeout = up->timeout;
66333965Sjdp		}
66460484Sobrien		if (update_requested)
66560484Sobrien			pfsync_sendout(sc);
66633965Sjdp#ifdef __FreeBSD__
66733965Sjdp		PF_UNLOCK();
66860484Sobrien#endif
66960484Sobrien		splx(s);
67060484Sobrien		break;
67160484Sobrien	}
67233965Sjdp	case PFSYNC_ACT_DEL_C:
67360484Sobrien		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
67433965Sjdp		    count * sizeof(*dp), &offp)) == NULL) {
67533965Sjdp			pfsyncstats.pfsyncs_badlen++;
67660484Sobrien			return;
67760484Sobrien		}
67860484Sobrien
67960484Sobrien		s = splsoftnet();
68060484Sobrien#ifdef __FreeBSD__
68160484Sobrien		PF_LOCK();
68260484Sobrien#endif
68360484Sobrien		for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp);
68460484Sobrien		    i < count; i++, dp++) {
68560484Sobrien			bcopy(dp->id, &key.id, sizeof(key.id));
68660484Sobrien			key.creatorid = dp->creatorid;
68760484Sobrien
68860484Sobrien			st = pf_find_state_byid(&key);
68960484Sobrien			if (st == NULL) {
69060484Sobrien				pfsyncstats.pfsyncs_badstate++;
69160484Sobrien				continue;
69260484Sobrien			}
69360484Sobrien			/*
69460484Sobrien			 * XXX
69560484Sobrien			 * pf_purge_expired_states() is expensive,
69660484Sobrien			 * we really want to purge the state directly.
69760484Sobrien			 */
69860484Sobrien			st->timeout = PFTM_PURGE;
69960484Sobrien			st->sync_flags |= PFSTATE_FROMSYNC;
70060484Sobrien		}
70160484Sobrien		pf_purge_expired_states();
70260484Sobrien#ifdef __FreeBSD__
70360484Sobrien		PF_UNLOCK();
70460484Sobrien#endif
70560484Sobrien		splx(s);
70660484Sobrien		break;
70760484Sobrien	case PFSYNC_ACT_INS_F:
70860484Sobrien	case PFSYNC_ACT_DEL_F:
70960484Sobrien		/* not implemented */
71060484Sobrien		break;
71160484Sobrien	case PFSYNC_ACT_UREQ:
71260484Sobrien		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
71360484Sobrien		    count * sizeof(*rup), &offp)) == NULL) {
71433965Sjdp			pfsyncstats.pfsyncs_badlen++;
71533965Sjdp			return;
71633965Sjdp		}
71733965Sjdp
71833965Sjdp		s = splsoftnet();
71933965Sjdp		/* XXX send existing. pfsync_pack_state should handle this. */
72033965Sjdp#ifdef __FreeBSD__
72133965Sjdp		PF_LOCK();
72233965Sjdp#endif
72333965Sjdp		if (sc->sc_mbuf != NULL)
72433965Sjdp			pfsync_sendout(sc);
72533965Sjdp		for (i = 0,
72633965Sjdp		    rup = (struct pfsync_state_upd_req *)(mp->m_data + offp);
72733965Sjdp		    i < count; i++, rup++) {
72833965Sjdp			bcopy(rup->id, &key.id, sizeof(key.id));
72933965Sjdp			key.creatorid = rup->creatorid;
73033965Sjdp
73133965Sjdp			if (key.id == 0 && key.creatorid == 0) {
73233965Sjdp#ifdef __FreeBSD__
73333965Sjdp				sc->sc_ureq_received = time_uptime;
73433965Sjdp#else
73533965Sjdp				sc->sc_ureq_received = mono_time.tv_sec;
73633965Sjdp#endif
73733965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
73833965Sjdp					printf("pfsync: received "
73933965Sjdp					    "bulk update request\n");
74033965Sjdp				pfsync_send_bus(sc, PFSYNC_BUS_START);
74133965Sjdp#ifdef __FreeBSD__
74233965Sjdp				callout_reset(&sc->sc_bulk_tmo, 1 * hz,
74333965Sjdp				    pfsync_bulk_update,
74433965Sjdp				    LIST_FIRST(&pfsync_list));
74533965Sjdp#else
74633965Sjdp				timeout_add(&sc->sc_bulk_tmo, 1 * hz);
74733965Sjdp#endif
74833965Sjdp			} else {
74933965Sjdp				st = pf_find_state_byid(&key);
75033965Sjdp				if (st == NULL) {
75133965Sjdp					pfsyncstats.pfsyncs_badstate++;
75233965Sjdp					continue;
75333965Sjdp				}
75433965Sjdp				pfsync_pack_state(PFSYNC_ACT_UPD, st, 0);
75533965Sjdp			}
75633965Sjdp		}
75733965Sjdp		if (sc->sc_mbuf != NULL)
75833965Sjdp			pfsync_sendout(sc);
75933965Sjdp#ifdef __FreeBSD__
76033965Sjdp		PF_UNLOCK();
76133965Sjdp#endif
76233965Sjdp		splx(s);
76333965Sjdp		break;
76433965Sjdp	case PFSYNC_ACT_BUS:
76533965Sjdp		/* If we're not waiting for a bulk update, who cares. */
76633965Sjdp		if (sc->sc_ureq_sent == 0)
76733965Sjdp			break;
76833965Sjdp
76933965Sjdp		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
77033965Sjdp		    sizeof(*bus), &offp)) == NULL) {
77133965Sjdp			pfsyncstats.pfsyncs_badlen++;
77233965Sjdp			return;
77333965Sjdp		}
77433965Sjdp		bus = (struct pfsync_state_bus *)(mp->m_data + offp);
77533965Sjdp		switch (bus->status) {
77633965Sjdp		case PFSYNC_BUS_START:
77733965Sjdp#ifdef __FreeBSD__
77833965Sjdp			callout_reset(&sc->sc_bulkfail_tmo,
77933965Sjdp			    pf_pool_limits[PF_LIMIT_STATES].limit /
78033965Sjdp			    (PFSYNC_BULKPACKETS * sc->sc_maxcount),
78133965Sjdp			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
78233965Sjdp#else
78333965Sjdp			timeout_add(&sc->sc_bulkfail_tmo,
78433965Sjdp			    pf_pool_limits[PF_LIMIT_STATES].limit /
78533965Sjdp			    (PFSYNC_BULKPACKETS * sc->sc_maxcount));
78633965Sjdp#endif
78733965Sjdp			if (pf_status.debug >= PF_DEBUG_MISC)
78833965Sjdp				printf("pfsync: received bulk "
78933965Sjdp				    "update start\n");
79033965Sjdp			break;
79133965Sjdp		case PFSYNC_BUS_END:
79233965Sjdp#ifdef __FreeBSD__
79333965Sjdp			if (time_uptime - ntohl(bus->endtime) >=
79433965Sjdp#else
79533965Sjdp			if (mono_time.tv_sec - ntohl(bus->endtime) >=
79633965Sjdp#endif
79733965Sjdp			    sc->sc_ureq_sent) {
79833965Sjdp				/* that's it, we're happy */
79933965Sjdp				sc->sc_ureq_sent = 0;
80033965Sjdp				sc->sc_bulk_tries = 0;
80133965Sjdp#ifdef __FreeBSD__
80233965Sjdp				callout_stop(&sc->sc_bulkfail_tmo);
80333965Sjdp#else
80433965Sjdp				timeout_del(&sc->sc_bulkfail_tmo);
80533965Sjdp#endif
80633965Sjdp				pfsync_sync_ok = 1;
80733965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
80833965Sjdp					printf("pfsync: received valid "
80933965Sjdp					    "bulk update end\n");
81033965Sjdp			} else {
81133965Sjdp				if (pf_status.debug >= PF_DEBUG_MISC)
81233965Sjdp					printf("pfsync: received invalid "
81333965Sjdp					    "bulk update end: bad timestamp\n");
81433965Sjdp			}
81533965Sjdp			break;
81633965Sjdp		}
81733965Sjdp		break;
81833965Sjdp	}
81933965Sjdp
82033965Sjdpdone:
82133965Sjdp	if (m)
82233965Sjdp		m_freem(m);
82333965Sjdp}
82433965Sjdp
82533965Sjdpint
82633965Sjdppfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
82733965Sjdp	struct rtentry *rt)
82833965Sjdp{
82933965Sjdp	m_freem(m);
83033965Sjdp	return (0);
83133965Sjdp}
83233965Sjdp
83333965Sjdp/* ARGSUSED */
83433965Sjdpint
83533965Sjdppfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
83633965Sjdp{
83733965Sjdp#ifndef __FreeBSD__
83833965Sjdp	struct proc *p = curproc;
83933965Sjdp#endif
84033965Sjdp	struct pfsync_softc *sc = ifp->if_softc;
84133965Sjdp	struct ifreq *ifr = (struct ifreq *)data;
84233965Sjdp	struct ip_moptions *imo = &sc->sc_imo;
84333965Sjdp	struct pfsyncreq pfsyncr;
84433965Sjdp	struct ifnet    *sifp;
84533965Sjdp	int s, error;
84633965Sjdp
84733965Sjdp	switch (cmd) {
84833965Sjdp	case SIOCSIFADDR:
84933965Sjdp	case SIOCAIFADDR:
85033965Sjdp	case SIOCSIFDSTADDR:
85133965Sjdp	case SIOCSIFFLAGS:
85233965Sjdp		if (ifp->if_flags & IFF_UP)
85333965Sjdp			ifp->if_flags |= IFF_RUNNING;
85433965Sjdp		else
85533965Sjdp			ifp->if_flags &= ~IFF_RUNNING;
85633965Sjdp		break;
85733965Sjdp	case SIOCSIFMTU:
85833965Sjdp		if (ifr->ifr_mtu < PFSYNC_MINMTU)
85933965Sjdp			return (EINVAL);
86033965Sjdp		if (ifr->ifr_mtu > MCLBYTES)
86133965Sjdp			ifr->ifr_mtu = MCLBYTES;
86233965Sjdp		s = splnet();
86333965Sjdp#ifdef __FreeBSD__
86433965Sjdp		PF_LOCK();
86533965Sjdp#endif
86633965Sjdp		if (ifr->ifr_mtu < ifp->if_mtu) {
86733965Sjdp			pfsync_sendout(sc);
86833965Sjdp		}
86933965Sjdp		pfsync_setmtu(sc, ifr->ifr_mtu);
87033965Sjdp#ifdef __FreeBSD__
87133965Sjdp		PF_UNLOCK();
87233965Sjdp#endif
87333965Sjdp		splx(s);
87433965Sjdp		break;
87533965Sjdp	case SIOCGETPFSYNC:
87633965Sjdp#ifdef __FreeBSD__
87733965Sjdp		/* XXX: read unlocked */
87833965Sjdp#endif
87933965Sjdp		bzero(&pfsyncr, sizeof(pfsyncr));
88033965Sjdp		if (sc->sc_sync_ifp)
88133965Sjdp			strlcpy(pfsyncr.pfsyncr_syncif,
88233965Sjdp			    sc->sc_sync_ifp->if_xname, IFNAMSIZ);
88333965Sjdp		pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
88433965Sjdp		if ((error = copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))))
88533965Sjdp			return (error);
88633965Sjdp		break;
88733965Sjdp	case SIOCSETPFSYNC:
88833965Sjdp#ifdef __FreeBSD__
88933965Sjdp		if ((error = suser(curthread)) != 0)
89033965Sjdp#else
89133965Sjdp		if ((error = suser(p, p->p_acflag)) != 0)
89233965Sjdp#endif
89333965Sjdp			return (error);
89433965Sjdp		if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr))))
89533965Sjdp			return (error);
89633965Sjdp
89733965Sjdp		if (pfsyncr.pfsyncr_maxupdates > 255)
89833965Sjdp			return (EINVAL);
89938889Sjdp#ifdef __FreeBSD__
90033965Sjdp		PF_LOCK();
90133965Sjdp#endif
90233965Sjdp		sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
90333965Sjdp
90433965Sjdp		if (pfsyncr.pfsyncr_syncif[0] == 0) {
90533965Sjdp			sc->sc_sync_ifp = NULL;
90633965Sjdp			if (sc->sc_mbuf_net != NULL) {
90733965Sjdp				/* Don't keep stale pfsync packets around. */
90833965Sjdp				s = splnet();
90933965Sjdp				m_freem(sc->sc_mbuf_net);
91033965Sjdp				sc->sc_mbuf_net = NULL;
91133965Sjdp				sc->sc_statep_net.s = NULL;
91233965Sjdp				splx(s);
91333965Sjdp			}
91433965Sjdp#ifdef __FreeBSD__
91533965Sjdp			PF_UNLOCK();
91633965Sjdp#endif
91733965Sjdp			break;
91833965Sjdp		}
91938889Sjdp		if ((sifp = ifunit(pfsyncr.pfsyncr_syncif)) == NULL) {
92033965Sjdp#ifdef __FreeBSD__
92133965Sjdp			PF_UNLOCK();
92233965Sjdp#endif
92333965Sjdp			return (EINVAL);
92433965Sjdp		}
92533965Sjdp		else if (sifp == sc->sc_sync_ifp) {
92633965Sjdp#ifdef __FreeBSD__
92733965Sjdp			PF_UNLOCK();
92833965Sjdp#endif
92933965Sjdp			break;
93033965Sjdp		}
93133965Sjdp
93233965Sjdp		s = splnet();
93333965Sjdp		if (sifp->if_mtu < sc->sc_if.if_mtu ||
93433965Sjdp		    (sc->sc_sync_ifp != NULL &&
93533965Sjdp		    sifp->if_mtu < sc->sc_sync_ifp->if_mtu) ||
93633965Sjdp		    sifp->if_mtu < MCLBYTES - sizeof(struct ip))
93733965Sjdp			pfsync_sendout(sc);
93833965Sjdp		sc->sc_sync_ifp = sifp;
93933965Sjdp
94038889Sjdp		pfsync_setmtu(sc, sc->sc_if.if_mtu);
94133965Sjdp
94233965Sjdp		if (imo->imo_num_memberships > 0) {
94333965Sjdp			in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
94433965Sjdp			imo->imo_multicast_ifp = NULL;
94533965Sjdp		}
94633965Sjdp
94733965Sjdp		if (sc->sc_sync_ifp) {
94833965Sjdp			struct in_addr addr;
94933965Sjdp
95033965Sjdp#ifdef __FreeBSD__
95133965Sjdp			PF_UNLOCK();		/* addmulti mallocs w/ WAITOK */
95233965Sjdp			addr.s_addr = htonl(INADDR_PFSYNC_GROUP);
95333965Sjdp#else
95433965Sjdp			addr.s_addr = INADDR_PFSYNC_GROUP;
95533965Sjdp#endif
95633965Sjdp			if ((imo->imo_membership[0] =
95733965Sjdp			    in_addmulti(&addr, sc->sc_sync_ifp)) == NULL) {
95833965Sjdp				splx(s);
95933965Sjdp				return (ENOBUFS);
96033965Sjdp			}
96133965Sjdp			imo->imo_num_memberships++;
96233965Sjdp			imo->imo_multicast_ifp = sc->sc_sync_ifp;
96333965Sjdp			imo->imo_multicast_ttl = PFSYNC_DFLTTL;
96433965Sjdp			imo->imo_multicast_loop = 0;
96533965Sjdp
96638889Sjdp			/* Request a full state table update. */
96733965Sjdp#ifdef __FreeBSD__
96833965Sjdp			PF_LOCK();
96933965Sjdp			sc->sc_ureq_sent = time_uptime;
97033965Sjdp#else
97133965Sjdp			sc->sc_ureq_sent = mono_time.tv_sec;
97233965Sjdp#endif
97333965Sjdp			pfsync_sync_ok = 0;
97433965Sjdp			if (pf_status.debug >= PF_DEBUG_MISC)
97533965Sjdp				printf("pfsync: requesting bulk update\n");
97633965Sjdp#ifdef __FreeBSD__
97733965Sjdp			callout_reset(&sc->sc_bulkfail_tmo, 5 * hz,
97833965Sjdp			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
97933965Sjdp#else
98033965Sjdp			timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
98133965Sjdp#endif
98233965Sjdp			pfsync_request_update(NULL, NULL);
98333965Sjdp			pfsync_sendout(sc);
98433965Sjdp		}
98533965Sjdp#ifdef __FreeBSD__
98633965Sjdp		PF_UNLOCK();
98733965Sjdp#endif
98833965Sjdp		splx(s);
98938889Sjdp
99033965Sjdp		break;
99133965Sjdp
99233965Sjdp	default:
99333965Sjdp		return (ENOTTY);
99433965Sjdp	}
99533965Sjdp
99633965Sjdp	return (0);
99733965Sjdp}
99833965Sjdp
99933965Sjdpvoid
100033965Sjdppfsync_setmtu(struct pfsync_softc *sc, int mtu_req)
100133965Sjdp{
100233965Sjdp	int mtu;
100333965Sjdp
100433965Sjdp	if (sc->sc_sync_ifp && sc->sc_sync_ifp->if_mtu < mtu_req)
100533965Sjdp		mtu = sc->sc_sync_ifp->if_mtu;
100633965Sjdp	else
100733965Sjdp		mtu = mtu_req;
100833965Sjdp
100933965Sjdp	sc->sc_maxcount = (mtu - sizeof(struct pfsync_header)) /
101033965Sjdp	    sizeof(struct pfsync_state);
101133965Sjdp	if (sc->sc_maxcount > 254)
101233965Sjdp	    sc->sc_maxcount = 254;
101333965Sjdp	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
101433965Sjdp	    sc->sc_maxcount * sizeof(struct pfsync_state);
101538889Sjdp}
101633965Sjdp
101733965Sjdpstruct mbuf *
101833965Sjdppfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp)
101933965Sjdp{
102033965Sjdp	struct pfsync_header *h;
102133965Sjdp	struct mbuf *m;
102233965Sjdp	int len;
102333965Sjdp
102433965Sjdp#ifdef __FreeBSD__
102533965Sjdp	PF_ASSERT(MA_OWNED);
102633965Sjdp#endif
102733965Sjdp	MGETHDR(m, M_DONTWAIT, MT_DATA);
102833965Sjdp	if (m == NULL) {
102933965Sjdp		sc->sc_if.if_oerrors++;
103033965Sjdp		return (NULL);
103133965Sjdp	}
103233965Sjdp
103333965Sjdp	switch (action) {
103433965Sjdp	case PFSYNC_ACT_CLR:
103533965Sjdp		len = sizeof(struct pfsync_header) +
103633965Sjdp		    sizeof(struct pfsync_state_clr);
103733965Sjdp		break;
103833965Sjdp	case PFSYNC_ACT_UPD_C:
103933965Sjdp		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd)) +
104033965Sjdp		    sizeof(struct pfsync_header);
104133965Sjdp		break;
104233965Sjdp	case PFSYNC_ACT_DEL_C:
104333965Sjdp		len = (sc->sc_maxcount * sizeof(struct pfsync_state_del)) +
104433965Sjdp		    sizeof(struct pfsync_header);
104560484Sobrien		break;
104660484Sobrien	case PFSYNC_ACT_UREQ:
1047130561Sobrien		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd_req)) +
1048130561Sobrien		    sizeof(struct pfsync_header);
104933965Sjdp		break;
105060484Sobrien	case PFSYNC_ACT_BUS:
105133965Sjdp		len = sizeof(struct pfsync_header) +
105260484Sobrien		    sizeof(struct pfsync_state_bus);
105389857Sobrien		break;
105433965Sjdp	default:
105533965Sjdp		len = (sc->sc_maxcount * sizeof(struct pfsync_state)) +
105633965Sjdp		    sizeof(struct pfsync_header);
105760484Sobrien		break;
105833965Sjdp	}
105933965Sjdp
106033965Sjdp	if (len > MHLEN) {
106133965Sjdp		MCLGET(m, M_DONTWAIT);
106233965Sjdp		if ((m->m_flags & M_EXT) == 0) {
106333965Sjdp			m_free(m);
106433965Sjdp			sc->sc_if.if_oerrors++;
106533965Sjdp			return (NULL);
106633965Sjdp		}
106791041Sobrien		m->m_data += (MCLBYTES - len) &~ (sizeof(long) - 1);
106891041Sobrien	} else
106933965Sjdp		MH_ALIGN(m, len);
107033965Sjdp
107191041Sobrien	m->m_pkthdr.rcvif = NULL;
107291041Sobrien	m->m_pkthdr.len = m->m_len = sizeof(struct pfsync_header);
107333965Sjdp	h = mtod(m, struct pfsync_header *);
107433965Sjdp	h->version = PFSYNC_VERSION;
107533965Sjdp	h->af = 0;
107633965Sjdp	h->count = 0;
107733965Sjdp	h->action = action;
107833965Sjdp
107960484Sobrien	*sp = (void *)((char *)h + PFSYNC_HDRLEN);
108033965Sjdp#ifdef __FreeBSD__
108133965Sjdp	callout_reset(&sc->sc_tmo, hz, pfsync_timeout,
108260484Sobrien	    LIST_FIRST(&pfsync_list));
108333965Sjdp#else
108433965Sjdp	timeout_add(&sc->sc_tmo, hz);
108533965Sjdp#endif
108633965Sjdp	return (m);
108733965Sjdp}
108833965Sjdp
108933965Sjdpint
109033965Sjdppfsync_pack_state(u_int8_t action, struct pf_state *st, int compress)
109133965Sjdp{
109233965Sjdp#ifdef __FreeBSD__
109333965Sjdp	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
109460484Sobrien#else
109533965Sjdp	struct ifnet *ifp = &pfsyncif.sc_if;
109633965Sjdp#endif
109733965Sjdp	struct pfsync_softc *sc = ifp->if_softc;
109833965Sjdp	struct pfsync_header *h, *h_net;
109933965Sjdp	struct pfsync_state *sp = NULL;
110033965Sjdp	struct pfsync_state_upd *up = NULL;
110133965Sjdp	struct pfsync_state_del *dp = NULL;
110233965Sjdp	struct pf_rule *r;
110333965Sjdp	u_long secs;
110433965Sjdp	int s, ret = 0;
110533965Sjdp	u_int8_t i = 255, newaction = 0;
110633965Sjdp
110733965Sjdp#ifdef __FreeBSD__
110833965Sjdp	PF_ASSERT(MA_OWNED);
110933965Sjdp#endif
111033965Sjdp	/*
111133965Sjdp	 * If a packet falls in the forest and there's nobody around to
111233965Sjdp	 * hear, does it make a sound?
111333965Sjdp	 */
111433965Sjdp	if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL) {
111560484Sobrien		/* Don't leave any stale pfsync packets hanging around. */
111633965Sjdp		if (sc->sc_mbuf != NULL) {
111733965Sjdp			m_freem(sc->sc_mbuf);
111860484Sobrien			sc->sc_mbuf = NULL;
111960484Sobrien			sc->sc_statep.s = NULL;
112033965Sjdp		}
112133965Sjdp		return (0);
112233965Sjdp	}
112333965Sjdp
112433965Sjdp	if (action >= PFSYNC_ACT_MAX)
112533965Sjdp		return (EINVAL);
112633965Sjdp
112733965Sjdp	s = splnet();
112833965Sjdp	if (sc->sc_mbuf == NULL) {
112960484Sobrien		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
113033965Sjdp		    (void *)&sc->sc_statep.s)) == NULL) {
113133965Sjdp			splx(s);
113233965Sjdp			return (ENOMEM);
113333965Sjdp		}
113433965Sjdp		h = mtod(sc->sc_mbuf, struct pfsync_header *);
113533965Sjdp	} else {
113633965Sjdp		h = mtod(sc->sc_mbuf, struct pfsync_header *);
113733965Sjdp		if (h->action != action) {
113833965Sjdp			pfsync_sendout(sc);
113933965Sjdp			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
114033965Sjdp			    (void *)&sc->sc_statep.s)) == NULL) {
114133965Sjdp				splx(s);
114260484Sobrien				return (ENOMEM);
114333965Sjdp			}
114433965Sjdp			h = mtod(sc->sc_mbuf, struct pfsync_header *);
114560484Sobrien		} else {
114633965Sjdp			/*
114733965Sjdp			 * If it's an update, look in the packet to see if
114833965Sjdp			 * we already have an update for the state.
114933965Sjdp			 */
115033965Sjdp			if (action == PFSYNC_ACT_UPD && sc->sc_maxupdates) {
115133965Sjdp				struct pfsync_state *usp =
115233965Sjdp				    (void *)((char *)h + PFSYNC_HDRLEN);
115333965Sjdp
115433965Sjdp				for (i = 0; i < h->count; i++) {
115533965Sjdp					if (!memcmp(usp->id, &st->id,
115633965Sjdp					    PFSYNC_ID_LEN) &&
115760484Sobrien					    usp->creatorid == st->creatorid) {
115833965Sjdp						sp = usp;
115933965Sjdp						sp->updates++;
116033965Sjdp						break;
116133965Sjdp					}
116233965Sjdp					usp++;
116333965Sjdp				}
116433965Sjdp			}
116533965Sjdp		}
116633965Sjdp	}
116733965Sjdp
116833965Sjdp#ifdef __FreeBSD__
116933965Sjdp	secs = time_second;
117033965Sjdp
117133965Sjdp	st->pfsync_time = time_uptime;
117233965Sjdp#else
117333965Sjdp	secs = time.tv_sec;
117433965Sjdp
117533965Sjdp	st->pfsync_time = mono_time.tv_sec;
117633965Sjdp#endif
117733965Sjdp	TAILQ_REMOVE(&state_updates, st, u.s.entry_updates);
117860484Sobrien	TAILQ_INSERT_TAIL(&state_updates, st, u.s.entry_updates);
117933965Sjdp
118033965Sjdp	if (sp == NULL) {
118160484Sobrien		/* not a "duplicate" update */
118260484Sobrien		i = 255;
118333965Sjdp		sp = sc->sc_statep.s++;
118433965Sjdp		sc->sc_mbuf->m_pkthdr.len =
118533965Sjdp		    sc->sc_mbuf->m_len += sizeof(struct pfsync_state);
118633965Sjdp		h->count++;
118733965Sjdp		bzero(sp, sizeof(*sp));
118833965Sjdp
118933965Sjdp		bcopy(&st->id, sp->id, sizeof(sp->id));
119033965Sjdp		sp->creatorid = st->creatorid;
119133965Sjdp
119260484Sobrien		strlcpy(sp->ifname, st->u.s.kif->pfik_name, sizeof(sp->ifname));
119333965Sjdp		pf_state_host_hton(&st->lan, &sp->lan);
119433965Sjdp		pf_state_host_hton(&st->gwy, &sp->gwy);
119533965Sjdp		pf_state_host_hton(&st->ext, &sp->ext);
119633965Sjdp
119733965Sjdp		bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
119833965Sjdp
119933965Sjdp		sp->creation = htonl(secs - st->creation);
120033965Sjdp		sp->packets[0] = htonl(st->packets[0]);
120133965Sjdp		sp->packets[1] = htonl(st->packets[1]);
120233965Sjdp		sp->bytes[0] = htonl(st->bytes[0]);
120333965Sjdp		sp->bytes[1] = htonl(st->bytes[1]);
120433965Sjdp		if ((r = st->rule.ptr) == NULL)
120533965Sjdp			sp->rule = htonl(-1);
120633965Sjdp		else
120733965Sjdp			sp->rule = htonl(r->nr);
120833965Sjdp		if ((r = st->anchor.ptr) == NULL)
120933965Sjdp			sp->anchor = htonl(-1);
121033965Sjdp		else
121133965Sjdp			sp->anchor = htonl(r->nr);
121233965Sjdp		sp->af = st->af;
121333965Sjdp		sp->proto = st->proto;
121433965Sjdp		sp->direction = st->direction;
121533965Sjdp		sp->log = st->log;
121633965Sjdp		sp->allow_opts = st->allow_opts;
121733965Sjdp		sp->timeout = st->timeout;
121833965Sjdp
121933965Sjdp		sp->sync_flags = st->sync_flags & PFSTATE_NOSYNC;
122033965Sjdp	}
122133965Sjdp
122233965Sjdp	pf_state_peer_hton(&st->src, &sp->src);
122333965Sjdp	pf_state_peer_hton(&st->dst, &sp->dst);
122433965Sjdp
122533965Sjdp	if (st->expire <= secs)
122633965Sjdp		sp->expire = htonl(0);
122733965Sjdp	else
122833965Sjdp		sp->expire = htonl(st->expire - secs);
122933965Sjdp
123033965Sjdp	/* do we need to build "compressed" actions for network transfer? */
123133965Sjdp	if (sc->sc_sync_ifp && compress) {
123233965Sjdp		switch (action) {
123333965Sjdp		case PFSYNC_ACT_UPD:
123433965Sjdp			newaction = PFSYNC_ACT_UPD_C;
123533965Sjdp			break;
123633965Sjdp		case PFSYNC_ACT_DEL:
123733965Sjdp			newaction = PFSYNC_ACT_DEL_C;
123833965Sjdp			break;
123933965Sjdp		default:
124033965Sjdp			/* by default we just send the uncompressed states */
124133965Sjdp			break;
124233965Sjdp		}
124333965Sjdp	}
124433965Sjdp
124533965Sjdp	if (newaction) {
124633965Sjdp		if (sc->sc_mbuf_net == NULL) {
124733965Sjdp			if ((sc->sc_mbuf_net = pfsync_get_mbuf(sc, newaction,
124833965Sjdp			    (void *)&sc->sc_statep_net.s)) == NULL) {
124933965Sjdp				splx(s);
125033965Sjdp				return (ENOMEM);
125133965Sjdp			}
125233965Sjdp		}
125333965Sjdp		h_net = mtod(sc->sc_mbuf_net, struct pfsync_header *);
125433965Sjdp
125533965Sjdp		switch (newaction) {
125633965Sjdp		case PFSYNC_ACT_UPD_C:
125733965Sjdp			if (i != 255) {
125833965Sjdp				up = (void *)((char *)h_net +
125933965Sjdp				    PFSYNC_HDRLEN + (i * sizeof(*up)));
126033965Sjdp				up->updates++;
126133965Sjdp			} else {
126233965Sjdp				h_net->count++;
126333965Sjdp				sc->sc_mbuf_net->m_pkthdr.len =
126433965Sjdp				    sc->sc_mbuf_net->m_len += sizeof(*up);
126533965Sjdp				up = sc->sc_statep_net.u++;
126633965Sjdp
126733965Sjdp				bzero(up, sizeof(*up));
126833965Sjdp				bcopy(&st->id, up->id, sizeof(up->id));
126933965Sjdp				up->creatorid = st->creatorid;
127033965Sjdp			}
127133965Sjdp			up->timeout = st->timeout;
127233965Sjdp			up->expire = sp->expire;
127333965Sjdp			up->src = sp->src;
127433965Sjdp			up->dst = sp->dst;
127533965Sjdp			break;
127633965Sjdp		case PFSYNC_ACT_DEL_C:
127733965Sjdp			sc->sc_mbuf_net->m_pkthdr.len =
127833965Sjdp			    sc->sc_mbuf_net->m_len += sizeof(*dp);
127933965Sjdp			dp = sc->sc_statep_net.d++;
128033965Sjdp			h_net->count++;
128133965Sjdp
128233965Sjdp			bzero(dp, sizeof(*dp));
128333965Sjdp			bcopy(&st->id, dp->id, sizeof(dp->id));
128433965Sjdp			dp->creatorid = st->creatorid;
128533965Sjdp			break;
128633965Sjdp		}
128733965Sjdp	}
128833965Sjdp
128933965Sjdp	if (h->count == sc->sc_maxcount ||
129033965Sjdp	    (sc->sc_maxupdates && (sp->updates >= sc->sc_maxupdates)))
129133965Sjdp		ret = pfsync_sendout(sc);
129233965Sjdp
129333965Sjdp	splx(s);
129433965Sjdp	return (ret);
129533965Sjdp}
129633965Sjdp
129733965Sjdp/* This must be called in splnet() */
129833965Sjdpint
129933965Sjdppfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src)
130033965Sjdp{
130133965Sjdp#ifdef __FreeBSD__
130233965Sjdp	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
130333965Sjdp#else
130433965Sjdp	struct ifnet *ifp = &pfsyncif.sc_if;
130533965Sjdp#endif
130633965Sjdp	struct pfsync_header *h;
130733965Sjdp	struct pfsync_softc *sc = ifp->if_softc;
130833965Sjdp	struct pfsync_state_upd_req *rup;
130933965Sjdp	int s = 0, ret = 0;	/* make the compiler happy */
131033965Sjdp
131133965Sjdp#ifdef __FreeBSD__
131233965Sjdp	PF_ASSERT(MA_OWNED);
131333965Sjdp#endif
131433965Sjdp	if (sc->sc_mbuf == NULL) {
131533965Sjdp		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
131660484Sobrien		    (void *)&sc->sc_statep.s)) == NULL) {
131733965Sjdp			splx(s);
131833965Sjdp			return (ENOMEM);
131933965Sjdp		}
132033965Sjdp		h = mtod(sc->sc_mbuf, struct pfsync_header *);
132133965Sjdp	} else {
132233965Sjdp		h = mtod(sc->sc_mbuf, struct pfsync_header *);
132333965Sjdp		if (h->action != PFSYNC_ACT_UREQ) {
132433965Sjdp			pfsync_sendout(sc);
132533965Sjdp			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
132633965Sjdp			    (void *)&sc->sc_statep.s)) == NULL) {
132738889Sjdp				splx(s);
132860484Sobrien				return (ENOMEM);
132960484Sobrien			}
133060484Sobrien			h = mtod(sc->sc_mbuf, struct pfsync_header *);
133160484Sobrien		}
133260484Sobrien	}
133360484Sobrien
133460484Sobrien	if (src != NULL)
133560484Sobrien		sc->sc_sendaddr = *src;
133660484Sobrien	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*rup);
133760484Sobrien	h->count++;
133833965Sjdp	rup = sc->sc_statep.r++;
133933965Sjdp	bzero(rup, sizeof(*rup));
134033965Sjdp	if (up != NULL) {
134133965Sjdp		bcopy(up->id, rup->id, sizeof(rup->id));
134260484Sobrien		rup->creatorid = up->creatorid;
134333965Sjdp	}
134433965Sjdp
134560484Sobrien	if (h->count == sc->sc_maxcount)
134633965Sjdp		ret = pfsync_sendout(sc);
134733965Sjdp
134833965Sjdp	return (ret);
134933965Sjdp}
135033965Sjdp
135133965Sjdpint
135233965Sjdppfsync_clear_states(u_int32_t creatorid, char *ifname)
135360484Sobrien{
135433965Sjdp#ifdef __FreeBSD__
135533965Sjdp	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
135633965Sjdp#else
135733965Sjdp	struct ifnet *ifp = &pfsyncif.sc_if;
135833965Sjdp#endif
135933965Sjdp	struct pfsync_softc *sc = ifp->if_softc;
136033965Sjdp	struct pfsync_state_clr *cp;
136133965Sjdp	int s, ret;
136233965Sjdp
136333965Sjdp	s = splnet();
136433965Sjdp#ifdef __FreeBSD__
136533965Sjdp	PF_ASSERT(MA_OWNED);
136633965Sjdp#endif
136733965Sjdp	if (sc->sc_mbuf != NULL)
136833965Sjdp		pfsync_sendout(sc);
136933965Sjdp	if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR,
137060484Sobrien	    (void *)&sc->sc_statep.c)) == NULL) {
137133965Sjdp		splx(s);
137233965Sjdp		return (ENOMEM);
137333965Sjdp	}
137433965Sjdp	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*cp);
137533965Sjdp	cp = sc->sc_statep.c;
137633965Sjdp	cp->creatorid = creatorid;
137760484Sobrien	if (ifname != NULL)
137833965Sjdp		strlcpy(cp->ifname, ifname, IFNAMSIZ);
137933965Sjdp
138060484Sobrien	ret = (pfsync_sendout(sc));
138133965Sjdp	splx(s);
138233965Sjdp	return (ret);
138333965Sjdp}
138433965Sjdp
138533965Sjdpvoid
138633965Sjdppfsync_timeout(void *v)
138733965Sjdp{
138860484Sobrien	struct pfsync_softc *sc = v;
138933965Sjdp	int s;
139033965Sjdp
139133965Sjdp	s = splnet();
139233965Sjdp#ifdef __FreeBSD__
139333965Sjdp	PF_LOCK();
139433965Sjdp#endif
139533965Sjdp	pfsync_sendout(sc);
139633965Sjdp#ifdef __FreeBSD__
139733965Sjdp	PF_UNLOCK();
139833965Sjdp#endif
139933965Sjdp	splx(s);
140033965Sjdp}
140133965Sjdp
140233965Sjdpvoid
140333965Sjdppfsync_send_bus(struct pfsync_softc *sc, u_int8_t status)
140433965Sjdp{
140533965Sjdp	struct pfsync_state_bus *bus;
140633965Sjdp
140760484Sobrien#ifdef __FreeBSD__
140833965Sjdp	PF_ASSERT(MA_OWNED);
140933965Sjdp#endif
141033965Sjdp	if (sc->sc_mbuf != NULL)
141133965Sjdp		pfsync_sendout(sc);
141233965Sjdp
141333965Sjdp	if (pfsync_sync_ok &&
141433965Sjdp	    (sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_BUS,
141533965Sjdp	    (void *)&sc->sc_statep.b)) != NULL) {
141633965Sjdp		sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*bus);
141733965Sjdp		bus = sc->sc_statep.b;
141833965Sjdp		bus->creatorid = pf_status.hostid;
141933965Sjdp		bus->status = status;
142033965Sjdp#ifdef __FreeBSD__
142133965Sjdp		bus->endtime = htonl(time_uptime - sc->sc_ureq_received);
142233965Sjdp#else
142333965Sjdp		bus->endtime = htonl(mono_time.tv_sec - sc->sc_ureq_received);
142433965Sjdp#endif
142533965Sjdp		pfsync_sendout(sc);
142633965Sjdp	}
142733965Sjdp}
142833965Sjdp
142933965Sjdpvoid
143033965Sjdppfsync_bulk_update(void *v)
143133965Sjdp{
143233965Sjdp	struct pfsync_softc *sc = v;
143333965Sjdp	int s, i = 0;
143433965Sjdp	struct pf_state *state;
143533965Sjdp
143633965Sjdp#ifdef __FreeBSD__
143733965Sjdp	PF_LOCK();
143833965Sjdp#endif
143933965Sjdp	s = splnet();
144033965Sjdp	if (sc->sc_mbuf != NULL)
144133965Sjdp		pfsync_sendout(sc);
144233965Sjdp
144333965Sjdp	/*
144433965Sjdp	 * Grab at most PFSYNC_BULKPACKETS worth of states which have not
144533965Sjdp	 * been sent since the latest request was made.
144633965Sjdp	 */
144733965Sjdp	while ((state = TAILQ_FIRST(&state_updates)) != NULL &&
144833965Sjdp	    ++i < (sc->sc_maxcount * PFSYNC_BULKPACKETS)) {
144933965Sjdp		if (state->pfsync_time > sc->sc_ureq_received) {
145033965Sjdp			/* we're done */
145133965Sjdp			pfsync_send_bus(sc, PFSYNC_BUS_END);
145233965Sjdp			sc->sc_ureq_received = 0;
145333965Sjdp#ifdef __FreeBSD__
145433965Sjdp			callout_stop(&sc->sc_bulk_tmo);
145533965Sjdp#else
145633965Sjdp			timeout_del(&sc->sc_bulk_tmo);
145733965Sjdp#endif
145833965Sjdp			if (pf_status.debug >= PF_DEBUG_MISC)
145933965Sjdp				printf("pfsync: bulk update complete\n");
146033965Sjdp			break;
146133965Sjdp		} else {
146233965Sjdp			/* send an update and move to end of list */
146333965Sjdp			if (!state->sync_flags)
146433965Sjdp				pfsync_pack_state(PFSYNC_ACT_UPD, state, 0);
146533965Sjdp#ifdef __FreeBSD__
146633965Sjdp			state->pfsync_time = time_uptime;
146733965Sjdp#else
146833965Sjdp			state->pfsync_time = mono_time.tv_sec;
146933965Sjdp#endif
147033965Sjdp			TAILQ_REMOVE(&state_updates, state, u.s.entry_updates);
147133965Sjdp			TAILQ_INSERT_TAIL(&state_updates, state,
147233965Sjdp			    u.s.entry_updates);
147333965Sjdp
147433965Sjdp			/* look again for more in a bit */
147533965Sjdp#ifdef __FreeBSD__
147633965Sjdp			callout_reset(&sc->sc_bulk_tmo, 1, pfsync_timeout,
147733965Sjdp			    LIST_FIRST(&pfsync_list));
147833965Sjdp#else
147933965Sjdp			timeout_add(&sc->sc_bulk_tmo, 1);
148033965Sjdp#endif
148133965Sjdp		}
148233965Sjdp	}
148333965Sjdp	if (sc->sc_mbuf != NULL)
148433965Sjdp		pfsync_sendout(sc);
148533965Sjdp	splx(s);
148633965Sjdp#ifdef __FreeBSD__
148733965Sjdp	PF_UNLOCK();
148833965Sjdp#endif
148933965Sjdp}
149033965Sjdp
149133965Sjdpvoid
149233965Sjdppfsync_bulkfail(void *v)
149333965Sjdp{
149433965Sjdp	struct pfsync_softc *sc = v;
149533965Sjdp
149633965Sjdp#ifdef __FreeBSD__
149733965Sjdp	PF_LOCK();
149860484Sobrien#endif
1499	if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) {
1500		/* Try again in a bit */
1501#ifdef __FreeBSD__
1502		callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulkfail,
1503		    LIST_FIRST(&pfsync_list));
1504#else
1505		timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
1506#endif
1507		pfsync_request_update(NULL, NULL);
1508		pfsync_sendout(sc);
1509	} else {
1510		/* Pretend like the transfer was ok */
1511		sc->sc_ureq_sent = 0;
1512		sc->sc_bulk_tries = 0;
1513		pfsync_sync_ok = 1;
1514		if (pf_status.debug >= PF_DEBUG_MISC)
1515			printf("pfsync: failed to receive "
1516			    "bulk update status\n");
1517#ifdef __FreeBSD__
1518		callout_stop(&sc->sc_bulkfail_tmo);
1519#else
1520		timeout_del(&sc->sc_bulkfail_tmo);
1521#endif
1522	}
1523#ifdef __FreeBSD__
1524	PF_UNLOCK();
1525#endif
1526}
1527
1528int
1529pfsync_sendout(sc)
1530	struct pfsync_softc *sc;
1531{
1532#if NBPFILTER > 0
1533	struct ifnet *ifp = &sc->sc_if;
1534#endif
1535	struct mbuf *m;
1536
1537#ifdef __FreeBSD__
1538	PF_ASSERT(MA_OWNED);
1539	callout_stop(&sc->sc_tmo);
1540#else
1541	timeout_del(&sc->sc_tmo);
1542#endif
1543
1544	if (sc->sc_mbuf == NULL)
1545		return (0);
1546	m = sc->sc_mbuf;
1547	sc->sc_mbuf = NULL;
1548	sc->sc_statep.s = NULL;
1549
1550#ifdef __FreeBSD__
1551	KASSERT(m != NULL, ("pfsync_sendout: null mbuf"));
1552#endif
1553#if NBPFILTER > 0
1554	if (ifp->if_bpf)
1555		bpf_mtap(ifp->if_bpf, m);
1556#endif
1557
1558	if (sc->sc_mbuf_net) {
1559		m_freem(m);
1560		m = sc->sc_mbuf_net;
1561		sc->sc_mbuf_net = NULL;
1562		sc->sc_statep_net.s = NULL;
1563	}
1564
1565	if (sc->sc_sync_ifp) {
1566		struct ip *ip;
1567		struct ifaddr *ifa;
1568		struct sockaddr sa;
1569
1570		M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
1571		if (m == NULL) {
1572			pfsyncstats.pfsyncs_onomem++;
1573			return (0);
1574		}
1575		ip = mtod(m, struct ip *);
1576		ip->ip_v = IPVERSION;
1577		ip->ip_hl = sizeof(*ip) >> 2;
1578		ip->ip_tos = IPTOS_LOWDELAY;
1579#ifdef __FreeBSD__
1580		ip->ip_len = m->m_pkthdr.len;
1581#else
1582		ip->ip_len = htons(m->m_pkthdr.len);
1583#endif
1584		ip->ip_id = htons(ip_randomid());
1585#ifdef __FreeBSD__
1586		ip->ip_off = IP_DF;
1587#else
1588		ip->ip_off = htons(IP_DF);
1589#endif
1590		ip->ip_ttl = PFSYNC_DFLTTL;
1591		ip->ip_p = IPPROTO_PFSYNC;
1592		ip->ip_sum = 0;
1593
1594		bzero(&sa, sizeof(sa));
1595		sa.sa_family = AF_INET;
1596		ifa = ifaof_ifpforaddr(&sa, sc->sc_sync_ifp);
1597		if (ifa == NULL)
1598			return (0);
1599		ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
1600
1601#ifdef __FreeBSD__
1602		if (sc->sc_sendaddr.s_addr == htonl(INADDR_PFSYNC_GROUP))
1603#else
1604		if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP)
1605#endif
1606			m->m_flags |= M_MCAST;
1607		ip->ip_dst = sc->sc_sendaddr;
1608#ifdef __FreeBSD__
1609		sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP);
1610#else
1611		sc->sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP;
1612#endif
1613
1614		pfsyncstats.pfsyncs_opackets++;
1615
1616#ifdef __FreeBSD__
1617		PF_UNLOCK();
1618#endif
1619		if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))
1620			pfsyncstats.pfsyncs_oerrors++;
1621
1622#ifdef __FreeBSD__
1623		PF_LOCK();
1624#endif
1625	} else
1626		m_freem(m);
1627
1628	return (0);
1629}
1630
1631
1632#ifdef __FreeBSD__
1633static int
1634pfsync_modevent(module_t mod, int type, void *data)
1635{
1636	int error = 0;
1637
1638	switch (type) {
1639	case MOD_LOAD:
1640		LIST_INIT(&pfsync_list);
1641		if_clone_attach(&pfsync_cloner);
1642		break;
1643
1644	case MOD_UNLOAD:
1645		if_clone_detach(&pfsync_cloner);
1646		while (!LIST_EMPTY(&pfsync_list))
1647			pfsync_clone_destroy(
1648				&LIST_FIRST(&pfsync_list)->sc_if);
1649		break;
1650
1651	default:
1652		error = EINVAL;
1653		break;
1654	}
1655
1656	return error;
1657}
1658
1659static moduledata_t pfsync_mod = {
1660	"pfsync",
1661	pfsync_modevent,
1662	0
1663};
1664
1665#define PFSYNC_MODVER 1
1666
1667DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
1668MODULE_VERSION(pfsync, PFSYNC_MODVER);
1669#endif /* __FreeBSD__ */
1670