if_pfsync.c revision 130933
1/*	$FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 130933 2004-06-22 20:13:25Z brooks $	*/
2/*	$OpenBSD: if_pfsync.c,v 1.26 2004/03/28 18:14:20 mcbride Exp $	*/
3
4/*
5 * Copyright (c) 2002 Michael Shalayeff
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#ifdef __FreeBSD__
31#include "opt_inet.h"
32#include "opt_inet6.h"
33#include "opt_random_ip_id.h"
34#endif
35
36#ifndef __FreeBSD__
37#include "bpfilter.h"
38#include "pfsync.h"
39#elif __FreeBSD__ >= 5
40#include "opt_bpf.h"
41#include "opt_pf.h"
42#define	NBPFILTER	DEV_BPF
43#define	NPFSYNC		DEV_PFSYNC
44#endif
45
46#include <sys/param.h>
47#include <sys/proc.h>
48#include <sys/systm.h>
49#include <sys/time.h>
50#include <sys/mbuf.h>
51#include <sys/socket.h>
52#ifdef __FreeBSD__
53#include <sys/kernel.h>
54#include <sys/malloc.h>
55#include <sys/module.h>
56#include <sys/sockio.h>
57#include <sys/lock.h>
58#include <sys/mutex.h>
59#else
60#include <sys/ioctl.h>
61#include <sys/timeout.h>
62#endif
63
64#include <net/if.h>
65#if defined(__FreeBSD__)
66#include <net/if_clone.h>
67#endif
68#include <net/if_types.h>
69#include <net/route.h>
70#include <net/bpf.h>
71
72#ifdef	INET
73#include <netinet/in.h>
74#include <netinet/in_systm.h>
75#include <netinet/in_var.h>
76#include <netinet/ip.h>
77#include <netinet/ip_var.h>
78#endif
79
80#ifdef INET6
81#ifndef INET
82#include <netinet/in.h>
83#endif
84#include <netinet6/nd6.h>
85#endif /* INET6 */
86
87#include <net/pfvar.h>
88#include <net/if_pfsync.h>
89
90#ifdef __FreeBSD__
91#define	PFSYNCNAME	"pfsync"
92#endif
93
94#define PFSYNC_MINMTU	\
95    (sizeof(struct pfsync_header) + sizeof(struct pf_state))
96
97#ifdef PFSYNCDEBUG
98#define DPRINTF(x)    do { if (pfsyncdebug) printf x ; } while (0)
99int pfsyncdebug;
100#else
101#define DPRINTF(x)
102#endif
103
104#ifndef __FreeBSD__
105struct pfsync_softc	pfsyncif;
106#endif
107int			pfsync_sync_ok;
108struct pfsyncstats	pfsyncstats;
109
110#ifndef RANDOM_IP_ID
111extern u_int16_t	 ip_randomid(void);
112#endif
113
114#ifdef __FreeBSD__
115
116/*
117 * Locking notes:
118 * Whenever we really touch/look at the state table we have to hold the
119 * PF_LOCK. Functions that do just the interface handling, grab the per
120 * softc lock instead.
121 *
122 */
123
124static void	pfsync_clone_destroy(struct ifnet *);
125static int	pfsync_clone_create(struct if_clone *, int);
126#else
127void	pfsyncattach(int);
128#endif
129void	pfsync_setmtu(struct pfsync_softc *, int);
130int	pfsync_insert_net_state(struct pfsync_state *);
131int	pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
132	    struct rtentry *);
133int	pfsyncioctl(struct ifnet *, u_long, caddr_t);
134void	pfsyncstart(struct ifnet *);
135
136struct mbuf *pfsync_get_mbuf(struct pfsync_softc *, u_int8_t, void **);
137int	pfsync_request_update(struct pfsync_state_upd *, struct in_addr *);
138int	pfsync_sendout(struct pfsync_softc *);
139void	pfsync_timeout(void *);
140void	pfsync_send_bus(struct pfsync_softc *, u_int8_t);
141void	pfsync_bulk_update(void *);
142void	pfsync_bulkfail(void *);
143
144#ifndef __FreeBSD__
145extern int ifqmaxlen;
146extern struct timeval time;
147extern struct timeval mono_time;
148extern int hz;
149#endif
150
151#ifdef __FreeBSD__
152static MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface");
153static LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list;
154IFC_SIMPLE_DECLARE(pfsync, 1);
155
156static void
157pfsync_clone_destroy(struct ifnet *ifp)
158{
159        struct pfsync_softc *sc;
160
161	sc = ifp->if_softc;
162	callout_stop(&sc->sc_tmo);
163	callout_stop(&sc->sc_bulk_tmo);
164	callout_stop(&sc->sc_bulkfail_tmo);
165
166#if NBPFILTER > 0
167        bpfdetach(ifp);
168#endif
169        if_detach(ifp);
170        LIST_REMOVE(sc, sc_next);
171        free(sc, M_PFSYNC);
172}
173
174static int
175pfsync_clone_create(struct if_clone *ifc, int unit)
176{
177	struct pfsync_softc *sc;
178	struct ifnet *ifp;
179
180	MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC,
181	    M_WAITOK|M_ZERO);
182
183	pfsync_sync_ok = 1;
184	sc->sc_mbuf = NULL;
185	sc->sc_mbuf_net = NULL;
186	sc->sc_statep.s = NULL;
187	sc->sc_statep_net.s = NULL;
188	sc->sc_maxupdates = 128;
189	sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP);
190	sc->sc_ureq_received = 0;
191	sc->sc_ureq_sent = 0;
192
193	ifp = &sc->sc_if;
194	if_initname(ifp, ifc->ifc_name, unit);
195	ifp->if_ioctl = pfsyncioctl;
196	ifp->if_output = pfsyncoutput;
197	ifp->if_start = pfsyncstart;
198	ifp->if_type = IFT_PFSYNC;
199	ifp->if_snd.ifq_maxlen = ifqmaxlen;
200	ifp->if_hdrlen = PFSYNC_HDRLEN;
201	ifp->if_baudrate = IF_Mbps(100);
202	ifp->if_softc = sc;
203	pfsync_setmtu(sc, MCLBYTES);
204	/*
205	 * XXX
206	 *  The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE
207	 * if Gaint lock is removed from the network stack.
208	 */
209	callout_init(&sc->sc_tmo, 0);
210	callout_init(&sc->sc_bulk_tmo, 0);
211	callout_init(&sc->sc_bulkfail_tmo, 0);
212	if_attach(&sc->sc_if);
213
214	LIST_INSERT_HEAD(&pfsync_list, sc, sc_next);
215#if NBPFILTER > 0
216	bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN);
217#endif
218
219	return (0);
220}
221#else /* !__FreeBSD__ */
222void
223pfsyncattach(int npfsync)
224{
225	struct ifnet *ifp;
226
227	pfsync_sync_ok = 1;
228	bzero(&pfsyncif, sizeof(pfsyncif));
229	pfsyncif.sc_mbuf = NULL;
230	pfsyncif.sc_mbuf_net = NULL;
231	pfsyncif.sc_statep.s = NULL;
232	pfsyncif.sc_statep_net.s = NULL;
233	pfsyncif.sc_maxupdates = 128;
234	pfsyncif.sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP;
235	pfsyncif.sc_ureq_received = 0;
236	pfsyncif.sc_ureq_sent = 0;
237	ifp = &pfsyncif.sc_if;
238	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
239	ifp->if_softc = &pfsyncif;
240	ifp->if_ioctl = pfsyncioctl;
241	ifp->if_output = pfsyncoutput;
242	ifp->if_start = pfsyncstart;
243	ifp->if_type = IFT_PFSYNC;
244	ifp->if_snd.ifq_maxlen = ifqmaxlen;
245	ifp->if_hdrlen = PFSYNC_HDRLEN;
246	pfsync_setmtu(&pfsyncif, MCLBYTES);
247	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
248	timeout_set(&pfsyncif.sc_bulk_tmo, pfsync_bulk_update, &pfsyncif);
249	timeout_set(&pfsyncif.sc_bulkfail_tmo, pfsync_bulkfail, &pfsyncif);
250	if_attach(ifp);
251	if_alloc_sadl(ifp);
252
253#if NBPFILTER > 0
254	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
255#endif
256}
257#endif
258
259/*
260 * Start output on the pfsync interface.
261 */
262void
263pfsyncstart(struct ifnet *ifp)
264{
265#ifdef __FreeBSD__
266	IF_LOCK(&ifp->if_snd);
267	_IF_DROP(&ifp->if_snd);
268	_IF_DRAIN(&ifp->if_snd);
269	IF_UNLOCK(&ifp->if_snd);
270#else
271	struct mbuf *m;
272	int s;
273
274	for (;;) {
275		s = splimp();
276		IF_DROP(&ifp->if_snd);
277		IF_DEQUEUE(&ifp->if_snd, m);
278		splx(s);
279
280		if (m == NULL)
281			return;
282		else
283			m_freem(m);
284	}
285#endif
286}
287
288int
289pfsync_insert_net_state(struct pfsync_state *sp)
290{
291	struct pf_state	*st = NULL;
292	struct pf_rule *r = NULL;
293	struct pfi_kif	*kif;
294
295#ifdef __FreeBSD__
296	PF_ASSERT(MA_OWNED);
297#endif
298	if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) {
299		printf("pfsync_insert_net_state: invalid creator id:"
300		    " %08x\n", ntohl(sp->creatorid));
301		return (EINVAL);
302	}
303
304	kif = pfi_lookup_create(sp->ifname);
305	if (kif == NULL) {
306		if (pf_status.debug >= PF_DEBUG_MISC)
307			printf("pfsync_insert_net_state: "
308			    "unknown interface: %s\n", sp->ifname);
309		/* skip this state */
310		return (0);
311	}
312
313	/*
314	 * Just use the default rule until we have infrastructure to find the
315	 * best matching rule.
316	 */
317	r = &pf_default_rule;
318
319	if (!r->max_states || r->states < r->max_states)
320		st = pool_get(&pf_state_pl, PR_NOWAIT);
321	if (st == NULL) {
322		pfi_maybe_destroy(kif);
323		return (ENOMEM);
324	}
325	bzero(st, sizeof(*st));
326
327	st->rule.ptr = r;
328	/* XXX get pointers to nat_rule and anchor */
329
330	/* fill in the rest of the state entry */
331	pf_state_host_ntoh(&sp->lan, &st->lan);
332	pf_state_host_ntoh(&sp->gwy, &st->gwy);
333	pf_state_host_ntoh(&sp->ext, &st->ext);
334
335	pf_state_peer_ntoh(&sp->src, &st->src);
336	pf_state_peer_ntoh(&sp->dst, &st->dst);
337
338	bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr));
339#ifdef __FreeBSD__
340	st->creation = ntohl(sp->creation) + time_second;
341	st->expire = ntohl(sp->expire) + time_second;
342#else
343	st->creation = ntohl(sp->creation) + time.tv_sec;
344	st->expire = ntohl(sp->expire) + time.tv_sec;
345#endif
346
347	st->af = sp->af;
348	st->proto = sp->proto;
349	st->direction = sp->direction;
350	st->log = sp->log;
351	st->timeout = sp->timeout;
352	st->allow_opts = sp->allow_opts;
353
354	bcopy(sp->id, &st->id, sizeof(st->id));
355	st->creatorid = sp->creatorid;
356	st->sync_flags = sp->sync_flags | PFSTATE_FROMSYNC;
357
358
359	if (pf_insert_state(kif, st)) {
360		pfi_maybe_destroy(kif);
361		pool_put(&pf_state_pl, st);
362		return (EINVAL);
363	}
364
365	return (0);
366}
367
368void
369#ifdef __FreeBSD__
370pfsync_input(struct mbuf *m, __unused int off)
371#else
372pfsync_input(struct mbuf *m, ...)
373#endif
374{
375	struct ip *ip = mtod(m, struct ip *);
376	struct pfsync_header *ph;
377#ifdef __FreeBSD__
378	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
379#else
380	struct pfsync_softc *sc = &pfsyncif;
381#endif
382	struct pf_state *st, key;
383	struct pfsync_state *sp;
384	struct pfsync_state_upd *up;
385	struct pfsync_state_del *dp;
386	struct pfsync_state_clr *cp;
387	struct pfsync_state_upd_req *rup;
388	struct pfsync_state_bus *bus;
389	struct in_addr src;
390	struct mbuf *mp;
391	int iplen, action, error, i, s, count, offp;
392
393	pfsyncstats.pfsyncs_ipackets++;
394
395	/* verify that we have a sync interface configured */
396	if (!sc->sc_sync_ifp || !pf_status.running) /* XXX PF_LOCK? */
397		goto done;
398
399	/* verify that the packet came in on the right interface */
400	if (sc->sc_sync_ifp != m->m_pkthdr.rcvif) {
401		pfsyncstats.pfsyncs_badif++;
402		goto done;
403	}
404
405	/* verify that the IP TTL is 255.  */
406	if (ip->ip_ttl != PFSYNC_DFLTTL) {
407		pfsyncstats.pfsyncs_badttl++;
408		goto done;
409	}
410
411	iplen = ip->ip_hl << 2;
412
413	if (m->m_pkthdr.len < iplen + sizeof(*ph)) {
414		pfsyncstats.pfsyncs_hdrops++;
415		goto done;
416	}
417
418	if (iplen + sizeof(*ph) > m->m_len) {
419		if ((m = m_pullup(m, iplen + sizeof(*ph))) == NULL) {
420			pfsyncstats.pfsyncs_hdrops++;
421			goto done;
422		}
423		ip = mtod(m, struct ip *);
424	}
425	ph = (struct pfsync_header *)((char *)ip + iplen);
426
427	/* verify the version */
428	if (ph->version != PFSYNC_VERSION) {
429		pfsyncstats.pfsyncs_badver++;
430		goto done;
431	}
432
433	action = ph->action;
434	count = ph->count;
435
436	/* make sure it's a valid action code */
437	if (action >= PFSYNC_ACT_MAX) {
438		pfsyncstats.pfsyncs_badact++;
439		goto done;
440	}
441
442	/* Cheaper to grab this now than having to mess with mbufs later */
443	src = ip->ip_src;
444
445	switch (action) {
446	case PFSYNC_ACT_CLR: {
447		struct pfi_kif	*kif;
448		u_int32_t creatorid;
449		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
450		    sizeof(*cp), &offp)) == NULL) {
451			pfsyncstats.pfsyncs_badlen++;
452			return;
453		}
454		cp = (struct pfsync_state_clr *)(mp->m_data + offp);
455		creatorid = cp->creatorid;
456
457		s = splsoftnet();
458#ifdef __FreeBSD__
459		PF_LOCK();
460#endif
461		if (cp->ifname[0] == '\0') {
462			RB_FOREACH(st, pf_state_tree_id, &tree_id) {
463				if (st->creatorid == creatorid)
464					st->timeout = PFTM_PURGE;
465			}
466		} else {
467			kif = pfi_lookup_if(cp->ifname);
468			if (kif == NULL) {
469				if (pf_status.debug >= PF_DEBUG_MISC)
470					printf("pfsync_input: PFSYNC_ACT_CLR "
471					    "bad interface: %s\n", cp->ifname);
472				splx(s);
473#ifdef __FreeBSD__
474				PF_UNLOCK();
475#endif
476				goto done;
477			}
478			RB_FOREACH(st, pf_state_tree_lan_ext,
479			    &kif->pfik_lan_ext) {
480				if (st->creatorid == creatorid)
481					st->timeout = PFTM_PURGE;
482			}
483		}
484		pf_purge_expired_states();
485#ifdef __FreeBSD__
486		PF_UNLOCK();
487#endif
488		splx(s);
489
490		break;
491	}
492	case PFSYNC_ACT_INS:
493		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
494		    count * sizeof(*sp), &offp)) == NULL) {
495			pfsyncstats.pfsyncs_badlen++;
496			return;
497		}
498
499		s = splsoftnet();
500#ifdef __FreeBSD__
501		PF_LOCK();
502#endif
503		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
504		    i < count; i++, sp++) {
505			/* check for invalid values */
506			if (sp->timeout >= PFTM_MAX ||
507			    sp->src.state > PF_TCPS_PROXY_DST ||
508			    sp->dst.state > PF_TCPS_PROXY_DST ||
509			    sp->direction > PF_OUT ||
510			    (sp->af != AF_INET && sp->af != AF_INET6)) {
511				if (pf_status.debug >= PF_DEBUG_MISC)
512					printf("pfsync_insert: PFSYNC_ACT_INS: "
513					    "invalid value\n");
514				pfsyncstats.pfsyncs_badstate++;
515				continue;
516			}
517
518			if ((error = pfsync_insert_net_state(sp))) {
519				if (error == ENOMEM) {
520					splx(s);
521#ifdef __FreeBSD__
522					PF_UNLOCK();
523#endif
524					goto done;
525				}
526				continue;
527			}
528		}
529#ifdef __FreeBSD__
530		PF_UNLOCK();
531#endif
532		splx(s);
533		break;
534	case PFSYNC_ACT_UPD:
535		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
536		    count * sizeof(*sp), &offp)) == NULL) {
537			pfsyncstats.pfsyncs_badlen++;
538			return;
539		}
540
541		s = splsoftnet();
542#ifdef __FreeBSD__
543		PF_LOCK();
544#endif
545		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
546		    i < count; i++, sp++) {
547			/* check for invalid values */
548			if (sp->timeout >= PFTM_MAX ||
549			    sp->src.state > PF_TCPS_PROXY_DST ||
550			    sp->dst.state > PF_TCPS_PROXY_DST) {
551				if (pf_status.debug >= PF_DEBUG_MISC)
552					printf("pfsync_insert: PFSYNC_ACT_UPD: "
553					    "invalid value\n");
554				pfsyncstats.pfsyncs_badstate++;
555				continue;
556			}
557
558			bcopy(sp->id, &key.id, sizeof(key.id));
559			key.creatorid = sp->creatorid;
560
561			st = pf_find_state_byid(&key);
562			if (st == NULL) {
563				/* insert the update */
564				if (pfsync_insert_net_state(sp))
565					pfsyncstats.pfsyncs_badstate++;
566				continue;
567			}
568			pf_state_peer_ntoh(&sp->src, &st->src);
569			pf_state_peer_ntoh(&sp->dst, &st->dst);
570#ifdef __FreeBSD__
571			st->expire = ntohl(sp->expire) + time_second;
572#else
573			st->expire = ntohl(sp->expire) + time.tv_sec;
574#endif
575			st->timeout = sp->timeout;
576
577		}
578#ifdef __FreeBSD__
579		PF_UNLOCK();
580#endif
581		splx(s);
582		break;
583	/*
584	 * It's not strictly necessary for us to support the "uncompressed"
585	 * delete action, but it's relatively simple and maintains consistency.
586	 */
587	case PFSYNC_ACT_DEL:
588		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
589		    count * sizeof(*sp), &offp)) == NULL) {
590			pfsyncstats.pfsyncs_badlen++;
591			return;
592		}
593
594		s = splsoftnet();
595#ifdef __FreeBSD__
596		PF_LOCK();
597#endif
598		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
599		    i < count; i++, sp++) {
600			bcopy(sp->id, &key.id, sizeof(key.id));
601			key.creatorid = sp->creatorid;
602
603			st = pf_find_state_byid(&key);
604			if (st == NULL) {
605				pfsyncstats.pfsyncs_badstate++;
606				continue;
607			}
608			/*
609			 * XXX
610			 * pf_purge_expired_states() is expensive,
611			 * we really want to purge the state directly.
612			 */
613			st->timeout = PFTM_PURGE;
614			st->sync_flags |= PFSTATE_FROMSYNC;
615		}
616		pf_purge_expired_states();
617#ifdef __FreeBSD__
618		PF_UNLOCK();
619#endif
620		splx(s);
621		break;
622	case PFSYNC_ACT_UPD_C: {
623		int update_requested = 0;
624
625		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
626		    count * sizeof(*up), &offp)) == NULL) {
627			pfsyncstats.pfsyncs_badlen++;
628			return;
629		}
630
631		s = splsoftnet();
632#ifdef __FreeBSD__
633		PF_LOCK();
634#endif
635		for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp);
636		    i < count; i++, up++) {
637			/* check for invalid values */
638			if (up->timeout >= PFTM_MAX ||
639			    up->src.state > PF_TCPS_PROXY_DST ||
640			    up->dst.state > PF_TCPS_PROXY_DST) {
641				if (pf_status.debug >= PF_DEBUG_MISC)
642					printf("pfsync_insert: "
643					    "PFSYNC_ACT_UPD_C: "
644					    "invalid value\n");
645				pfsyncstats.pfsyncs_badstate++;
646				continue;
647			}
648
649			bcopy(up->id, &key.id, sizeof(key.id));
650			key.creatorid = up->creatorid;
651
652			st = pf_find_state_byid(&key);
653			if (st == NULL) {
654				/* We don't have this state. Ask for it. */
655				pfsync_request_update(up, &src);
656				update_requested = 1;
657				pfsyncstats.pfsyncs_badstate++;
658				continue;
659			}
660			pf_state_peer_ntoh(&up->src, &st->src);
661			pf_state_peer_ntoh(&up->dst, &st->dst);
662#ifdef __FreeBSD__
663			st->expire = ntohl(up->expire) + time_second;
664#else
665			st->expire = ntohl(up->expire) + time.tv_sec;
666#endif
667			st->timeout = up->timeout;
668		}
669		if (update_requested)
670			pfsync_sendout(sc);
671#ifdef __FreeBSD__
672		PF_UNLOCK();
673#endif
674		splx(s);
675		break;
676	}
677	case PFSYNC_ACT_DEL_C:
678		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
679		    count * sizeof(*dp), &offp)) == NULL) {
680			pfsyncstats.pfsyncs_badlen++;
681			return;
682		}
683
684		s = splsoftnet();
685#ifdef __FreeBSD__
686		PF_LOCK();
687#endif
688		for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp);
689		    i < count; i++, dp++) {
690			bcopy(dp->id, &key.id, sizeof(key.id));
691			key.creatorid = dp->creatorid;
692
693			st = pf_find_state_byid(&key);
694			if (st == NULL) {
695				pfsyncstats.pfsyncs_badstate++;
696				continue;
697			}
698			/*
699			 * XXX
700			 * pf_purge_expired_states() is expensive,
701			 * we really want to purge the state directly.
702			 */
703			st->timeout = PFTM_PURGE;
704			st->sync_flags |= PFSTATE_FROMSYNC;
705		}
706		pf_purge_expired_states();
707#ifdef __FreeBSD__
708		PF_UNLOCK();
709#endif
710		splx(s);
711		break;
712	case PFSYNC_ACT_INS_F:
713	case PFSYNC_ACT_DEL_F:
714		/* not implemented */
715		break;
716	case PFSYNC_ACT_UREQ:
717		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
718		    count * sizeof(*rup), &offp)) == NULL) {
719			pfsyncstats.pfsyncs_badlen++;
720			return;
721		}
722
723		s = splsoftnet();
724		/* XXX send existing. pfsync_pack_state should handle this. */
725#ifdef __FreeBSD__
726		PF_LOCK();
727#endif
728		if (sc->sc_mbuf != NULL)
729			pfsync_sendout(sc);
730		for (i = 0,
731		    rup = (struct pfsync_state_upd_req *)(mp->m_data + offp);
732		    i < count; i++, rup++) {
733			bcopy(rup->id, &key.id, sizeof(key.id));
734			key.creatorid = rup->creatorid;
735
736			if (key.id == 0 && key.creatorid == 0) {
737#ifdef __FreeBSD__
738				sc->sc_ureq_received = time_uptime;
739#else
740				sc->sc_ureq_received = mono_time.tv_sec;
741#endif
742				if (pf_status.debug >= PF_DEBUG_MISC)
743					printf("pfsync: received "
744					    "bulk update request\n");
745				pfsync_send_bus(sc, PFSYNC_BUS_START);
746#ifdef __FreeBSD__
747				callout_reset(&sc->sc_bulk_tmo, 1 * hz,
748				    pfsync_bulk_update,
749				    LIST_FIRST(&pfsync_list));
750#else
751				timeout_add(&sc->sc_bulk_tmo, 1 * hz);
752#endif
753			} else {
754				st = pf_find_state_byid(&key);
755				if (st == NULL) {
756					pfsyncstats.pfsyncs_badstate++;
757					continue;
758				}
759				pfsync_pack_state(PFSYNC_ACT_UPD, st, 0);
760			}
761		}
762		if (sc->sc_mbuf != NULL)
763			pfsync_sendout(sc);
764#ifdef __FreeBSD__
765		PF_UNLOCK();
766#endif
767		splx(s);
768		break;
769	case PFSYNC_ACT_BUS:
770		/* If we're not waiting for a bulk update, who cares. */
771		if (sc->sc_ureq_sent == 0)
772			break;
773
774		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
775		    sizeof(*bus), &offp)) == NULL) {
776			pfsyncstats.pfsyncs_badlen++;
777			return;
778		}
779		bus = (struct pfsync_state_bus *)(mp->m_data + offp);
780		switch (bus->status) {
781		case PFSYNC_BUS_START:
782#ifdef __FreeBSD__
783			callout_reset(&sc->sc_bulkfail_tmo,
784			    pf_pool_limits[PF_LIMIT_STATES].limit /
785			    (PFSYNC_BULKPACKETS * sc->sc_maxcount),
786			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
787#else
788			timeout_add(&sc->sc_bulkfail_tmo,
789			    pf_pool_limits[PF_LIMIT_STATES].limit /
790			    (PFSYNC_BULKPACKETS * sc->sc_maxcount));
791#endif
792			if (pf_status.debug >= PF_DEBUG_MISC)
793				printf("pfsync: received bulk "
794				    "update start\n");
795			break;
796		case PFSYNC_BUS_END:
797#ifdef __FreeBSD__
798			if (time_uptime - ntohl(bus->endtime) >=
799#else
800			if (mono_time.tv_sec - ntohl(bus->endtime) >=
801#endif
802			    sc->sc_ureq_sent) {
803				/* that's it, we're happy */
804				sc->sc_ureq_sent = 0;
805				sc->sc_bulk_tries = 0;
806#ifdef __FreeBSD__
807				callout_stop(&sc->sc_bulkfail_tmo);
808#else
809				timeout_del(&sc->sc_bulkfail_tmo);
810#endif
811				pfsync_sync_ok = 1;
812				if (pf_status.debug >= PF_DEBUG_MISC)
813					printf("pfsync: received valid "
814					    "bulk update end\n");
815			} else {
816				if (pf_status.debug >= PF_DEBUG_MISC)
817					printf("pfsync: received invalid "
818					    "bulk update end: bad timestamp\n");
819			}
820			break;
821		}
822		break;
823	}
824
825done:
826	if (m)
827		m_freem(m);
828}
829
830int
831pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
832	struct rtentry *rt)
833{
834	m_freem(m);
835	return (0);
836}
837
838/* ARGSUSED */
839int
840pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
841{
842#ifndef __FreeBSD__
843	struct proc *p = curproc;
844#endif
845	struct pfsync_softc *sc = ifp->if_softc;
846	struct ifreq *ifr = (struct ifreq *)data;
847	struct ip_moptions *imo = &sc->sc_imo;
848	struct pfsyncreq pfsyncr;
849	struct ifnet    *sifp;
850	int s, error;
851
852	switch (cmd) {
853	case SIOCSIFADDR:
854	case SIOCAIFADDR:
855	case SIOCSIFDSTADDR:
856	case SIOCSIFFLAGS:
857		if (ifp->if_flags & IFF_UP)
858			ifp->if_flags |= IFF_RUNNING;
859		else
860			ifp->if_flags &= ~IFF_RUNNING;
861		break;
862	case SIOCSIFMTU:
863		if (ifr->ifr_mtu < PFSYNC_MINMTU)
864			return (EINVAL);
865		if (ifr->ifr_mtu > MCLBYTES)
866			ifr->ifr_mtu = MCLBYTES;
867		s = splnet();
868#ifdef __FreeBSD__
869		PF_LOCK();
870#endif
871		if (ifr->ifr_mtu < ifp->if_mtu) {
872			pfsync_sendout(sc);
873		}
874		pfsync_setmtu(sc, ifr->ifr_mtu);
875#ifdef __FreeBSD__
876		PF_UNLOCK();
877#endif
878		splx(s);
879		break;
880	case SIOCGETPFSYNC:
881#ifdef __FreeBSD__
882		/* XXX: read unlocked */
883#endif
884		bzero(&pfsyncr, sizeof(pfsyncr));
885		if (sc->sc_sync_ifp)
886			strlcpy(pfsyncr.pfsyncr_syncif,
887			    sc->sc_sync_ifp->if_xname, IFNAMSIZ);
888		pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
889		if ((error = copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))))
890			return (error);
891		break;
892	case SIOCSETPFSYNC:
893#ifdef __FreeBSD__
894		if ((error = suser(curthread)) != 0)
895#else
896		if ((error = suser(p, p->p_acflag)) != 0)
897#endif
898			return (error);
899		if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr))))
900			return (error);
901
902		if (pfsyncr.pfsyncr_maxupdates > 255)
903			return (EINVAL);
904#ifdef __FreeBSD__
905		PF_LOCK();
906#endif
907		sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
908
909		if (pfsyncr.pfsyncr_syncif[0] == 0) {
910			sc->sc_sync_ifp = NULL;
911			if (sc->sc_mbuf_net != NULL) {
912				/* Don't keep stale pfsync packets around. */
913				s = splnet();
914				m_freem(sc->sc_mbuf_net);
915				sc->sc_mbuf_net = NULL;
916				sc->sc_statep_net.s = NULL;
917				splx(s);
918			}
919#ifdef __FreeBSD__
920			PF_UNLOCK();
921#endif
922			break;
923		}
924		if ((sifp = ifunit(pfsyncr.pfsyncr_syncif)) == NULL) {
925#ifdef __FreeBSD__
926			PF_UNLOCK();
927#endif
928			return (EINVAL);
929		}
930		else if (sifp == sc->sc_sync_ifp) {
931#ifdef __FreeBSD__
932			PF_UNLOCK();
933#endif
934			break;
935		}
936
937		s = splnet();
938		if (sifp->if_mtu < sc->sc_if.if_mtu ||
939		    (sc->sc_sync_ifp != NULL &&
940		    sifp->if_mtu < sc->sc_sync_ifp->if_mtu) ||
941		    sifp->if_mtu < MCLBYTES - sizeof(struct ip))
942			pfsync_sendout(sc);
943		sc->sc_sync_ifp = sifp;
944
945		pfsync_setmtu(sc, sc->sc_if.if_mtu);
946
947		if (imo->imo_num_memberships > 0) {
948			in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
949			imo->imo_multicast_ifp = NULL;
950		}
951
952		if (sc->sc_sync_ifp) {
953			struct in_addr addr;
954
955#ifdef __FreeBSD__
956			PF_UNLOCK();		/* addmulti mallocs w/ WAITOK */
957			addr.s_addr = htonl(INADDR_PFSYNC_GROUP);
958#else
959			addr.s_addr = INADDR_PFSYNC_GROUP;
960#endif
961			if ((imo->imo_membership[0] =
962			    in_addmulti(&addr, sc->sc_sync_ifp)) == NULL) {
963				splx(s);
964				return (ENOBUFS);
965			}
966			imo->imo_num_memberships++;
967			imo->imo_multicast_ifp = sc->sc_sync_ifp;
968			imo->imo_multicast_ttl = PFSYNC_DFLTTL;
969			imo->imo_multicast_loop = 0;
970
971			/* Request a full state table update. */
972#ifdef __FreeBSD__
973			PF_LOCK();
974			sc->sc_ureq_sent = time_uptime;
975#else
976			sc->sc_ureq_sent = mono_time.tv_sec;
977#endif
978			pfsync_sync_ok = 0;
979			if (pf_status.debug >= PF_DEBUG_MISC)
980				printf("pfsync: requesting bulk update\n");
981#ifdef __FreeBSD__
982			callout_reset(&sc->sc_bulkfail_tmo, 5 * hz,
983			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
984#else
985			timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
986#endif
987			pfsync_request_update(NULL, NULL);
988			pfsync_sendout(sc);
989		}
990#ifdef __FreeBSD__
991		PF_UNLOCK();
992#endif
993		splx(s);
994
995		break;
996
997	default:
998		return (ENOTTY);
999	}
1000
1001	return (0);
1002}
1003
1004void
1005pfsync_setmtu(struct pfsync_softc *sc, int mtu_req)
1006{
1007	int mtu;
1008
1009	if (sc->sc_sync_ifp && sc->sc_sync_ifp->if_mtu < mtu_req)
1010		mtu = sc->sc_sync_ifp->if_mtu;
1011	else
1012		mtu = mtu_req;
1013
1014	sc->sc_maxcount = (mtu - sizeof(struct pfsync_header)) /
1015	    sizeof(struct pfsync_state);
1016	if (sc->sc_maxcount > 254)
1017	    sc->sc_maxcount = 254;
1018	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
1019	    sc->sc_maxcount * sizeof(struct pfsync_state);
1020}
1021
1022struct mbuf *
1023pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp)
1024{
1025	struct pfsync_header *h;
1026	struct mbuf *m;
1027	int len;
1028
1029#ifdef __FreeBSD__
1030	PF_ASSERT(MA_OWNED);
1031#endif
1032	MGETHDR(m, M_DONTWAIT, MT_DATA);
1033	if (m == NULL) {
1034		sc->sc_if.if_oerrors++;
1035		return (NULL);
1036	}
1037
1038	switch (action) {
1039	case PFSYNC_ACT_CLR:
1040		len = sizeof(struct pfsync_header) +
1041		    sizeof(struct pfsync_state_clr);
1042		break;
1043	case PFSYNC_ACT_UPD_C:
1044		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd)) +
1045		    sizeof(struct pfsync_header);
1046		break;
1047	case PFSYNC_ACT_DEL_C:
1048		len = (sc->sc_maxcount * sizeof(struct pfsync_state_del)) +
1049		    sizeof(struct pfsync_header);
1050		break;
1051	case PFSYNC_ACT_UREQ:
1052		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd_req)) +
1053		    sizeof(struct pfsync_header);
1054		break;
1055	case PFSYNC_ACT_BUS:
1056		len = sizeof(struct pfsync_header) +
1057		    sizeof(struct pfsync_state_bus);
1058		break;
1059	default:
1060		len = (sc->sc_maxcount * sizeof(struct pfsync_state)) +
1061		    sizeof(struct pfsync_header);
1062		break;
1063	}
1064
1065	if (len > MHLEN) {
1066		MCLGET(m, M_DONTWAIT);
1067		if ((m->m_flags & M_EXT) == 0) {
1068			m_free(m);
1069			sc->sc_if.if_oerrors++;
1070			return (NULL);
1071		}
1072		m->m_data += (MCLBYTES - len) &~ (sizeof(long) - 1);
1073	} else
1074		MH_ALIGN(m, len);
1075
1076	m->m_pkthdr.rcvif = NULL;
1077	m->m_pkthdr.len = m->m_len = sizeof(struct pfsync_header);
1078	h = mtod(m, struct pfsync_header *);
1079	h->version = PFSYNC_VERSION;
1080	h->af = 0;
1081	h->count = 0;
1082	h->action = action;
1083
1084	*sp = (void *)((char *)h + PFSYNC_HDRLEN);
1085#ifdef __FreeBSD__
1086	callout_reset(&sc->sc_tmo, hz, pfsync_timeout,
1087	    LIST_FIRST(&pfsync_list));
1088#else
1089	timeout_add(&sc->sc_tmo, hz);
1090#endif
1091	return (m);
1092}
1093
1094int
1095pfsync_pack_state(u_int8_t action, struct pf_state *st, int compress)
1096{
1097#ifdef __FreeBSD__
1098	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
1099#else
1100	struct ifnet *ifp = &pfsyncif.sc_if;
1101#endif
1102	struct pfsync_softc *sc = ifp->if_softc;
1103	struct pfsync_header *h, *h_net;
1104	struct pfsync_state *sp = NULL;
1105	struct pfsync_state_upd *up = NULL;
1106	struct pfsync_state_del *dp = NULL;
1107	struct pf_rule *r;
1108	u_long secs;
1109	int s, ret = 0;
1110	u_int8_t i = 255, newaction = 0;
1111
1112#ifdef __FreeBSD__
1113	PF_ASSERT(MA_OWNED);
1114#endif
1115	/*
1116	 * If a packet falls in the forest and there's nobody around to
1117	 * hear, does it make a sound?
1118	 */
1119	if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL) {
1120		/* Don't leave any stale pfsync packets hanging around. */
1121		if (sc->sc_mbuf != NULL) {
1122			m_freem(sc->sc_mbuf);
1123			sc->sc_mbuf = NULL;
1124			sc->sc_statep.s = NULL;
1125		}
1126		return (0);
1127	}
1128
1129	if (action >= PFSYNC_ACT_MAX)
1130		return (EINVAL);
1131
1132	s = splnet();
1133	if (sc->sc_mbuf == NULL) {
1134		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
1135		    (void *)&sc->sc_statep.s)) == NULL) {
1136			splx(s);
1137			return (ENOMEM);
1138		}
1139		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1140	} else {
1141		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1142		if (h->action != action) {
1143			pfsync_sendout(sc);
1144			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
1145			    (void *)&sc->sc_statep.s)) == NULL) {
1146				splx(s);
1147				return (ENOMEM);
1148			}
1149			h = mtod(sc->sc_mbuf, struct pfsync_header *);
1150		} else {
1151			/*
1152			 * If it's an update, look in the packet to see if
1153			 * we already have an update for the state.
1154			 */
1155			if (action == PFSYNC_ACT_UPD && sc->sc_maxupdates) {
1156				struct pfsync_state *usp =
1157				    (void *)((char *)h + PFSYNC_HDRLEN);
1158
1159				for (i = 0; i < h->count; i++) {
1160					if (!memcmp(usp->id, &st->id,
1161					    PFSYNC_ID_LEN) &&
1162					    usp->creatorid == st->creatorid) {
1163						sp = usp;
1164						sp->updates++;
1165						break;
1166					}
1167					usp++;
1168				}
1169			}
1170		}
1171	}
1172
1173#ifdef __FreeBSD__
1174	secs = time_second;
1175
1176	st->pfsync_time = time_uptime;
1177#else
1178	secs = time.tv_sec;
1179
1180	st->pfsync_time = mono_time.tv_sec;
1181#endif
1182	TAILQ_REMOVE(&state_updates, st, u.s.entry_updates);
1183	TAILQ_INSERT_TAIL(&state_updates, st, u.s.entry_updates);
1184
1185	if (sp == NULL) {
1186		/* not a "duplicate" update */
1187		i = 255;
1188		sp = sc->sc_statep.s++;
1189		sc->sc_mbuf->m_pkthdr.len =
1190		    sc->sc_mbuf->m_len += sizeof(struct pfsync_state);
1191		h->count++;
1192		bzero(sp, sizeof(*sp));
1193
1194		bcopy(&st->id, sp->id, sizeof(sp->id));
1195		sp->creatorid = st->creatorid;
1196
1197		strlcpy(sp->ifname, st->u.s.kif->pfik_name, sizeof(sp->ifname));
1198		pf_state_host_hton(&st->lan, &sp->lan);
1199		pf_state_host_hton(&st->gwy, &sp->gwy);
1200		pf_state_host_hton(&st->ext, &sp->ext);
1201
1202		bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
1203
1204		sp->creation = htonl(secs - st->creation);
1205		sp->packets[0] = htonl(st->packets[0]);
1206		sp->packets[1] = htonl(st->packets[1]);
1207		sp->bytes[0] = htonl(st->bytes[0]);
1208		sp->bytes[1] = htonl(st->bytes[1]);
1209		if ((r = st->rule.ptr) == NULL)
1210			sp->rule = htonl(-1);
1211		else
1212			sp->rule = htonl(r->nr);
1213		if ((r = st->anchor.ptr) == NULL)
1214			sp->anchor = htonl(-1);
1215		else
1216			sp->anchor = htonl(r->nr);
1217		sp->af = st->af;
1218		sp->proto = st->proto;
1219		sp->direction = st->direction;
1220		sp->log = st->log;
1221		sp->allow_opts = st->allow_opts;
1222		sp->timeout = st->timeout;
1223
1224		sp->sync_flags = st->sync_flags & PFSTATE_NOSYNC;
1225	}
1226
1227	pf_state_peer_hton(&st->src, &sp->src);
1228	pf_state_peer_hton(&st->dst, &sp->dst);
1229
1230	if (st->expire <= secs)
1231		sp->expire = htonl(0);
1232	else
1233		sp->expire = htonl(st->expire - secs);
1234
1235	/* do we need to build "compressed" actions for network transfer? */
1236	if (sc->sc_sync_ifp && compress) {
1237		switch (action) {
1238		case PFSYNC_ACT_UPD:
1239			newaction = PFSYNC_ACT_UPD_C;
1240			break;
1241		case PFSYNC_ACT_DEL:
1242			newaction = PFSYNC_ACT_DEL_C;
1243			break;
1244		default:
1245			/* by default we just send the uncompressed states */
1246			break;
1247		}
1248	}
1249
1250	if (newaction) {
1251		if (sc->sc_mbuf_net == NULL) {
1252			if ((sc->sc_mbuf_net = pfsync_get_mbuf(sc, newaction,
1253			    (void *)&sc->sc_statep_net.s)) == NULL) {
1254				splx(s);
1255				return (ENOMEM);
1256			}
1257		}
1258		h_net = mtod(sc->sc_mbuf_net, struct pfsync_header *);
1259
1260		switch (newaction) {
1261		case PFSYNC_ACT_UPD_C:
1262			if (i != 255) {
1263				up = (void *)((char *)h_net +
1264				    PFSYNC_HDRLEN + (i * sizeof(*up)));
1265				up->updates++;
1266			} else {
1267				h_net->count++;
1268				sc->sc_mbuf_net->m_pkthdr.len =
1269				    sc->sc_mbuf_net->m_len += sizeof(*up);
1270				up = sc->sc_statep_net.u++;
1271
1272				bzero(up, sizeof(*up));
1273				bcopy(&st->id, up->id, sizeof(up->id));
1274				up->creatorid = st->creatorid;
1275			}
1276			up->timeout = st->timeout;
1277			up->expire = sp->expire;
1278			up->src = sp->src;
1279			up->dst = sp->dst;
1280			break;
1281		case PFSYNC_ACT_DEL_C:
1282			sc->sc_mbuf_net->m_pkthdr.len =
1283			    sc->sc_mbuf_net->m_len += sizeof(*dp);
1284			dp = sc->sc_statep_net.d++;
1285			h_net->count++;
1286
1287			bzero(dp, sizeof(*dp));
1288			bcopy(&st->id, dp->id, sizeof(dp->id));
1289			dp->creatorid = st->creatorid;
1290			break;
1291		}
1292	}
1293
1294	if (h->count == sc->sc_maxcount ||
1295	    (sc->sc_maxupdates && (sp->updates >= sc->sc_maxupdates)))
1296		ret = pfsync_sendout(sc);
1297
1298	splx(s);
1299	return (ret);
1300}
1301
1302/* This must be called in splnet() */
1303int
1304pfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src)
1305{
1306#ifdef __FreeBSD__
1307	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
1308#else
1309	struct ifnet *ifp = &pfsyncif.sc_if;
1310#endif
1311	struct pfsync_header *h;
1312	struct pfsync_softc *sc = ifp->if_softc;
1313	struct pfsync_state_upd_req *rup;
1314	int s, ret = 0;		/* make the compiler happy */
1315
1316#ifdef __FreeBSD__
1317	PF_ASSERT(MA_OWNED);
1318#endif
1319	if (sc->sc_mbuf == NULL) {
1320		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
1321		    (void *)&sc->sc_statep.s)) == NULL) {
1322			splx(s);
1323			return (ENOMEM);
1324		}
1325		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1326	} else {
1327		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1328		if (h->action != PFSYNC_ACT_UREQ) {
1329			pfsync_sendout(sc);
1330			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
1331			    (void *)&sc->sc_statep.s)) == NULL) {
1332				splx(s);
1333				return (ENOMEM);
1334			}
1335			h = mtod(sc->sc_mbuf, struct pfsync_header *);
1336		}
1337	}
1338
1339	if (src != NULL)
1340		sc->sc_sendaddr = *src;
1341	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*rup);
1342	h->count++;
1343	rup = sc->sc_statep.r++;
1344	bzero(rup, sizeof(*rup));
1345	if (up != NULL) {
1346		bcopy(up->id, rup->id, sizeof(rup->id));
1347		rup->creatorid = up->creatorid;
1348	}
1349
1350	if (h->count == sc->sc_maxcount)
1351		ret = pfsync_sendout(sc);
1352
1353	return (ret);
1354}
1355
1356int
1357pfsync_clear_states(u_int32_t creatorid, char *ifname)
1358{
1359#ifdef __FreeBSD__
1360	struct ifnet *ifp = &(LIST_FIRST(&pfsync_list))->sc_if;
1361#else
1362	struct ifnet *ifp = &pfsyncif.sc_if;
1363#endif
1364	struct pfsync_softc *sc = ifp->if_softc;
1365	struct pfsync_state_clr *cp;
1366	int s, ret;
1367
1368	s = splnet();
1369#ifdef __FreeBSD__
1370	PF_ASSERT(MA_OWNED);
1371#endif
1372	if (sc->sc_mbuf != NULL)
1373		pfsync_sendout(sc);
1374	if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR,
1375	    (void *)&sc->sc_statep.c)) == NULL) {
1376		splx(s);
1377		return (ENOMEM);
1378	}
1379	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*cp);
1380	cp = sc->sc_statep.c;
1381	cp->creatorid = creatorid;
1382	if (ifname != NULL)
1383		strlcpy(cp->ifname, ifname, IFNAMSIZ);
1384
1385	ret = (pfsync_sendout(sc));
1386	splx(s);
1387	return (ret);
1388}
1389
1390void
1391pfsync_timeout(void *v)
1392{
1393	struct pfsync_softc *sc = v;
1394	int s;
1395
1396	s = splnet();
1397#ifdef __FreeBSD__
1398	PF_LOCK();
1399#endif
1400	pfsync_sendout(sc);
1401#ifdef __FreeBSD__
1402	PF_UNLOCK();
1403#endif
1404	splx(s);
1405}
1406
1407void
1408pfsync_send_bus(struct pfsync_softc *sc, u_int8_t status)
1409{
1410	struct pfsync_state_bus *bus;
1411
1412#ifdef __FreeBSD__
1413	PF_ASSERT(MA_OWNED);
1414#endif
1415	if (sc->sc_mbuf != NULL)
1416		pfsync_sendout(sc);
1417
1418	if (pfsync_sync_ok &&
1419	    (sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_BUS,
1420	    (void *)&sc->sc_statep.b)) != NULL) {
1421		sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*bus);
1422		bus = sc->sc_statep.b;
1423		bus->creatorid = pf_status.hostid;
1424		bus->status = status;
1425#ifdef __FreeBSD__
1426		bus->endtime = htonl(time_uptime - sc->sc_ureq_received);
1427#else
1428		bus->endtime = htonl(mono_time.tv_sec - sc->sc_ureq_received);
1429#endif
1430		pfsync_sendout(sc);
1431	}
1432}
1433
1434void
1435pfsync_bulk_update(void *v)
1436{
1437	struct pfsync_softc *sc = v;
1438	int s, i = 0;
1439	struct pf_state *state;
1440
1441#ifdef __FreeBSD__
1442	PF_LOCK();
1443#endif
1444	s = splnet();
1445	if (sc->sc_mbuf != NULL)
1446		pfsync_sendout(sc);
1447
1448	/*
1449	 * Grab at most PFSYNC_BULKPACKETS worth of states which have not
1450	 * been sent since the latest request was made.
1451	 */
1452	while ((state = TAILQ_FIRST(&state_updates)) != NULL &&
1453	    ++i < (sc->sc_maxcount * PFSYNC_BULKPACKETS)) {
1454		if (state->pfsync_time > sc->sc_ureq_received) {
1455			/* we're done */
1456			pfsync_send_bus(sc, PFSYNC_BUS_END);
1457			sc->sc_ureq_received = 0;
1458#ifdef __FreeBSD__
1459			callout_stop(&sc->sc_bulk_tmo);
1460#else
1461			timeout_del(&sc->sc_bulk_tmo);
1462#endif
1463			if (pf_status.debug >= PF_DEBUG_MISC)
1464				printf("pfsync: bulk update complete\n");
1465			break;
1466		} else {
1467			/* send an update and move to end of list */
1468			if (!state->sync_flags)
1469				pfsync_pack_state(PFSYNC_ACT_UPD, state, 0);
1470#ifdef __FreeBSD__
1471			state->pfsync_time = time_uptime;
1472#else
1473			state->pfsync_time = mono_time.tv_sec;
1474#endif
1475			TAILQ_REMOVE(&state_updates, state, u.s.entry_updates);
1476			TAILQ_INSERT_TAIL(&state_updates, state,
1477			    u.s.entry_updates);
1478
1479			/* look again for more in a bit */
1480#ifdef __FreeBSD__
1481			callout_reset(&sc->sc_bulk_tmo, 1, pfsync_timeout,
1482			    LIST_FIRST(&pfsync_list));
1483#else
1484			timeout_add(&sc->sc_bulk_tmo, 1);
1485#endif
1486		}
1487	}
1488	if (sc->sc_mbuf != NULL)
1489		pfsync_sendout(sc);
1490	splx(s);
1491#ifdef __FreeBSD__
1492	PF_UNLOCK();
1493#endif
1494}
1495
1496void
1497pfsync_bulkfail(void *v)
1498{
1499	struct pfsync_softc *sc = v;
1500
1501#ifdef __FreeBSD__
1502	PF_LOCK();
1503#endif
1504	if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) {
1505		/* Try again in a bit */
1506#ifdef __FreeBSD__
1507		callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulkfail,
1508		    LIST_FIRST(&pfsync_list));
1509#else
1510		timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
1511#endif
1512		pfsync_request_update(NULL, NULL);
1513		pfsync_sendout(sc);
1514	} else {
1515		/* Pretend like the transfer was ok */
1516		sc->sc_ureq_sent = 0;
1517		sc->sc_bulk_tries = 0;
1518		pfsync_sync_ok = 1;
1519		if (pf_status.debug >= PF_DEBUG_MISC)
1520			printf("pfsync: failed to receive "
1521			    "bulk update status\n");
1522#ifdef __FreeBSD__
1523		callout_stop(&sc->sc_bulkfail_tmo);
1524#else
1525		timeout_del(&sc->sc_bulkfail_tmo);
1526#endif
1527	}
1528#ifdef __FreeBSD__
1529	PF_UNLOCK();
1530#endif
1531}
1532
1533int
1534pfsync_sendout(sc)
1535	struct pfsync_softc *sc;
1536{
1537	struct ifnet *ifp = &sc->sc_if;
1538	struct mbuf *m;
1539
1540#ifdef __FreeBSD__
1541	PF_ASSERT(MA_OWNED);
1542	callout_stop(&sc->sc_tmo);
1543#else
1544	timeout_del(&sc->sc_tmo);
1545#endif
1546
1547	if (sc->sc_mbuf == NULL)
1548		return (0);
1549	m = sc->sc_mbuf;
1550	sc->sc_mbuf = NULL;
1551	sc->sc_statep.s = NULL;
1552
1553#ifdef __FreeBSD__
1554	KASSERT(m != NULL, ("pfsync_sendout: null mbuf"));
1555#endif
1556#if NBPFILTER > 0
1557	if (ifp->if_bpf)
1558		bpf_mtap(ifp->if_bpf, m);
1559#endif
1560
1561	if (sc->sc_mbuf_net) {
1562		m_freem(m);
1563		m = sc->sc_mbuf_net;
1564		sc->sc_mbuf_net = NULL;
1565		sc->sc_statep_net.s = NULL;
1566	}
1567
1568	if (sc->sc_sync_ifp) {
1569		struct ip *ip;
1570		struct ifaddr *ifa;
1571		struct sockaddr sa;
1572
1573		M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
1574		if (m == NULL) {
1575			pfsyncstats.pfsyncs_onomem++;
1576			return (0);
1577		}
1578		ip = mtod(m, struct ip *);
1579		ip->ip_v = IPVERSION;
1580		ip->ip_hl = sizeof(*ip) >> 2;
1581		ip->ip_tos = IPTOS_LOWDELAY;
1582#ifdef __FreeBSD__
1583		ip->ip_len = m->m_pkthdr.len;
1584#else
1585		ip->ip_len = htons(m->m_pkthdr.len);
1586#endif
1587		ip->ip_id = htons(ip_randomid());
1588#ifdef __FreeBSD__
1589		ip->ip_off = IP_DF;
1590#else
1591		ip->ip_off = htons(IP_DF);
1592#endif
1593		ip->ip_ttl = PFSYNC_DFLTTL;
1594		ip->ip_p = IPPROTO_PFSYNC;
1595		ip->ip_sum = 0;
1596
1597		bzero(&sa, sizeof(sa));
1598		sa.sa_family = AF_INET;
1599		ifa = ifaof_ifpforaddr(&sa, sc->sc_sync_ifp);
1600		if (ifa == NULL)
1601			return (0);
1602		ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr;
1603
1604#ifdef __FreeBSD__
1605		if (sc->sc_sendaddr.s_addr == htonl(INADDR_PFSYNC_GROUP))
1606#else
1607		if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP)
1608#endif
1609			m->m_flags |= M_MCAST;
1610		ip->ip_dst = sc->sc_sendaddr;
1611#ifdef __FreeBSD__
1612		sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP);
1613#else
1614		sc->sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP;
1615#endif
1616
1617		pfsyncstats.pfsyncs_opackets++;
1618
1619#ifdef __FreeBSD__
1620		PF_UNLOCK();
1621#endif
1622		if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))
1623			pfsyncstats.pfsyncs_oerrors++;
1624
1625#ifdef __FreeBSD__
1626		PF_LOCK();
1627#endif
1628	} else
1629		m_freem(m);
1630
1631	return (0);
1632}
1633
1634
1635#ifdef __FreeBSD__
1636static int
1637pfsync_modevent(module_t mod, int type, void *data)
1638{
1639	int error = 0;
1640
1641	switch (type) {
1642	case MOD_LOAD:
1643		LIST_INIT(&pfsync_list);
1644		if_clone_attach(&pfsync_cloner);
1645		break;
1646
1647	case MOD_UNLOAD:
1648		if_clone_detach(&pfsync_cloner);
1649		while (!LIST_EMPTY(&pfsync_list))
1650			pfsync_clone_destroy(
1651				&LIST_FIRST(&pfsync_list)->sc_if);
1652		break;
1653
1654	default:
1655		error = EINVAL;
1656		break;
1657	}
1658
1659	return error;
1660}
1661
1662static moduledata_t pfsync_mod = {
1663	"pfsync",
1664	pfsync_modevent,
1665	0
1666};
1667
1668#define PFSYNC_MODVER 1
1669
1670DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
1671MODULE_VERSION(pfsync, PFSYNC_MODVER);
1672#endif /* __FreeBSD__ */
1673