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