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