if_pfsync.c revision 165632
1101704Smjacob/*	$FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 165632 2006-12-29 13:59:50Z jhb $	*/
2139749Simp/*	$OpenBSD: if_pfsync.c,v 1.46 2005/02/20 15:58:38 mcbride Exp $	*/
3101704Smjacob
4101704Smjacob/*
5101704Smjacob * Copyright (c) 2002 Michael Shalayeff
6101704Smjacob * All rights reserved.
7101704Smjacob *
8101704Smjacob * Redistribution and use in source and binary forms, with or without
9101704Smjacob * modification, are permitted provided that the following conditions
10101704Smjacob * are met:
11101704Smjacob * 1. Redistributions of source code must retain the above copyright
12101704Smjacob *    notice, this list of conditions and the following disclaimer.
13101704Smjacob * 2. Redistributions in binary form must reproduce the above copyright
14101704Smjacob *    notice, this list of conditions and the following disclaimer in the
15101704Smjacob *    documentation and/or other materials provided with the distribution.
16101704Smjacob *
17101704Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18101704Smjacob * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19101704Smjacob * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20101704Smjacob * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
21101704Smjacob * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22101704Smjacob * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23101704Smjacob * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24101704Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25101704Smjacob * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26101704Smjacob * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27101704Smjacob * THE POSSIBILITY OF SUCH DAMAGE.
28156000Smjacob */
29156000Smjacob
30156000Smjacob#ifdef __FreeBSD__
31156000Smjacob#include "opt_inet.h"
32156000Smjacob#include "opt_inet6.h"
33156000Smjacob#endif
34156000Smjacob
35156000Smjacob#ifndef __FreeBSD__
36156000Smjacob#include "bpfilter.h"
37156000Smjacob#include "pfsync.h"
38156000Smjacob#elif __FreeBSD__ >= 5
39156000Smjacob#include "opt_bpf.h"
40156000Smjacob#include "opt_pf.h"
41156000Smjacob
42156000Smjacob#ifdef DEV_BPF
43156000Smjacob#define	NBPFILTER	DEV_BPF
44156000Smjacob#else
45156000Smjacob#define	NBPFILTER	0
46156000Smjacob#endif
47156000Smjacob
48156000Smjacob#ifdef DEV_PFSYNC
49156000Smjacob#define	NPFSYNC		DEV_PFSYNC
50156000Smjacob#else
51156000Smjacob#define	NPFSYNC		0
52156000Smjacob#endif
53156000Smjacob
54156000Smjacob#endif
55156000Smjacob
56156000Smjacob#include <sys/param.h>
57156000Smjacob#ifdef __FreeBSD__
58147883Sscottl#include <sys/priv.h>
59156000Smjacob#endif
60156000Smjacob#include <sys/proc.h>
61159052Smjacob#include <sys/systm.h>
62159052Smjacob#include <sys/time.h>
63159052Smjacob#include <sys/mbuf.h>
64159052Smjacob#include <sys/socket.h>
65101704Smjacob#include <sys/kernel.h>
66101704Smjacob#ifdef __FreeBSD__
67147883Sscottl#include <sys/endian.h>
68147883Sscottl#include <sys/malloc.h>
69147883Sscottl#include <sys/module.h>
70147883Sscottl#include <sys/sockio.h>
71147883Sscottl#include <sys/lock.h>
72147883Sscottl#include <sys/mutex.h>
73147883Sscottl#include <sys/sysctl.h>
74147883Sscottl#else
75147883Sscottl#include <sys/ioctl.h>
76147883Sscottl#include <sys/timeout.h>
77147883Sscottl#endif
78147883Sscottl
79147883Sscottl#include <net/if.h>
80147883Sscottl#if defined(__FreeBSD__)
81147883Sscottl#include <net/if_clone.h>
82148679Sgibbs#endif
83148679Sgibbs#include <net/if_types.h>
84148679Sgibbs#include <net/route.h>
85147883Sscottl#include <net/bpf.h>
86147883Sscottl#include <netinet/tcp.h>
87147883Sscottl#include <netinet/tcp_seq.h>
88147883Sscottl
89147883Sscottl#ifdef	INET
90147883Sscottl#include <netinet/in.h>
91147883Sscottl#include <netinet/in_systm.h>
92147883Sscottl#include <netinet/in_var.h>
93147883Sscottl#include <netinet/ip.h>
94147883Sscottl#include <netinet/ip_var.h>
95147883Sscottl#endif
96147883Sscottl
97101704Smjacob#ifdef INET6
98101704Smjacob#ifndef INET
99101704Smjacob#include <netinet/in.h>
100101704Smjacob#endif
101147883Sscottl#include <netinet6/nd6.h>
102147883Sscottl#endif /* INET6 */
103147883Sscottl
104147883Sscottl#ifdef __FreeBSD__
105241874Smarius#include "opt_carp.h"
106241874Smarius#ifdef DEV_CARP
107147883Sscottl#define	NCARP	1
108147883Sscottl#else
109147883Sscottl#define	NCARP	0
110147883Sscottl#endif
111147883Sscottl#else
112241874Smarius#include "carp.h"
113147883Sscottl#endif
114147883Sscottl#if NCARP > 0
115241874Smariusextern int carp_suppress_preempt;
116241874Smarius#endif
117241874Smarius
118147883Sscottl#include <net/pfvar.h>
119241874Smarius#include <net/if_pfsync.h>
120147883Sscottl
121147883Sscottl#ifdef __FreeBSD__
122147883Sscottl#define	PFSYNCNAME	"pfsync"
123207287Smarius#endif
124207287Smarius
125207287Smarius#define PFSYNC_MINMTU	\
126207287Smarius    (sizeof(struct pfsync_header) + sizeof(struct pf_state))
127207287Smarius
128155521Smjacob#ifdef PFSYNCDEBUG
129155521Smjacob#define DPRINTF(x)    do { if (pfsyncdebug) printf x ; } while (0)
130155521Smjacobint pfsyncdebug;
131147883Sscottl#else
132147883Sscottl#define DPRINTF(x)
133147883Sscottl#endif
134147883Sscottl
135147883Sscottl#ifndef __FreeBSD__
136147883Sscottlstruct pfsync_softc	pfsyncif;
137147883Sscottl#endif
138147883Sscottlstruct pfsyncstats	pfsyncstats;
139147883Sscottl#ifdef __FreeBSD__
140147883SscottlSYSCTL_DECL(_net_inet_pfsync);
141147883SscottlSYSCTL_STRUCT(_net_inet_pfsync, 0, stats, CTLFLAG_RW,
142147883Sscottl    &pfsyncstats, pfsyncstats,
143147883Sscottl    "PFSYNC statistics (struct pfsyncstats, net/if_pfsync.h)");
144147883Sscottl
145147883Sscottl/*
146164990Smjacob * Locking notes:
147164990Smjacob * Whenever we really touch/look at the state table we have to hold the
148164990Smjacob * PF_LOCK. Functions that do just the interface handling, grab the per
149147883Sscottl * softc lock instead.
150165814Smjacob *
151101704Smjacob */
152101704Smjacob
153101704Smjacobstatic void	pfsync_clone_destroy(struct ifnet *);
154147883Sscottlstatic int	pfsync_clone_create(struct if_clone *, int, caddr_t params);
155101704Smjacobstatic void	pfsync_senddef(void *);
156157117Smjacob#else
157157117Smjacobvoid	pfsyncattach(int);
158157117Smjacob#endif
159157117Smjacobvoid	pfsync_setmtu(struct pfsync_softc *, int);
160157117Smjacobint	pfsync_insert_net_state(struct pfsync_state *);
161157117Smjacobint	pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
162207287Smarius	    struct rtentry *);
163207287Smariusint	pfsyncioctl(struct ifnet *, u_long, caddr_t);
164147883Sscottlvoid	pfsyncstart(struct ifnet *);
165147883Sscottl
166147883Sscottlstruct mbuf *pfsync_get_mbuf(struct pfsync_softc *, u_int8_t, void **);
167147883Sscottlint	pfsync_request_update(struct pfsync_state_upd *, struct in_addr *);
168101704Smjacobint	pfsync_sendout(struct pfsync_softc *);
169147883Sscottlvoid	pfsync_timeout(void *);
170147883Sscottlvoid	pfsync_send_bus(struct pfsync_softc *, u_int8_t);
171147883Sscottlvoid	pfsync_bulk_update(void *);
172147883Sscottlvoid	pfsync_bulkfail(void *);
173157117Smjacob
174162133Smjacobint	pfsync_sync_ok;
175147883Sscottl#ifndef __FreeBSD__
176147883Sscottlextern int ifqmaxlen;
177147883Sscottlextern struct timeval time;
178147883Sscottlextern struct timeval mono_time;
179147883Sscottlextern int hz;
180147883Sscottl#endif
181147883Sscottl
182147883Sscottl#ifdef __FreeBSD__
183147883Sscottlstatic MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface");
184147883Sscottlstatic LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list;
185147883Sscottl#define	SCP2IFP(sc)		((sc)->sc_ifp)
186147883SscottlIFC_SIMPLE_DECLARE(pfsync, 1);
187147883Sscottl
188147883Sscottlstatic void
189147883Sscottlpfsync_clone_destroy(struct ifnet *ifp)
190147883Sscottl{
191147883Sscottl        struct pfsync_softc *sc;
192157117Smjacob
193162133Smjacob	sc = ifp->if_softc;
194147883Sscottl	callout_stop(&sc->sc_tmo);
195147883Sscottl	callout_stop(&sc->sc_bulk_tmo);
196147883Sscottl	callout_stop(&sc->sc_bulkfail_tmo);
197147883Sscottl
198147883Sscottl	callout_stop(&sc->sc_send_tmo);
199147883Sscottl
200101704Smjacob#if NBPFILTER > 0
201101704Smjacob        bpfdetach(ifp);
202147883Sscottl#endif
203101704Smjacob        if_detach(ifp);
204147883Sscottl	if_free(ifp);
205147883Sscottl        LIST_REMOVE(sc, sc_next);
206101704Smjacob        free(sc->sc_imo.imo_membership, M_PFSYNC);
207147883Sscottl        free(sc, M_PFSYNC);
208147883Sscottl}
209101704Smjacob
210147883Sscottlstatic int
211147883Sscottl#ifdef __FreeBSD__
212147883Sscottlpfsync_clone_create(struct if_clone *ifc, int unit, caddr_t params)
213147883Sscottl#else
214147883Sscottlpfsync_clone_create(struct if_clone *ifc, int unit)
215147883Sscottl#endif
216147883Sscottl{
217147883Sscottl	struct pfsync_softc *sc;
218147883Sscottl	struct ifnet *ifp;
219147883Sscottl
220147883Sscottl	MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC,
221147883Sscottl	    M_WAITOK|M_ZERO);
222147883Sscottl	ifp = sc->sc_ifp = if_alloc(IFT_PFSYNC);
223167426Smjacob	if (ifp == NULL) {
224167426Smjacob		free(sc, M_PFSYNC);
225167426Smjacob		return (ENOSPC);
226147883Sscottl	}
227147883Sscottl
228147883Sscottl	pfsync_sync_ok = 1;
229147883Sscottl	sc->sc_mbuf = NULL;
230147883Sscottl	sc->sc_mbuf_net = NULL;
231147883Sscottl	sc->sc_statep.s = NULL;
232147883Sscottl	sc->sc_statep_net.s = NULL;
233169293Smjacob	sc->sc_maxupdates = 128;
234147883Sscottl	sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP);
235147883Sscottl	sc->sc_sendaddr.s_addr = htonl(INADDR_PFSYNC_GROUP);
236147883Sscottl	sc->sc_ureq_received = 0;
237147883Sscottl	sc->sc_ureq_sent = 0;
238147883Sscottl	sc->sc_imo.imo_membership = (struct in_multi **)malloc(
239147883Sscottl	    (sizeof(struct in_multi *) * IP_MIN_MEMBERSHIPS), M_PFSYNC,
240147883Sscottl	    M_WAITOK);
241147883Sscottl	sc->sc_imo.imo_max_memberships = IP_MIN_MEMBERSHIPS;
242166935Smjacob
243166935Smjacob	ifp = SCP2IFP(sc);
244166935Smjacob	if_initname(ifp, ifc->ifc_name, unit);
245166935Smjacob	ifp->if_ioctl = pfsyncioctl;
246166935Smjacob	ifp->if_output = pfsyncoutput;
247166935Smjacob	ifp->if_start = pfsyncstart;
248166935Smjacob	ifp->if_snd.ifq_maxlen = ifqmaxlen;
249147883Sscottl	ifp->if_hdrlen = PFSYNC_HDRLEN;
250178725Sjkim	ifp->if_baudrate = IF_Mbps(100);
251178725Sjkim	ifp->if_softc = sc;
252178725Sjkim	pfsync_setmtu(sc, MCLBYTES);
253178725Sjkim	callout_init(&sc->sc_tmo, NET_CALLOUT_MPSAFE);
254178725Sjkim	callout_init(&sc->sc_bulk_tmo, NET_CALLOUT_MPSAFE);
255178725Sjkim	callout_init(&sc->sc_bulkfail_tmo, NET_CALLOUT_MPSAFE);
256178725Sjkim	callout_init(&sc->sc_send_tmo, NET_CALLOUT_MPSAFE);
257178725Sjkim	sc->sc_ifq.ifq_maxlen = ifqmaxlen;
258147883Sscottl	mtx_init(&sc->sc_ifq.ifq_mtx, ifp->if_xname, "pfsync send queue",
259172842Sjulian	    MTX_DEF);
260147883Sscottl	if_attach(ifp);
261172836Sjulian
262178725Sjkim	LIST_INSERT_HEAD(&pfsync_list, sc, sc_next);
263178725Sjkim#if NBPFILTER > 0
264241874Smarius	bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
265147883Sscottl#endif
266172842Sjulian
267178725Sjkim	return (0);
268178725Sjkim}
269172842Sjulian#else /* !__FreeBSD__ */
270147883Sscottlvoid
271147883Sscottlpfsyncattach(int npfsync)
272164990Smjacob{
273164990Smjacob	struct ifnet *ifp;
274164990Smjacob
275147883Sscottl	pfsync_sync_ok = 1;
276164990Smjacob	bzero(&pfsyncif, sizeof(pfsyncif));
277164990Smjacob	pfsyncif.sc_mbuf = NULL;
278164990Smjacob	pfsyncif.sc_mbuf_net = NULL;
279147883Sscottl	pfsyncif.sc_statep.s = NULL;
280164998Smjacob	pfsyncif.sc_statep_net.s = NULL;
281164998Smjacob	pfsyncif.sc_maxupdates = 128;
282164998Smjacob	pfsyncif.sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP;
283164998Smjacob	pfsyncif.sc_sendaddr.s_addr = INADDR_PFSYNC_GROUP;
284164998Smjacob	pfsyncif.sc_ureq_received = 0;
285186878Smarius	pfsyncif.sc_ureq_sent = 0;
286186878Smarius	ifp = &pfsyncif.sc_if;
287186878Smarius	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
288186878Smarius	ifp->if_softc = &pfsyncif;
289186878Smarius	ifp->if_ioctl = pfsyncioctl;
290186878Smarius	ifp->if_output = pfsyncoutput;
291186878Smarius	ifp->if_start = pfsyncstart;
292186878Smarius	ifp->if_type = IFT_PFSYNC;
293186878Smarius	ifp->if_snd.ifq_maxlen = ifqmaxlen;
294186878Smarius	ifp->if_hdrlen = PFSYNC_HDRLEN;
295186878Smarius	pfsync_setmtu(&pfsyncif, MCLBYTES);
296164998Smjacob	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
297186878Smarius	timeout_set(&pfsyncif.sc_bulk_tmo, pfsync_bulk_update, &pfsyncif);
298164998Smjacob	timeout_set(&pfsyncif.sc_bulkfail_tmo, pfsync_bulkfail, &pfsyncif);
299164998Smjacob	if_attach(ifp);
300164990Smjacob	if_alloc_sadl(ifp);
301164990Smjacob
302164990Smjacob#if NBPFILTER > 0
303164990Smjacob	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
304186878Smarius#endif
305186878Smarius}
306186878Smarius#endif
307186878Smarius
308186878Smarius/*
309186878Smarius * Start output on the pfsync interface.
310186878Smarius */
311186878Smariusvoid
312186878Smariuspfsyncstart(struct ifnet *ifp)
313186878Smarius{
314186878Smarius#ifdef __FreeBSD__
315164990Smjacob	IF_LOCK(&ifp->if_snd);
316186878Smarius	_IF_DROP(&ifp->if_snd);
317186878Smarius	_IF_DRAIN(&ifp->if_snd);
318164990Smjacob	IF_UNLOCK(&ifp->if_snd);
319164990Smjacob#else
320164990Smjacob	struct mbuf *m;
321147883Sscottl	int s;
322147883Sscottl
323157354Smjacob	for (;;) {
324157354Smjacob		s = splimp();
325157354Smjacob		IF_DROP(&ifp->if_snd);
326157354Smjacob		IF_DEQUEUE(&ifp->if_snd, m);
327157354Smjacob		splx(s);
328157354Smjacob
329157354Smjacob		if (m == NULL)
330157354Smjacob			return;
331147883Sscottl		else
332147883Sscottl			m_freem(m);
333147883Sscottl	}
334147883Sscottl#endif
335147883Sscottl}
336147883Sscottl
337147883Sscottlint
338147883Sscottlpfsync_insert_net_state(struct pfsync_state *sp)
339220945Smarius{
340157662Smjacob	struct pf_state	*st = NULL;
341147883Sscottl	struct pf_rule *r = NULL;
342147883Sscottl	struct pfi_kif	*kif;
343147883Sscottl
344147883Sscottl#ifdef __FreeBSD__
345147883Sscottl	PF_ASSERT(MA_OWNED);
346159312Smjacob#endif
347155521Smjacob	if (sp->creatorid == 0 && pf_status.debug >= PF_DEBUG_MISC) {
348169293Smjacob		printf("pfsync_insert_net_state: invalid creator id:"
349147883Sscottl		    " %08x\n", ntohl(sp->creatorid));
350147883Sscottl		return (EINVAL);
351170252Sscottl	}
352170252Sscottl
353170252Sscottl	kif = pfi_lookup_create(sp->ifname);
354170252Sscottl	if (kif == NULL) {
355170252Sscottl		if (pf_status.debug >= PF_DEBUG_MISC)
356170252Sscottl			printf("pfsync_insert_net_state: "
357170252Sscottl			    "unknown interface: %s\n", sp->ifname);
358170252Sscottl		/* skip this state */
359170252Sscottl		return (0);
360170252Sscottl	}
361170252Sscottl
362157117Smjacob	/*
363157117Smjacob	 * Just use the default rule until we have infrastructure to find the
364157117Smjacob	 * best matching rule.
365157117Smjacob	 */
366157117Smjacob	r = &pf_default_rule;
367157117Smjacob
368157117Smjacob	if (!r->max_states || r->states < r->max_states)
369157117Smjacob		st = pool_get(&pf_state_pl, PR_NOWAIT);
370160391Smjacob	if (st == NULL) {
371160391Smjacob		pfi_maybe_destroy(kif);
372160391Smjacob		return (ENOMEM);
373157354Smjacob	}
374157117Smjacob	bzero(st, sizeof(*st));
375157117Smjacob
376157354Smjacob	st->rule.ptr = r;
377157117Smjacob	/* XXX get pointers to nat_rule and anchor */
378157117Smjacob
379157117Smjacob	/* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */
380157117Smjacob	r->states++;
381157117Smjacob
382157117Smjacob	/* fill in the rest of the state entry */
383157117Smjacob	pf_state_host_ntoh(&sp->lan, &st->lan);
384157117Smjacob	pf_state_host_ntoh(&sp->gwy, &st->gwy);
385157117Smjacob	pf_state_host_ntoh(&sp->ext, &st->ext);
386157117Smjacob
387157117Smjacob	pf_state_peer_ntoh(&sp->src, &st->src);
388157117Smjacob	pf_state_peer_ntoh(&sp->dst, &st->dst);
389157117Smjacob
390157117Smjacob	bcopy(&sp->rt_addr, &st->rt_addr, sizeof(st->rt_addr));
391157117Smjacob	st->creation = time_second - ntohl(sp->creation);
392157117Smjacob	st->expire = ntohl(sp->expire) + time_second;
393157117Smjacob
394157117Smjacob	st->af = sp->af;
395157117Smjacob	st->proto = sp->proto;
396157117Smjacob	st->direction = sp->direction;
397157354Smjacob	st->log = sp->log;
398157117Smjacob	st->timeout = sp->timeout;
399157117Smjacob	st->allow_opts = sp->allow_opts;
400157117Smjacob
401157117Smjacob	bcopy(sp->id, &st->id, sizeof(st->id));
402157354Smjacob	st->creatorid = sp->creatorid;
403157117Smjacob	st->sync_flags = PFSTATE_FROMSYNC;
404157117Smjacob
405157117Smjacob
406157117Smjacob	if (pf_insert_state(kif, st)) {
407157117Smjacob		pfi_maybe_destroy(kif);
408157117Smjacob		/* XXX when we have nat_rule/anchors, use STATE_DEC_COUNTERS */
409157117Smjacob		r->states--;
410157117Smjacob		pool_put(&pf_state_pl, st);
411157117Smjacob		return (EINVAL);
412157117Smjacob	}
413157117Smjacob
414157117Smjacob	return (0);
415157662Smjacob}
416157117Smjacob
417147883Sscottlvoid
418101704Smjacob#ifdef __FreeBSD__
419147883Sscottlpfsync_input(struct mbuf *m, __unused int off)
420147883Sscottl#else
421147883Sscottlpfsync_input(struct mbuf *m, ...)
422147883Sscottl#endif
423147883Sscottl{
424147883Sscottl	struct ip *ip = mtod(m, struct ip *);
425147883Sscottl	struct pfsync_header *ph;
426147883Sscottl#ifdef __FreeBSD__
427147883Sscottl	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
428147883Sscottl#else
429147883Sscottl	struct pfsync_softc *sc = &pfsyncif;
430147883Sscottl#endif
431147883Sscottl	struct pf_state *st, key;
432147883Sscottl	struct pfsync_state *sp;
433147883Sscottl	struct pfsync_state_upd *up;
434147883Sscottl	struct pfsync_state_del *dp;
435147883Sscottl	struct pfsync_state_clr *cp;
436147883Sscottl	struct pfsync_state_upd_req *rup;
437101704Smjacob	struct pfsync_state_bus *bus;
438157117Smjacob	struct in_addr src;
439147883Sscottl	struct mbuf *mp;
440147883Sscottl	int iplen, action, error, i, s, count, offp, sfail, stale = 0;
441147883Sscottl
442147883Sscottl	pfsyncstats.pfsyncs_ipackets++;
443157117Smjacob
444147883Sscottl	/* verify that we have a sync interface configured */
445147883Sscottl	if (!sc->sc_sync_ifp || !pf_status.running) /* XXX PF_LOCK? */
446147883Sscottl		goto done;
447101704Smjacob
448147883Sscottl	/* verify that the packet came in on the right interface */
449147883Sscottl	if (sc->sc_sync_ifp != m->m_pkthdr.rcvif) {
450147883Sscottl		pfsyncstats.pfsyncs_badif++;
451147883Sscottl		goto done;
452147883Sscottl	}
453147883Sscottl
454101704Smjacob	/* verify that the IP TTL is 255.  */
455147883Sscottl	if (ip->ip_ttl != PFSYNC_DFLTTL) {
456147883Sscottl		pfsyncstats.pfsyncs_badttl++;
457147883Sscottl		goto done;
458147883Sscottl	}
459101704Smjacob
460101704Smjacob	iplen = ip->ip_hl << 2;
461147883Sscottl
462147883Sscottl	if (m->m_pkthdr.len < iplen + sizeof(*ph)) {
463147883Sscottl		pfsyncstats.pfsyncs_hdrops++;
464147883Sscottl		goto done;
465147883Sscottl	}
466147883Sscottl
467147883Sscottl	if (iplen + sizeof(*ph) > m->m_len) {
468147883Sscottl		if ((m = m_pullup(m, iplen + sizeof(*ph))) == NULL) {
469147883Sscottl			pfsyncstats.pfsyncs_hdrops++;
470147883Sscottl			goto done;
471147883Sscottl		}
472147883Sscottl		ip = mtod(m, struct ip *);
473147883Sscottl	}
474147883Sscottl	ph = (struct pfsync_header *)((char *)ip + iplen);
475147883Sscottl
476147883Sscottl	/* verify the version */
477147883Sscottl	if (ph->version != PFSYNC_VERSION) {
478147883Sscottl		pfsyncstats.pfsyncs_badver++;
479147883Sscottl		goto done;
480147883Sscottl	}
481147883Sscottl
482147883Sscottl	action = ph->action;
483147883Sscottl	count = ph->count;
484147883Sscottl
485147883Sscottl	/* make sure it's a valid action code */
486147883Sscottl	if (action >= PFSYNC_ACT_MAX) {
487147883Sscottl		pfsyncstats.pfsyncs_badact++;
488147883Sscottl		goto done;
489147883Sscottl	}
490147883Sscottl
491101704Smjacob	/* Cheaper to grab this now than having to mess with mbufs later */
492101704Smjacob	src = ip->ip_src;
493147883Sscottl
494147883Sscottl	switch (action) {
495147883Sscottl	case PFSYNC_ACT_CLR: {
496147883Sscottl		struct pf_state *nexts;
497147883Sscottl		struct pfi_kif	*kif;
498147883Sscottl		u_int32_t creatorid;
499147883Sscottl		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
500147883Sscottl		    sizeof(*cp), &offp)) == NULL) {
501101704Smjacob			pfsyncstats.pfsyncs_badlen++;
502147883Sscottl			return;
503147883Sscottl		}
504147883Sscottl		cp = (struct pfsync_state_clr *)(mp->m_data + offp);
505147883Sscottl		creatorid = cp->creatorid;
506158933Smjacob
507101704Smjacob		s = splsoftnet();
508101704Smjacob#ifdef __FreeBSD__
509147883Sscottl		PF_LOCK();
510147883Sscottl#endif
511147883Sscottl		if (cp->ifname[0] == '\0') {
512147883Sscottl			for (st = RB_MIN(pf_state_tree_id, &tree_id);
513147883Sscottl			    st; st = nexts) {
514147883Sscottl                		nexts = RB_NEXT(pf_state_tree_id, &tree_id, st);
515147883Sscottl				if (st->creatorid == creatorid) {
516147883Sscottl					st->timeout = PFTM_PURGE;
517147883Sscottl					pf_purge_expired_state(st);
518147883Sscottl				}
519147883Sscottl			}
520147883Sscottl		} else {
521147883Sscottl			kif = pfi_lookup_if(cp->ifname);
522147883Sscottl			if (kif == NULL) {
523147883Sscottl				if (pf_status.debug >= PF_DEBUG_MISC)
524147883Sscottl					printf("pfsync_input: PFSYNC_ACT_CLR "
525101704Smjacob					    "bad interface: %s\n", cp->ifname);
526101704Smjacob				splx(s);
527147883Sscottl#ifdef __FreeBSD__
528147883Sscottl				PF_UNLOCK();
529147883Sscottl#endif
530147883Sscottl				goto done;
531147883Sscottl			}
532101704Smjacob			for (st = RB_MIN(pf_state_tree_lan_ext,
533147883Sscottl			    &kif->pfik_lan_ext); st; st = nexts) {
534101704Smjacob				nexts = RB_NEXT(pf_state_tree_lan_ext,
535170252Sscottl				    &kif->pfik_lan_ext, st);
536170252Sscottl				if (st->creatorid == creatorid) {
537170252Sscottl					st->timeout = PFTM_PURGE;
538170252Sscottl					pf_purge_expired_state(st);
539170252Sscottl				}
540170252Sscottl			}
541170252Sscottl		}
542170252Sscottl#ifdef __FreeBSD__
543170252Sscottl		PF_UNLOCK();
544170252Sscottl#endif
545170252Sscottl		splx(s);
546170252Sscottl
547170252Sscottl		break;
548170252Sscottl	}
549170252Sscottl	case PFSYNC_ACT_INS:
550170252Sscottl		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
551170252Sscottl		    count * sizeof(*sp), &offp)) == NULL) {
552170252Sscottl			pfsyncstats.pfsyncs_badlen++;
553170252Sscottl			return;
554170252Sscottl		}
555170252Sscottl
556170252Sscottl		s = splsoftnet();
557170252Sscottl#ifdef __FreeBSD__
558170252Sscottl		PF_LOCK();
559170252Sscottl#endif
560170252Sscottl		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
561170252Sscottl		    i < count; i++, sp++) {
562170252Sscottl			/* check for invalid values */
563170252Sscottl			if (sp->timeout >= PFTM_MAX ||
564170252Sscottl			    sp->src.state > PF_TCPS_PROXY_DST ||
565147883Sscottl			    sp->dst.state > PF_TCPS_PROXY_DST ||
566147883Sscottl			    sp->direction > PF_OUT ||
567147883Sscottl			    (sp->af != AF_INET && sp->af != AF_INET6)) {
568157662Smjacob				if (pf_status.debug >= PF_DEBUG_MISC)
569147883Sscottl					printf("pfsync_insert: PFSYNC_ACT_INS: "
570160290Smjacob					    "invalid value\n");
571231518Smarius				pfsyncstats.pfsyncs_badstate++;
572157117Smjacob				continue;
573165814Smjacob			}
574164990Smjacob
575164416Smjacob			if ((error = pfsync_insert_net_state(sp))) {
576157117Smjacob				if (error == ENOMEM) {
577157117Smjacob					splx(s);
578160290Smjacob#ifdef __FreeBSD__
579158982Smjacob					PF_UNLOCK();
580147883Sscottl#endif
581147883Sscottl					goto done;
582147883Sscottl				}
583147883Sscottl				continue;
584147883Sscottl			}
585147883Sscottl		}
586159178Smjacob#ifdef __FreeBSD__
587157117Smjacob		PF_UNLOCK();
588231518Smarius#endif
589231518Smarius		splx(s);
590101704Smjacob		break;
591160290Smjacob	case PFSYNC_ACT_UPD:
592160290Smjacob		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
593160290Smjacob		    count * sizeof(*sp), &offp)) == NULL) {
594147883Sscottl			pfsyncstats.pfsyncs_badlen++;
595165814Smjacob			return;
596165814Smjacob		}
597165814Smjacob
598101704Smjacob		s = splsoftnet();
599147883Sscottl#ifdef __FreeBSD__
600147883Sscottl		PF_LOCK();
601147883Sscottl#endif
602164990Smjacob		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
603101704Smjacob		    i < count; i++, sp++) {
604147883Sscottl			int flags = PFSYNC_FLAG_STALE;
605147883Sscottl
606147883Sscottl			/* check for invalid values */
607164990Smjacob			if (sp->timeout >= PFTM_MAX ||
608164990Smjacob			    sp->src.state > PF_TCPS_PROXY_DST ||
609102822Smjacob			    sp->dst.state > PF_TCPS_PROXY_DST) {
610147883Sscottl				if (pf_status.debug >= PF_DEBUG_MISC)
611147883Sscottl					printf("pfsync_insert: PFSYNC_ACT_UPD: "
612147883Sscottl					    "invalid value\n");
613147883Sscottl				pfsyncstats.pfsyncs_badstate++;
614147883Sscottl				continue;
615147883Sscottl			}
616147883Sscottl
617147883Sscottl			bcopy(sp->id, &key.id, sizeof(key.id));
618147883Sscottl			key.creatorid = sp->creatorid;
619147883Sscottl
620207287Smarius			st = pf_find_state_byid(&key);
621147883Sscottl			if (st == NULL) {
622147883Sscottl				/* insert the update */
623147883Sscottl				if (pfsync_insert_net_state(sp))
624147883Sscottl					pfsyncstats.pfsyncs_badstate++;
625147883Sscottl				continue;
626147883Sscottl			}
627147883Sscottl			sfail = 0;
628147883Sscottl			if (st->proto == IPPROTO_TCP) {
629207287Smarius				/*
630147883Sscottl				 * The state should never go backwards except
631147883Sscottl				 * for syn-proxy states.  Neither should the
632147883Sscottl				 * sequence window slide backwards.
633157117Smjacob				 */
634159919Smjacob				if (st->src.state > sp->src.state &&
635157117Smjacob				    (st->src.state < PF_TCPS_PROXY_SRC ||
636159919Smjacob				    sp->src.state >= PF_TCPS_PROXY_SRC))
637147883Sscottl					sfail = 1;
638147883Sscottl				else if (SEQ_GT(st->src.seqlo,
639160397Smjacob				    ntohl(sp->src.seqlo)))
640160397Smjacob					sfail = 3;
641160397Smjacob				else if (st->dst.state > sp->dst.state) {
642160397Smjacob					/* There might still be useful
643160397Smjacob					 * information about the src state here,
644160397Smjacob					 * so import that part of the update,
645160397Smjacob					 * then "fail" so we send the updated
646160397Smjacob					 * state back to the peer who is missing
647160397Smjacob					 * our what we know. */
648160397Smjacob					pf_state_peer_ntoh(&sp->src, &st->src);
649160397Smjacob					/* XXX do anything with timeouts? */
650160397Smjacob					sfail = 7;
651147883Sscottl					flags = 0;
652158982Smjacob				} else if (st->dst.state >= TCPS_SYN_SENT &&
653147883Sscottl				    SEQ_GT(st->dst.seqlo, ntohl(sp->dst.seqlo)))
654147883Sscottl					sfail = 4;
655147883Sscottl			} else {
656147883Sscottl				/*
657147883Sscottl				 * Non-TCP protocol state machine always go
658147883Sscottl				 * forwards
659147883Sscottl				 */
660147883Sscottl				if (st->src.state > sp->src.state)
661147883Sscottl					sfail = 5;
662147883Sscottl				else if ( st->dst.state > sp->dst.state)
663147883Sscottl					sfail = 6;
664147883Sscottl			}
665147883Sscottl			if (sfail) {
666147883Sscottl				if (pf_status.debug >= PF_DEBUG_MISC)
667152444Skan					printf("pfsync: %s stale update "
668147883Sscottl					    "(%d) id: %016llx "
669147883Sscottl					    "creatorid: %08x\n",
670147883Sscottl					    (sfail < 7 ?  "ignoring"
671147883Sscottl					     : "partial"), sfail,
672147883Sscottl#ifdef __FreeBSD__
673147883Sscottl					    (unsigned long long)be64toh(st->id),
674164305Sjhb#else
675147883Sscottl					    betoh64(st->id),
676220945Smarius#endif
677233403Smarius					    ntohl(st->creatorid));
678147883Sscottl				pfsyncstats.pfsyncs_badstate++;
679233403Smarius
680147883Sscottl				if (!(sp->sync_flags & PFSTATE_STALE)) {
681147883Sscottl					/* we have a better state, send it */
682147883Sscottl					if (sc->sc_mbuf != NULL && !stale)
683147883Sscottl						pfsync_sendout(sc);
684147883Sscottl					stale++;
685147883Sscottl					if (!st->sync_flags)
686147883Sscottl						pfsync_pack_state(
687147883Sscottl						    PFSYNC_ACT_UPD, st, flags);
688147883Sscottl				}
689147883Sscottl				continue;
690147883Sscottl			}
691147883Sscottl			pf_state_peer_ntoh(&sp->src, &st->src);
692147883Sscottl			pf_state_peer_ntoh(&sp->dst, &st->dst);
693147883Sscottl			st->expire = ntohl(sp->expire) + time_second;
694147883Sscottl			st->timeout = sp->timeout;
695147883Sscottl		}
696147883Sscottl		if (stale && sc->sc_mbuf != NULL)
697147883Sscottl			pfsync_sendout(sc);
698147883Sscottl#ifdef __FreeBSD__
699147883Sscottl		PF_UNLOCK();
700147883Sscottl#endif
701147883Sscottl		splx(s);
702157117Smjacob		break;
703147883Sscottl	/*
704155521Smjacob	 * It's not strictly necessary for us to support the "uncompressed"
705209599Sken	 * delete action, but it's relatively simple and maintains consistency.
706155521Smjacob	 */
707155521Smjacob	case PFSYNC_ACT_DEL:
708155521Smjacob		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
709155521Smjacob		    count * sizeof(*sp), &offp)) == NULL) {
710147883Sscottl			pfsyncstats.pfsyncs_badlen++;
711147883Sscottl			return;
712147883Sscottl		}
713147883Sscottl
714147883Sscottl		s = splsoftnet();
715147883Sscottl#ifdef __FreeBSD__
716147883Sscottl		PF_LOCK();
717147883Sscottl#endif
718147883Sscottl		for (i = 0, sp = (struct pfsync_state *)(mp->m_data + offp);
719147883Sscottl		    i < count; i++, sp++) {
720147883Sscottl			bcopy(sp->id, &key.id, sizeof(key.id));
721147883Sscottl			key.creatorid = sp->creatorid;
722147883Sscottl
723147883Sscottl			st = pf_find_state_byid(&key);
724147883Sscottl			if (st == NULL) {
725147883Sscottl				pfsyncstats.pfsyncs_badstate++;
726147883Sscottl				continue;
727147883Sscottl			}
728147883Sscottl			st->timeout = PFTM_PURGE;
729147883Sscottl			st->sync_flags |= PFSTATE_FROMSYNC;
730157117Smjacob			pf_purge_expired_state(st);
731157354Smjacob		}
732157354Smjacob#ifdef __FreeBSD__
733157354Smjacob		PF_UNLOCK();
734159312Smjacob#endif
735157354Smjacob		splx(s);
736157117Smjacob		break;
737157117Smjacob	case PFSYNC_ACT_UPD_C: {
738157117Smjacob		int update_requested = 0;
739157117Smjacob
740157662Smjacob		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
741147883Sscottl		    count * sizeof(*up), &offp)) == NULL) {
742157117Smjacob			pfsyncstats.pfsyncs_badlen++;
743157117Smjacob			return;
744157117Smjacob		}
745157117Smjacob
746157117Smjacob		s = splsoftnet();
747160391Smjacob#ifdef __FreeBSD__
748157117Smjacob		PF_LOCK();
749157117Smjacob#endif
750157662Smjacob		for (i = 0, up = (struct pfsync_state_upd *)(mp->m_data + offp);
751157117Smjacob		    i < count; i++, up++) {
752157117Smjacob			/* check for invalid values */
753157117Smjacob			if (up->timeout >= PFTM_MAX ||
754157662Smjacob			    up->src.state > PF_TCPS_PROXY_DST ||
755157662Smjacob			    up->dst.state > PF_TCPS_PROXY_DST) {
756157117Smjacob				if (pf_status.debug >= PF_DEBUG_MISC)
757157117Smjacob					printf("pfsync_insert: "
758159312Smjacob					    "PFSYNC_ACT_UPD_C: "
759147883Sscottl					    "invalid value\n");
760147883Sscottl				pfsyncstats.pfsyncs_badstate++;
761147883Sscottl				continue;
762147883Sscottl			}
763147883Sscottl
764147883Sscottl			bcopy(up->id, &key.id, sizeof(key.id));
765147883Sscottl			key.creatorid = up->creatorid;
766159178Smjacob
767147883Sscottl			st = pf_find_state_byid(&key);
768170252Sscottl			if (st == NULL) {
769170252Sscottl				/* We don't have this state. Ask for it. */
770170252Sscottl				error = pfsync_request_update(up, &src);
771147883Sscottl				if (error == ENOMEM) {
772147883Sscottl					splx(s);
773147883Sscottl					goto done;
774178814Sjhb				}
775178814Sjhb				update_requested = 1;
776178814Sjhb				pfsyncstats.pfsyncs_badstate++;
777147883Sscottl				continue;
778147883Sscottl			}
779147883Sscottl			sfail = 0;
780157662Smjacob			if (st->proto == IPPROTO_TCP) {
781157662Smjacob				/*
782157662Smjacob				 * The state should never go backwards except
783157662Smjacob				 * for syn-proxy states.  Neither should the
784157662Smjacob				 * sequence window slide backwards.
785157662Smjacob				 */
786157662Smjacob				if (st->src.state > up->src.state &&
787157662Smjacob				    (st->src.state < PF_TCPS_PROXY_SRC ||
788157662Smjacob				    up->src.state >= PF_TCPS_PROXY_SRC))
789157662Smjacob					sfail = 1;
790157117Smjacob				else if (st->dst.state > up->dst.state)
791147883Sscottl					sfail = 2;
792147883Sscottl				else if (SEQ_GT(st->src.seqlo,
793147883Sscottl				    ntohl(up->src.seqlo)))
794147883Sscottl					sfail = 3;
795147883Sscottl				else if (st->dst.state >= TCPS_SYN_SENT &&
796147883Sscottl				    SEQ_GT(st->dst.seqlo, ntohl(up->dst.seqlo)))
797147883Sscottl					sfail = 4;
798147883Sscottl			} else {
799147883Sscottl				/*
800147883Sscottl				 * Non-TCP protocol state machine always go
801147883Sscottl				 * forwards
802147883Sscottl				 */
803157662Smjacob				if (st->src.state > up->src.state)
804169293Smjacob					sfail = 5;
805147883Sscottl				else if (st->dst.state > up->dst.state)
806147883Sscottl					sfail = 6;
807169293Smjacob			}
808198262Skan			if (sfail) {
809169293Smjacob				if (pf_status.debug >= PF_DEBUG_MISC)
810169293Smjacob					printf("pfsync: ignoring stale update "
811198262Skan					    "(%d) id: %016llx "
812198262Skan					    "creatorid: %08x\n", sfail,
813198262Skan#ifdef __FreeBSD__
814198262Skan					    (unsigned long long)be64toh(st->id),
815157662Smjacob#else
816147883Sscottl					    betoh64(st->id),
817147883Sscottl#endif
818147883Sscottl					    ntohl(st->creatorid));
819147883Sscottl				pfsyncstats.pfsyncs_badstate++;
820147883Sscottl
821147883Sscottl				/* we have a better state, send it out */
822147883Sscottl				if ((!stale || update_requested) &&
823147883Sscottl				    sc->sc_mbuf != NULL) {
824147883Sscottl					pfsync_sendout(sc);
825147883Sscottl					update_requested = 0;
826147883Sscottl				}
827147883Sscottl				stale++;
828147883Sscottl				if (!st->sync_flags)
829147883Sscottl					pfsync_pack_state(PFSYNC_ACT_UPD, st,
830147883Sscottl					    PFSYNC_FLAG_STALE);
831147883Sscottl				continue;
832147883Sscottl			}
833147883Sscottl			pf_state_peer_ntoh(&up->src, &st->src);
834147883Sscottl			pf_state_peer_ntoh(&up->dst, &st->dst);
835147883Sscottl			st->expire = ntohl(up->expire) + time_second;
836147883Sscottl			st->timeout = up->timeout;
837147883Sscottl		}
838147883Sscottl		if ((update_requested || stale) && sc->sc_mbuf)
839147883Sscottl			pfsync_sendout(sc);
840147883Sscottl#ifdef __FreeBSD__
841147883Sscottl		PF_UNLOCK();
842231518Smarius#endif
843147883Sscottl		splx(s);
844147883Sscottl		break;
845147883Sscottl	}
846147883Sscottl	case PFSYNC_ACT_DEL_C:
847147883Sscottl		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
848147883Sscottl		    count * sizeof(*dp), &offp)) == NULL) {
849231518Smarius			pfsyncstats.pfsyncs_badlen++;
850147883Sscottl			return;
851147883Sscottl		}
852147883Sscottl
853147883Sscottl		s = splsoftnet();
854155521Smjacob#ifdef __FreeBSD__
855147883Sscottl		PF_LOCK();
856157117Smjacob#endif
857157117Smjacob		for (i = 0, dp = (struct pfsync_state_del *)(mp->m_data + offp);
858157117Smjacob		    i < count; i++, dp++) {
859155521Smjacob			bcopy(dp->id, &key.id, sizeof(key.id));
860155521Smjacob			key.creatorid = dp->creatorid;
861155521Smjacob
862147883Sscottl			st = pf_find_state_byid(&key);
863147883Sscottl			if (st == NULL) {
864157354Smjacob				pfsyncstats.pfsyncs_badstate++;
865157117Smjacob				continue;
866147883Sscottl			}
867147883Sscottl			st->timeout = PFTM_PURGE;
868147883Sscottl			st->sync_flags |= PFSTATE_FROMSYNC;
869157354Smjacob			pf_purge_expired_state(st);
870157117Smjacob		}
871147883Sscottl#ifdef __FreeBSD__
872147883Sscottl		PF_UNLOCK();
873147883Sscottl#endif
874147883Sscottl		splx(s);
875147883Sscottl		break;
876147883Sscottl	case PFSYNC_ACT_INS_F:
877147883Sscottl	case PFSYNC_ACT_DEL_F:
878147883Sscottl		/* not implemented */
879147883Sscottl		break;
880147883Sscottl	case PFSYNC_ACT_UREQ:
881147883Sscottl		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
882147883Sscottl		    count * sizeof(*rup), &offp)) == NULL) {
883147883Sscottl			pfsyncstats.pfsyncs_badlen++;
884147883Sscottl			return;
885158982Smjacob		}
886147883Sscottl
887147883Sscottl		s = splsoftnet();
888147883Sscottl#ifdef __FreeBSD__
889147883Sscottl		PF_LOCK();
890147883Sscottl#endif
891147883Sscottl		if (sc->sc_mbuf != NULL)
892147883Sscottl			pfsync_sendout(sc);
893147883Sscottl		for (i = 0,
894147883Sscottl		    rup = (struct pfsync_state_upd_req *)(mp->m_data + offp);
895147883Sscottl		    i < count; i++, rup++) {
896147883Sscottl			bcopy(rup->id, &key.id, sizeof(key.id));
897147883Sscottl			key.creatorid = rup->creatorid;
898147883Sscottl
899147883Sscottl			if (key.id == 0 && key.creatorid == 0) {
900147883Sscottl				sc->sc_ureq_received = time_uptime;
901147883Sscottl				if (pf_status.debug >= PF_DEBUG_MISC)
902147883Sscottl					printf("pfsync: received "
903147883Sscottl					    "bulk update request\n");
904147883Sscottl				pfsync_send_bus(sc, PFSYNC_BUS_START);
905147883Sscottl#ifdef __FreeBSD__
906147883Sscottl				callout_reset(&sc->sc_bulk_tmo, 1 * hz,
907147883Sscottl				    pfsync_bulk_update,
908147883Sscottl				    LIST_FIRST(&pfsync_list));
909157354Smjacob#else
910157354Smjacob				timeout_add(&sc->sc_bulk_tmo, 1 * hz);
911157354Smjacob#endif
912220945Smarius			} else {
913155521Smjacob				st = pf_find_state_byid(&key);
914164990Smjacob				if (st == NULL) {
915155521Smjacob					pfsyncstats.pfsyncs_badstate++;
916155521Smjacob					continue;
917155521Smjacob				}
918155521Smjacob				if (!st->sync_flags)
919155521Smjacob					pfsync_pack_state(PFSYNC_ACT_UPD,
920155521Smjacob					    st, 0);
921155521Smjacob			}
922147883Sscottl		}
923155521Smjacob		if (sc->sc_mbuf != NULL)
924155521Smjacob			pfsync_sendout(sc);
925155521Smjacob#ifdef __FreeBSD__
926155521Smjacob		PF_UNLOCK();
927155521Smjacob#endif
928147883Sscottl		splx(s);
929155521Smjacob		break;
930155521Smjacob	case PFSYNC_ACT_BUS:
931155521Smjacob		/* If we're not waiting for a bulk update, who cares. */
932147883Sscottl		if (sc->sc_ureq_sent == 0)
933147883Sscottl			break;
934147883Sscottl
935147883Sscottl		if ((mp = m_pulldown(m, iplen + sizeof(*ph),
936157117Smjacob		    sizeof(*bus), &offp)) == NULL) {
937160290Smjacob			pfsyncstats.pfsyncs_badlen++;
938158933Smjacob			return;
939158933Smjacob		}
940147883Sscottl		bus = (struct pfsync_state_bus *)(mp->m_data + offp);
941147883Sscottl		switch (bus->status) {
942147883Sscottl		case PFSYNC_BUS_START:
943147883Sscottl#ifdef __FreeBSD__
944147883Sscottl			callout_reset(&sc->sc_bulkfail_tmo,
945147883Sscottl			    pf_pool_limits[PF_LIMIT_STATES].limit /
946159049Smjacob			    (PFSYNC_BULKPACKETS * sc->sc_maxcount),
947147883Sscottl			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
948157117Smjacob#else
949157117Smjacob			timeout_add(&sc->sc_bulkfail_tmo,
950157117Smjacob			    pf_pool_limits[PF_LIMIT_STATES].limit /
951155521Smjacob			    (PFSYNC_BULKPACKETS * sc->sc_maxcount));
952155521Smjacob#endif
953147883Sscottl			if (pf_status.debug >= PF_DEBUG_MISC)
954147883Sscottl				printf("pfsync: received bulk "
955147883Sscottl				    "update start\n");
956147883Sscottl			break;
957147883Sscottl		case PFSYNC_BUS_END:
958147883Sscottl			if (time_uptime - ntohl(bus->endtime) >=
959147883Sscottl			    sc->sc_ureq_sent) {
960147883Sscottl				/* that's it, we're happy */
961224493Smarius				sc->sc_ureq_sent = 0;
962215325Smarius				sc->sc_bulk_tries = 0;
963215325Smarius#ifdef __FreeBSD__
964215325Smarius				callout_stop(&sc->sc_bulkfail_tmo);
965215325Smarius#else
966147883Sscottl				timeout_del(&sc->sc_bulkfail_tmo);
967224493Smarius#endif
968241874Smarius#if NCARP > 0	/* XXX_IMPORT */
969157117Smjacob				if (!pfsync_sync_ok)
970157117Smjacob					carp_suppress_preempt--;
971157117Smjacob#endif
972157117Smjacob				pfsync_sync_ok = 1;
973147883Sscottl				if (pf_status.debug >= PF_DEBUG_MISC)
974157117Smjacob					printf("pfsync: received valid "
975157117Smjacob					    "bulk update end\n");
976157117Smjacob			} else {
977157117Smjacob				if (pf_status.debug >= PF_DEBUG_MISC)
978157117Smjacob					printf("pfsync: received invalid "
979157117Smjacob					    "bulk update end: bad timestamp\n");
980157117Smjacob			}
981157117Smjacob			break;
982157117Smjacob		}
983157117Smjacob		break;
984157117Smjacob	}
985157117Smjacob
986157117Smjacobdone:
987157117Smjacob	if (m)
988157117Smjacob		m_freem(m);
989157117Smjacob}
990157117Smjacob
991157117Smjacobint
992157117Smjacobpfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
993157117Smjacob	struct rtentry *rt)
994157117Smjacob{
995157117Smjacob	m_freem(m);
996157117Smjacob	return (0);
997157354Smjacob}
998231228Smarius
999157117Smjacob/* ARGSUSED */
1000157117Smjacobint
1001157117Smjacobpfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1002157117Smjacob{
1003213147Smarius#ifndef __FreeBSD__
1004157662Smjacob	struct proc *p = curproc;
1005157662Smjacob#endif
1006157662Smjacob	struct pfsync_softc *sc = ifp->if_softc;
1007157662Smjacob	struct ifreq *ifr = (struct ifreq *)data;
1008157662Smjacob	struct ip_moptions *imo = &sc->sc_imo;
1009157662Smjacob	struct pfsyncreq pfsyncr;
1010157662Smjacob	struct ifnet    *sifp;
1011157662Smjacob	int s, error;
1012157662Smjacob
1013157662Smjacob	switch (cmd) {
1014157662Smjacob	case SIOCSIFADDR:
1015157662Smjacob	case SIOCAIFADDR:
1016157662Smjacob	case SIOCSIFDSTADDR:
1017157662Smjacob	case SIOCSIFFLAGS:
1018157662Smjacob#ifdef __FreeBSD__
1019157662Smjacob		if (ifp->if_flags & IFF_UP)
1020157662Smjacob			ifp->if_drv_flags |= IFF_DRV_RUNNING;
1021157662Smjacob		else
1022157662Smjacob			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1023157662Smjacob#else
1024157662Smjacob		if (ifp->if_flags & IFF_UP)
1025157662Smjacob			ifp->if_flags |= IFF_RUNNING;
1026157662Smjacob		else
1027157662Smjacob			ifp->if_flags &= ~IFF_RUNNING;
1028157662Smjacob#endif
1029157662Smjacob		break;
1030157662Smjacob	case SIOCSIFMTU:
1031157662Smjacob		if (ifr->ifr_mtu < PFSYNC_MINMTU)
1032157662Smjacob			return (EINVAL);
1033157662Smjacob		if (ifr->ifr_mtu > MCLBYTES)
1034157662Smjacob			ifr->ifr_mtu = MCLBYTES;
1035157662Smjacob		s = splnet();
1036157662Smjacob#ifdef __FreeBSD__
1037157662Smjacob		PF_LOCK();
1038157662Smjacob#endif
1039157662Smjacob		if (ifr->ifr_mtu < ifp->if_mtu) {
1040157662Smjacob			pfsync_sendout(sc);
1041157662Smjacob		}
1042213147Smarius		pfsync_setmtu(sc, ifr->ifr_mtu);
1043213147Smarius#ifdef __FreeBSD__
1044213147Smarius		PF_UNLOCK();
1045213147Smarius#endif
1046213147Smarius		splx(s);
1047213147Smarius		break;
1048157662Smjacob	case SIOCGETPFSYNC:
1049157662Smjacob#ifdef __FreeBSD__
1050157662Smjacob		/* XXX: read unlocked */
1051157662Smjacob#endif
1052157662Smjacob		bzero(&pfsyncr, sizeof(pfsyncr));
1053157662Smjacob		if (sc->sc_sync_ifp)
1054157662Smjacob			strlcpy(pfsyncr.pfsyncr_syncdev,
1055157662Smjacob			    sc->sc_sync_ifp->if_xname, IFNAMSIZ);
1056157662Smjacob		pfsyncr.pfsyncr_syncpeer = sc->sc_sync_peer;
1057157662Smjacob		pfsyncr.pfsyncr_maxupdates = sc->sc_maxupdates;
1058157662Smjacob		if ((error = copyout(&pfsyncr, ifr->ifr_data, sizeof(pfsyncr))))
1059157662Smjacob			return (error);
1060157662Smjacob		break;
1061157662Smjacob	case SIOCSETPFSYNC:
1062157662Smjacob#ifdef __FreeBSD__
1063157662Smjacob		if ((error = priv_check(curthread, PRIV_NETINET_PF)) != 0)
1064157662Smjacob#else
1065231228Smarius		if ((error = suser(p, p->p_acflag)) != 0)
1066157662Smjacob#endif
1067157662Smjacob			return (error);
1068157662Smjacob		if ((error = copyin(ifr->ifr_data, &pfsyncr, sizeof(pfsyncr))))
1069157662Smjacob			return (error);
1070157662Smjacob
1071157662Smjacob		if (pfsyncr.pfsyncr_syncpeer.s_addr == 0)
1072157662Smjacob#ifdef __FreeBSD__
1073157662Smjacob			sc->sc_sync_peer.s_addr = htonl(INADDR_PFSYNC_GROUP);
1074157662Smjacob#else
1075157662Smjacob			sc->sc_sync_peer.s_addr = INADDR_PFSYNC_GROUP;
1076157662Smjacob#endif
1077157662Smjacob		else
1078157662Smjacob			sc->sc_sync_peer.s_addr =
1079231228Smarius			    pfsyncr.pfsyncr_syncpeer.s_addr;
1080157662Smjacob
1081157662Smjacob		if (pfsyncr.pfsyncr_maxupdates > 255)
1082157662Smjacob			return (EINVAL);
1083157662Smjacob#ifdef __FreeBSD__
1084157662Smjacob		callout_drain(&sc->sc_send_tmo);
1085231228Smarius		PF_LOCK();
1086157662Smjacob#endif
1087157662Smjacob		sc->sc_maxupdates = pfsyncr.pfsyncr_maxupdates;
1088157662Smjacob
1089157662Smjacob		if (pfsyncr.pfsyncr_syncdev[0] == 0) {
1090157117Smjacob			sc->sc_sync_ifp = NULL;
1091147883Sscottl			if (sc->sc_mbuf_net != NULL) {
1092160391Smjacob				/* Don't keep stale pfsync packets around. */
1093160391Smjacob				s = splnet();
1094160391Smjacob				m_freem(sc->sc_mbuf_net);
1095157117Smjacob				sc->sc_mbuf_net = NULL;
1096157117Smjacob				sc->sc_statep_net.s = NULL;
1097157117Smjacob				splx(s);
1098157117Smjacob			}
1099157117Smjacob			if (imo->imo_num_memberships > 0) {
1100157117Smjacob				in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
1101157117Smjacob				imo->imo_multicast_ifp = NULL;
1102157117Smjacob			}
1103157117Smjacob#ifdef __FreeBSD__
1104147883Sscottl			PF_UNLOCK();
1105147883Sscottl#endif
1106147883Sscottl			break;
1107147883Sscottl		}
1108147883Sscottl
1109147883Sscottl		if ((sifp = ifunit(pfsyncr.pfsyncr_syncdev)) == NULL) {
1110147883Sscottl#ifdef __FreeBSD__
1111147883Sscottl			PF_UNLOCK();
1112147883Sscottl#endif
1113147883Sscottl			return (EINVAL);
1114147883Sscottl		}
1115147883Sscottl
1116147883Sscottl		s = splnet();
1117147883Sscottl#ifdef __FreeBSD__
1118147883Sscottl		if (sifp->if_mtu < SCP2IFP(sc)->if_mtu ||
1119147883Sscottl#else
1120147883Sscottl		if (sifp->if_mtu < sc->sc_if.if_mtu ||
1121147883Sscottl#endif
1122147883Sscottl		    (sc->sc_sync_ifp != NULL &&
1123147883Sscottl		    sifp->if_mtu < sc->sc_sync_ifp->if_mtu) ||
1124147883Sscottl		    sifp->if_mtu < MCLBYTES - sizeof(struct ip))
1125147883Sscottl			pfsync_sendout(sc);
1126170252Sscottl		sc->sc_sync_ifp = sifp;
1127147883Sscottl
1128147883Sscottl#ifdef __FreeBSD__
1129170252Sscottl		pfsync_setmtu(sc, SCP2IFP(sc)->if_mtu);
1130170252Sscottl#else
1131170252Sscottl		pfsync_setmtu(sc, sc->sc_if.if_mtu);
1132170252Sscottl#endif
1133170252Sscottl
1134170252Sscottl		if (imo->imo_num_memberships > 0) {
1135170252Sscottl			in_delmulti(imo->imo_membership[--imo->imo_num_memberships]);
1136170252Sscottl			imo->imo_multicast_ifp = NULL;
1137170252Sscottl		}
1138170252Sscottl
1139147883Sscottl		if (sc->sc_sync_ifp &&
1140147883Sscottl#ifdef __FreeBSD__
1141147883Sscottl		    sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) {
1142147883Sscottl#else
1143147883Sscottl		    sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) {
1144147883Sscottl#endif
1145147883Sscottl			struct in_addr addr;
1146147883Sscottl
1147147883Sscottl			if (!(sc->sc_sync_ifp->if_flags & IFF_MULTICAST)) {
1148147883Sscottl				sc->sc_sync_ifp = NULL;
1149147883Sscottl#ifdef __FreeBSD__
1150147883Sscottl				PF_UNLOCK();
1151147883Sscottl#endif
1152147883Sscottl				splx(s);
1153147883Sscottl				return (EADDRNOTAVAIL);
1154147883Sscottl			}
1155147883Sscottl#ifdef __FreeBSD__
1156147883Sscottl			PF_UNLOCK();		/* addmulti mallocs w/ WAITOK */
1157147883Sscottl			addr.s_addr = htonl(INADDR_PFSYNC_GROUP);
1158147883Sscottl#else
1159147883Sscottl			addr.s_addr = INADDR_PFSYNC_GROUP;
1160147883Sscottl#endif
1161147883Sscottl
1162147883Sscottl			if ((imo->imo_membership[0] =
1163147883Sscottl			    in_addmulti(&addr, sc->sc_sync_ifp)) == NULL) {
1164147883Sscottl				sc->sc_sync_ifp = NULL;
1165147883Sscottl				splx(s);
1166147883Sscottl				return (ENOBUFS);
1167147883Sscottl			}
1168147883Sscottl			imo->imo_num_memberships++;
1169147883Sscottl			imo->imo_multicast_ifp = sc->sc_sync_ifp;
1170101704Smjacob			imo->imo_multicast_ttl = PFSYNC_DFLTTL;
1171101704Smjacob			imo->imo_multicast_loop = 0;
1172147883Sscottl#ifdef __FreeBSD__
1173101704Smjacob			PF_LOCK();
1174147883Sscottl#endif
1175147883Sscottl		}
1176101704Smjacob
1177101704Smjacob		if (sc->sc_sync_ifp ||
1178155521Smjacob#ifdef __FreeBSD__
1179101704Smjacob		    sc->sc_sendaddr.s_addr != htonl(INADDR_PFSYNC_GROUP)) {
1180#else
1181		    sc->sc_sendaddr.s_addr != INADDR_PFSYNC_GROUP) {
1182#endif
1183			/* Request a full state table update. */
1184			sc->sc_ureq_sent = time_uptime;
1185#if NCARP > 0
1186			if (pfsync_sync_ok)
1187				carp_suppress_preempt++;
1188#endif
1189			pfsync_sync_ok = 0;
1190			if (pf_status.debug >= PF_DEBUG_MISC)
1191				printf("pfsync: requesting bulk update\n");
1192#ifdef __FreeBSD__
1193			callout_reset(&sc->sc_bulkfail_tmo, 5 * hz,
1194			    pfsync_bulkfail, LIST_FIRST(&pfsync_list));
1195#else
1196			timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
1197#endif
1198			error = pfsync_request_update(NULL, NULL);
1199			if (error == ENOMEM) {
1200#ifdef __FreeBSD__
1201				PF_UNLOCK();
1202#endif
1203				splx(s);
1204				return (ENOMEM);
1205			}
1206			pfsync_sendout(sc);
1207		}
1208#ifdef __FreeBSD__
1209		PF_UNLOCK();
1210#endif
1211		splx(s);
1212
1213		break;
1214
1215	default:
1216		return (ENOTTY);
1217	}
1218
1219	return (0);
1220}
1221
1222void
1223pfsync_setmtu(struct pfsync_softc *sc, int mtu_req)
1224{
1225	int mtu;
1226
1227	if (sc->sc_sync_ifp && sc->sc_sync_ifp->if_mtu < mtu_req)
1228		mtu = sc->sc_sync_ifp->if_mtu;
1229	else
1230		mtu = mtu_req;
1231
1232	sc->sc_maxcount = (mtu - sizeof(struct pfsync_header)) /
1233	    sizeof(struct pfsync_state);
1234	if (sc->sc_maxcount > 254)
1235	    sc->sc_maxcount = 254;
1236#ifdef __FreeBSD__
1237	SCP2IFP(sc)->if_mtu = sizeof(struct pfsync_header) +
1238	    sc->sc_maxcount * sizeof(struct pfsync_state);
1239#else
1240	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
1241	    sc->sc_maxcount * sizeof(struct pfsync_state);
1242#endif
1243}
1244
1245struct mbuf *
1246pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action, void **sp)
1247{
1248	struct pfsync_header *h;
1249	struct mbuf *m;
1250	int len;
1251
1252#ifdef __FreeBSD__
1253	PF_ASSERT(MA_OWNED);
1254#endif
1255	MGETHDR(m, M_DONTWAIT, MT_DATA);
1256	if (m == NULL) {
1257#ifdef __FreeBSD__
1258		SCP2IFP(sc)->if_oerrors++;
1259#else
1260		sc->sc_if.if_oerrors++;
1261#endif
1262		return (NULL);
1263	}
1264
1265	switch (action) {
1266	case PFSYNC_ACT_CLR:
1267		len = sizeof(struct pfsync_header) +
1268		    sizeof(struct pfsync_state_clr);
1269		break;
1270	case PFSYNC_ACT_UPD_C:
1271		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd)) +
1272		    sizeof(struct pfsync_header);
1273		break;
1274	case PFSYNC_ACT_DEL_C:
1275		len = (sc->sc_maxcount * sizeof(struct pfsync_state_del)) +
1276		    sizeof(struct pfsync_header);
1277		break;
1278	case PFSYNC_ACT_UREQ:
1279		len = (sc->sc_maxcount * sizeof(struct pfsync_state_upd_req)) +
1280		    sizeof(struct pfsync_header);
1281		break;
1282	case PFSYNC_ACT_BUS:
1283		len = sizeof(struct pfsync_header) +
1284		    sizeof(struct pfsync_state_bus);
1285		break;
1286	default:
1287		len = (sc->sc_maxcount * sizeof(struct pfsync_state)) +
1288		    sizeof(struct pfsync_header);
1289		break;
1290	}
1291
1292	if (len > MHLEN) {
1293		MCLGET(m, M_DONTWAIT);
1294		if ((m->m_flags & M_EXT) == 0) {
1295			m_free(m);
1296#ifdef __FreeBSD__
1297			SCP2IFP(sc)->if_oerrors++;
1298#else
1299			sc->sc_if.if_oerrors++;
1300#endif
1301			return (NULL);
1302		}
1303		m->m_data += (MCLBYTES - len) &~ (sizeof(long) - 1);
1304	} else
1305		MH_ALIGN(m, len);
1306
1307	m->m_pkthdr.rcvif = NULL;
1308	m->m_pkthdr.len = m->m_len = sizeof(struct pfsync_header);
1309	h = mtod(m, struct pfsync_header *);
1310	h->version = PFSYNC_VERSION;
1311	h->af = 0;
1312	h->count = 0;
1313	h->action = action;
1314
1315	*sp = (void *)((char *)h + PFSYNC_HDRLEN);
1316#ifdef __FreeBSD__
1317	callout_reset(&sc->sc_tmo, hz, pfsync_timeout,
1318	    LIST_FIRST(&pfsync_list));
1319#else
1320	timeout_add(&sc->sc_tmo, hz);
1321#endif
1322	return (m);
1323}
1324
1325int
1326pfsync_pack_state(u_int8_t action, struct pf_state *st, int flags)
1327{
1328#ifdef __FreeBSD__
1329	struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list));
1330#else
1331	struct ifnet *ifp = &pfsyncif.sc_if;
1332#endif
1333	struct pfsync_softc *sc = ifp->if_softc;
1334	struct pfsync_header *h, *h_net;
1335	struct pfsync_state *sp = NULL;
1336	struct pfsync_state_upd *up = NULL;
1337	struct pfsync_state_del *dp = NULL;
1338	struct pf_rule *r;
1339	u_long secs;
1340	int s, ret = 0;
1341	u_int8_t i = 255, newaction = 0;
1342
1343#ifdef __FreeBSD__
1344	PF_ASSERT(MA_OWNED);
1345#endif
1346	/*
1347	 * If a packet falls in the forest and there's nobody around to
1348	 * hear, does it make a sound?
1349	 */
1350	if (ifp->if_bpf == NULL && sc->sc_sync_ifp == NULL &&
1351#ifdef __FreeBSD__
1352	    sc->sc_sync_peer.s_addr == htonl(INADDR_PFSYNC_GROUP)) {
1353#else
1354	    sc->sc_sync_peer.s_addr == INADDR_PFSYNC_GROUP) {
1355#endif
1356		/* Don't leave any stale pfsync packets hanging around. */
1357		if (sc->sc_mbuf != NULL) {
1358			m_freem(sc->sc_mbuf);
1359			sc->sc_mbuf = NULL;
1360			sc->sc_statep.s = NULL;
1361		}
1362		return (0);
1363	}
1364
1365	if (action >= PFSYNC_ACT_MAX)
1366		return (EINVAL);
1367
1368	s = splnet();
1369	if (sc->sc_mbuf == NULL) {
1370		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
1371		    (void *)&sc->sc_statep.s)) == NULL) {
1372			splx(s);
1373			return (ENOMEM);
1374		}
1375		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1376	} else {
1377		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1378		if (h->action != action) {
1379			pfsync_sendout(sc);
1380			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, action,
1381			    (void *)&sc->sc_statep.s)) == NULL) {
1382				splx(s);
1383				return (ENOMEM);
1384			}
1385			h = mtod(sc->sc_mbuf, struct pfsync_header *);
1386		} else {
1387			/*
1388			 * If it's an update, look in the packet to see if
1389			 * we already have an update for the state.
1390			 */
1391			if (action == PFSYNC_ACT_UPD && sc->sc_maxupdates) {
1392				struct pfsync_state *usp =
1393				    (void *)((char *)h + PFSYNC_HDRLEN);
1394
1395				for (i = 0; i < h->count; i++) {
1396					if (!memcmp(usp->id, &st->id,
1397					    PFSYNC_ID_LEN) &&
1398					    usp->creatorid == st->creatorid) {
1399						sp = usp;
1400						sp->updates++;
1401						break;
1402					}
1403					usp++;
1404				}
1405			}
1406		}
1407	}
1408
1409	secs = time_second;
1410
1411	st->pfsync_time = time_uptime;
1412	TAILQ_REMOVE(&state_updates, st, u.s.entry_updates);
1413	TAILQ_INSERT_TAIL(&state_updates, st, u.s.entry_updates);
1414
1415	if (sp == NULL) {
1416		/* not a "duplicate" update */
1417		i = 255;
1418		sp = sc->sc_statep.s++;
1419		sc->sc_mbuf->m_pkthdr.len =
1420		    sc->sc_mbuf->m_len += sizeof(struct pfsync_state);
1421		h->count++;
1422		bzero(sp, sizeof(*sp));
1423
1424		bcopy(&st->id, sp->id, sizeof(sp->id));
1425		sp->creatorid = st->creatorid;
1426
1427		strlcpy(sp->ifname, st->u.s.kif->pfik_name, sizeof(sp->ifname));
1428		pf_state_host_hton(&st->lan, &sp->lan);
1429		pf_state_host_hton(&st->gwy, &sp->gwy);
1430		pf_state_host_hton(&st->ext, &sp->ext);
1431
1432		bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
1433
1434		sp->creation = htonl(secs - st->creation);
1435		sp->packets[0] = htonl(st->packets[0]);
1436		sp->packets[1] = htonl(st->packets[1]);
1437		sp->bytes[0] = htonl(st->bytes[0]);
1438		sp->bytes[1] = htonl(st->bytes[1]);
1439		if ((r = st->rule.ptr) == NULL)
1440			sp->rule = htonl(-1);
1441		else
1442			sp->rule = htonl(r->nr);
1443		if ((r = st->anchor.ptr) == NULL)
1444			sp->anchor = htonl(-1);
1445		else
1446			sp->anchor = htonl(r->nr);
1447		sp->af = st->af;
1448		sp->proto = st->proto;
1449		sp->direction = st->direction;
1450		sp->log = st->log;
1451		sp->allow_opts = st->allow_opts;
1452		sp->timeout = st->timeout;
1453
1454		if (flags & PFSYNC_FLAG_STALE)
1455			sp->sync_flags |= PFSTATE_STALE;
1456	}
1457
1458	pf_state_peer_hton(&st->src, &sp->src);
1459	pf_state_peer_hton(&st->dst, &sp->dst);
1460
1461	if (st->expire <= secs)
1462		sp->expire = htonl(0);
1463	else
1464		sp->expire = htonl(st->expire - secs);
1465
1466	/* do we need to build "compressed" actions for network transfer? */
1467	if (sc->sc_sync_ifp && flags & PFSYNC_FLAG_COMPRESS) {
1468		switch (action) {
1469		case PFSYNC_ACT_UPD:
1470			newaction = PFSYNC_ACT_UPD_C;
1471			break;
1472		case PFSYNC_ACT_DEL:
1473			newaction = PFSYNC_ACT_DEL_C;
1474			break;
1475		default:
1476			/* by default we just send the uncompressed states */
1477			break;
1478		}
1479	}
1480
1481	if (newaction) {
1482		if (sc->sc_mbuf_net == NULL) {
1483			if ((sc->sc_mbuf_net = pfsync_get_mbuf(sc, newaction,
1484			    (void *)&sc->sc_statep_net.s)) == NULL) {
1485				splx(s);
1486				return (ENOMEM);
1487			}
1488		}
1489		h_net = mtod(sc->sc_mbuf_net, struct pfsync_header *);
1490
1491		switch (newaction) {
1492		case PFSYNC_ACT_UPD_C:
1493			if (i != 255) {
1494				up = (void *)((char *)h_net +
1495				    PFSYNC_HDRLEN + (i * sizeof(*up)));
1496				up->updates++;
1497			} else {
1498				h_net->count++;
1499				sc->sc_mbuf_net->m_pkthdr.len =
1500				    sc->sc_mbuf_net->m_len += sizeof(*up);
1501				up = sc->sc_statep_net.u++;
1502
1503				bzero(up, sizeof(*up));
1504				bcopy(&st->id, up->id, sizeof(up->id));
1505				up->creatorid = st->creatorid;
1506			}
1507			up->timeout = st->timeout;
1508			up->expire = sp->expire;
1509			up->src = sp->src;
1510			up->dst = sp->dst;
1511			break;
1512		case PFSYNC_ACT_DEL_C:
1513			sc->sc_mbuf_net->m_pkthdr.len =
1514			    sc->sc_mbuf_net->m_len += sizeof(*dp);
1515			dp = sc->sc_statep_net.d++;
1516			h_net->count++;
1517
1518			bzero(dp, sizeof(*dp));
1519			bcopy(&st->id, dp->id, sizeof(dp->id));
1520			dp->creatorid = st->creatorid;
1521			break;
1522		}
1523	}
1524
1525	if (h->count == sc->sc_maxcount ||
1526	    (sc->sc_maxupdates && (sp->updates >= sc->sc_maxupdates)))
1527		ret = pfsync_sendout(sc);
1528
1529	splx(s);
1530	return (ret);
1531}
1532
1533/* This must be called in splnet() */
1534int
1535pfsync_request_update(struct pfsync_state_upd *up, struct in_addr *src)
1536{
1537#ifdef __FreeBSD__
1538	struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list));
1539#else
1540	struct ifnet *ifp = &pfsyncif.sc_if;
1541#endif
1542	struct pfsync_header *h;
1543	struct pfsync_softc *sc = ifp->if_softc;
1544	struct pfsync_state_upd_req *rup;
1545	int ret = 0;
1546
1547#ifdef __FreeBSD__
1548	PF_ASSERT(MA_OWNED);
1549#endif
1550	if (sc->sc_mbuf == NULL) {
1551		if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
1552		    (void *)&sc->sc_statep.s)) == NULL)
1553			return (ENOMEM);
1554		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1555	} else {
1556		h = mtod(sc->sc_mbuf, struct pfsync_header *);
1557		if (h->action != PFSYNC_ACT_UREQ) {
1558			pfsync_sendout(sc);
1559			if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_UREQ,
1560			    (void *)&sc->sc_statep.s)) == NULL)
1561				return (ENOMEM);
1562			h = mtod(sc->sc_mbuf, struct pfsync_header *);
1563		}
1564	}
1565
1566	if (src != NULL)
1567		sc->sc_sendaddr = *src;
1568	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*rup);
1569	h->count++;
1570	rup = sc->sc_statep.r++;
1571	bzero(rup, sizeof(*rup));
1572	if (up != NULL) {
1573		bcopy(up->id, rup->id, sizeof(rup->id));
1574		rup->creatorid = up->creatorid;
1575	}
1576
1577	if (h->count == sc->sc_maxcount)
1578		ret = pfsync_sendout(sc);
1579
1580	return (ret);
1581}
1582
1583int
1584pfsync_clear_states(u_int32_t creatorid, char *ifname)
1585{
1586#ifdef __FreeBSD__
1587	struct ifnet *ifp = SCP2IFP(LIST_FIRST(&pfsync_list));
1588#else
1589	struct ifnet *ifp = &pfsyncif.sc_if;
1590#endif
1591	struct pfsync_softc *sc = ifp->if_softc;
1592	struct pfsync_state_clr *cp;
1593	int s, ret;
1594
1595	s = splnet();
1596#ifdef __FreeBSD__
1597	PF_ASSERT(MA_OWNED);
1598#endif
1599	if (sc->sc_mbuf != NULL)
1600		pfsync_sendout(sc);
1601	if ((sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR,
1602	    (void *)&sc->sc_statep.c)) == NULL) {
1603		splx(s);
1604		return (ENOMEM);
1605	}
1606	sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*cp);
1607	cp = sc->sc_statep.c;
1608	cp->creatorid = creatorid;
1609	if (ifname != NULL)
1610		strlcpy(cp->ifname, ifname, IFNAMSIZ);
1611
1612	ret = (pfsync_sendout(sc));
1613	splx(s);
1614	return (ret);
1615}
1616
1617void
1618pfsync_timeout(void *v)
1619{
1620	struct pfsync_softc *sc = v;
1621	int s;
1622
1623	s = splnet();
1624#ifdef __FreeBSD__
1625	PF_LOCK();
1626#endif
1627	pfsync_sendout(sc);
1628#ifdef __FreeBSD__
1629	PF_UNLOCK();
1630#endif
1631	splx(s);
1632}
1633
1634/* This must be called in splnet() */
1635void
1636pfsync_send_bus(struct pfsync_softc *sc, u_int8_t status)
1637{
1638	struct pfsync_state_bus *bus;
1639
1640#ifdef __FreeBSD__
1641	PF_ASSERT(MA_OWNED);
1642#endif
1643	if (sc->sc_mbuf != NULL)
1644		pfsync_sendout(sc);
1645
1646	if (pfsync_sync_ok &&
1647	    (sc->sc_mbuf = pfsync_get_mbuf(sc, PFSYNC_ACT_BUS,
1648	    (void *)&sc->sc_statep.b)) != NULL) {
1649		sc->sc_mbuf->m_pkthdr.len = sc->sc_mbuf->m_len += sizeof(*bus);
1650		bus = sc->sc_statep.b;
1651		bus->creatorid = pf_status.hostid;
1652		bus->status = status;
1653		bus->endtime = htonl(time_uptime - sc->sc_ureq_received);
1654		pfsync_sendout(sc);
1655	}
1656}
1657
1658void
1659pfsync_bulk_update(void *v)
1660{
1661	struct pfsync_softc *sc = v;
1662	int s, i = 0;
1663	struct pf_state *state;
1664
1665#ifdef __FreeBSD__
1666	PF_LOCK();
1667#endif
1668	s = splnet();
1669	if (sc->sc_mbuf != NULL)
1670		pfsync_sendout(sc);
1671
1672	/*
1673	 * Grab at most PFSYNC_BULKPACKETS worth of states which have not
1674	 * been sent since the latest request was made.
1675	 */
1676	while ((state = TAILQ_FIRST(&state_updates)) != NULL &&
1677	    ++i < (sc->sc_maxcount * PFSYNC_BULKPACKETS)) {
1678		if (state->pfsync_time > sc->sc_ureq_received) {
1679			/* we're done */
1680			pfsync_send_bus(sc, PFSYNC_BUS_END);
1681			sc->sc_ureq_received = 0;
1682#ifdef __FreeBSD__
1683			callout_stop(&sc->sc_bulk_tmo);
1684#else
1685			timeout_del(&sc->sc_bulk_tmo);
1686#endif
1687			if (pf_status.debug >= PF_DEBUG_MISC)
1688				printf("pfsync: bulk update complete\n");
1689			break;
1690		} else {
1691			/* send an update and move to end of list */
1692			if (!state->sync_flags)
1693				pfsync_pack_state(PFSYNC_ACT_UPD, state, 0);
1694			state->pfsync_time = time_uptime;
1695			TAILQ_REMOVE(&state_updates, state, u.s.entry_updates);
1696			TAILQ_INSERT_TAIL(&state_updates, state,
1697			    u.s.entry_updates);
1698
1699			/* look again for more in a bit */
1700#ifdef __FreeBSD__
1701			callout_reset(&sc->sc_bulk_tmo, 1, pfsync_timeout,
1702			    LIST_FIRST(&pfsync_list));
1703#else
1704			timeout_add(&sc->sc_bulk_tmo, 1);
1705#endif
1706		}
1707	}
1708	if (sc->sc_mbuf != NULL)
1709		pfsync_sendout(sc);
1710	splx(s);
1711#ifdef __FreeBSD__
1712	PF_UNLOCK();
1713#endif
1714}
1715
1716void
1717pfsync_bulkfail(void *v)
1718{
1719	struct pfsync_softc *sc = v;
1720	int s, error;
1721
1722#ifdef __FreeBSD__
1723	PF_LOCK();
1724#endif
1725	if (sc->sc_bulk_tries++ < PFSYNC_MAX_BULKTRIES) {
1726		/* Try again in a bit */
1727#ifdef __FreeBSD__
1728		callout_reset(&sc->sc_bulkfail_tmo, 5 * hz, pfsync_bulkfail,
1729		    LIST_FIRST(&pfsync_list));
1730#else
1731		timeout_add(&sc->sc_bulkfail_tmo, 5 * hz);
1732#endif
1733		s = splnet();
1734		error = pfsync_request_update(NULL, NULL);
1735		if (error == ENOMEM) {
1736			if (pf_status.debug >= PF_DEBUG_MISC)
1737				printf("pfsync: cannot allocate mbufs for "
1738				    "bulk update\n");
1739		} else
1740			pfsync_sendout(sc);
1741		splx(s);
1742	} else {
1743		/* Pretend like the transfer was ok */
1744		sc->sc_ureq_sent = 0;
1745		sc->sc_bulk_tries = 0;
1746#if NCARP > 0
1747		if (!pfsync_sync_ok)
1748			carp_suppress_preempt--;
1749#endif
1750		pfsync_sync_ok = 1;
1751		if (pf_status.debug >= PF_DEBUG_MISC)
1752			printf("pfsync: failed to receive "
1753			    "bulk update status\n");
1754#ifdef __FreeBSD__
1755		callout_stop(&sc->sc_bulkfail_tmo);
1756#else
1757		timeout_del(&sc->sc_bulkfail_tmo);
1758#endif
1759	}
1760#ifdef __FreeBSD__
1761	PF_UNLOCK();
1762#endif
1763}
1764
1765/* This must be called in splnet() */
1766int
1767pfsync_sendout(sc)
1768	struct pfsync_softc *sc;
1769{
1770#if NBPFILTER > 0
1771# ifdef __FreeBSD__
1772	struct ifnet *ifp = SCP2IFP(sc);
1773# else
1774	struct ifnet *ifp = &sc->if_sc;
1775# endif
1776#endif
1777	struct mbuf *m;
1778
1779#ifdef __FreeBSD__
1780	PF_ASSERT(MA_OWNED);
1781	callout_stop(&sc->sc_tmo);
1782#else
1783	timeout_del(&sc->sc_tmo);
1784#endif
1785
1786	if (sc->sc_mbuf == NULL)
1787		return (0);
1788	m = sc->sc_mbuf;
1789	sc->sc_mbuf = NULL;
1790	sc->sc_statep.s = NULL;
1791
1792#ifdef __FreeBSD__
1793	KASSERT(m != NULL, ("pfsync_sendout: null mbuf"));
1794#endif
1795#if NBPFILTER > 0
1796#ifdef __FreeBSD__
1797	BPF_MTAP(ifp, m);
1798#else
1799	if (ifp->if_bpf)
1800		bpf_mtap(ifp->if_bpf, m);
1801#endif
1802#endif
1803
1804	if (sc->sc_mbuf_net) {
1805		m_freem(m);
1806		m = sc->sc_mbuf_net;
1807		sc->sc_mbuf_net = NULL;
1808		sc->sc_statep_net.s = NULL;
1809	}
1810
1811#ifdef __FreeBSD__
1812	if (sc->sc_sync_ifp ||
1813	    sc->sc_sync_peer.s_addr != htonl(INADDR_PFSYNC_GROUP)) {
1814#else
1815	if (sc->sc_sync_ifp ||sc->sc_sync_peer.s_addr != INADDR_PFSYNC_GROUP) {
1816#endif
1817		struct ip *ip;
1818		struct sockaddr sa;
1819
1820		M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
1821		if (m == NULL) {
1822			pfsyncstats.pfsyncs_onomem++;
1823			return (0);
1824		}
1825		ip = mtod(m, struct ip *);
1826		ip->ip_v = IPVERSION;
1827		ip->ip_hl = sizeof(*ip) >> 2;
1828		ip->ip_tos = IPTOS_LOWDELAY;
1829#ifdef __FreeBSD__
1830		ip->ip_len = m->m_pkthdr.len;
1831#else
1832		ip->ip_len = htons(m->m_pkthdr.len);
1833#endif
1834		ip->ip_id = htons(ip_randomid());
1835#ifdef __FreeBSD__
1836		ip->ip_off = IP_DF;
1837#else
1838		ip->ip_off = htons(IP_DF);
1839#endif
1840		ip->ip_ttl = PFSYNC_DFLTTL;
1841		ip->ip_p = IPPROTO_PFSYNC;
1842		ip->ip_sum = 0;
1843
1844		bzero(&sa, sizeof(sa));
1845		ip->ip_src.s_addr = INADDR_ANY;
1846
1847#ifdef __FreeBSD__
1848		if (sc->sc_sendaddr.s_addr == htonl(INADDR_PFSYNC_GROUP))
1849#else
1850		if (sc->sc_sendaddr.s_addr == INADDR_PFSYNC_GROUP)
1851#endif
1852			m->m_flags |= M_MCAST;
1853		ip->ip_dst = sc->sc_sendaddr;
1854		sc->sc_sendaddr.s_addr = sc->sc_sync_peer.s_addr;
1855
1856		pfsyncstats.pfsyncs_opackets++;
1857#ifdef __FreeBSD__
1858		if (!IF_HANDOFF(&sc->sc_ifq, m, NULL))
1859			pfsyncstats.pfsyncs_oerrors++;
1860		callout_reset(&sc->sc_send_tmo, 1, pfsync_senddef, sc);
1861#else
1862		if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))
1863			pfsyncstats.pfsyncs_oerrors++;
1864#endif
1865	} else
1866		m_freem(m);
1867
1868	return (0);
1869}
1870
1871#ifdef __FreeBSD__
1872static void
1873pfsync_senddef(void *arg)
1874{
1875	struct pfsync_softc *sc = (struct pfsync_softc *)arg;
1876	struct mbuf *m;
1877
1878	for(;;) {
1879		IF_DEQUEUE(&sc->sc_ifq, m);
1880		if (m == NULL)
1881			break;
1882		if (ip_output(m, NULL, NULL, IP_RAWOUTPUT, &sc->sc_imo, NULL))
1883			pfsyncstats.pfsyncs_oerrors++;
1884	}
1885}
1886
1887static int
1888pfsync_modevent(module_t mod, int type, void *data)
1889{
1890	int error = 0;
1891
1892	switch (type) {
1893	case MOD_LOAD:
1894		LIST_INIT(&pfsync_list);
1895		if_clone_attach(&pfsync_cloner);
1896		break;
1897
1898	case MOD_UNLOAD:
1899		if_clone_detach(&pfsync_cloner);
1900		break;
1901
1902	default:
1903		error = EINVAL;
1904		break;
1905	}
1906
1907	return error;
1908}
1909
1910static moduledata_t pfsync_mod = {
1911	"pfsync",
1912	pfsync_modevent,
1913	0
1914};
1915
1916#define PFSYNC_MODVER 1
1917
1918DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
1919MODULE_VERSION(pfsync, PFSYNC_MODVER);
1920#endif /* __FreeBSD__ */
1921