if_pfsync.c revision 126261
1/*	$FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 126261 2004-02-26 02:34:12Z mlaier $	*/
2/*	$OpenBSD: if_pfsync.c,v 1.6 2003/06/21 09:07:01 djm Exp $	*/
3
4/*
5 * Copyright (c) 2002 Michael Shalayeff
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#if defined(__FreeBSD__) && __FreeBSD__ >= 5
31#include "opt_inet.h"
32#include "opt_inet6.h"
33#endif
34
35#if !defined(__FreeBSD__)
36#include "bpfilter.h"
37#include "pfsync.h"
38#elif __FreeBSD__ >= 5
39#include "opt_bpf.h"
40#define NBPFILTER DEV_BPF
41#include "opt_pf.h"
42#define NPFSYNC DEV_PFSYNC
43#endif
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/time.h>
48#include <sys/mbuf.h>
49#include <sys/socket.h>
50#if defined(__FreeBSD__)
51#include <sys/kernel.h>
52#include <sys/malloc.h>
53#include <sys/sockio.h>
54#else
55#include <sys/ioctl.h>
56#include <sys/timeout.h>
57#endif
58
59#include <net/if.h>
60#include <net/if_types.h>
61#include <net/route.h>
62#include <net/bpf.h>
63
64#ifdef	INET
65#include <netinet/in.h>
66#include <netinet/in_var.h>
67#endif
68
69#ifdef INET6
70#ifndef INET
71#include <netinet/in.h>
72#endif
73#include <netinet6/nd6.h>
74#endif /* INET6 */
75
76#include <net/pfvar.h>
77#include <net/if_pfsync.h>
78
79#if defined(__FreeBSD__)
80#define PFSYNCNAME	"pfsync"
81#endif
82
83#define PFSYNC_MINMTU	\
84    (sizeof(struct pfsync_header) + sizeof(struct pf_state))
85
86#ifdef PFSYNCDEBUG
87#define DPRINTF(x)    do { if (pfsyncdebug) printf x ; } while (0)
88int pfsyncdebug;
89#else
90#define DPRINTF(x)
91#endif
92
93#if !defined(__FreeBSD__)
94struct pfsync_softc pfsyncif;
95#endif
96
97#if defined(__FreeBSD__)
98void	pfsync_clone_destroy(struct ifnet *);
99int	pfsync_clone_create(struct if_clone *, int);
100#else
101void	pfsyncattach(int);
102#endif
103void	pfsync_setmtu(struct pfsync_softc *sc, int);
104int	pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
105	       struct rtentry *);
106int	pfsyncioctl(struct ifnet *, u_long, caddr_t);
107void	pfsyncstart(struct ifnet *);
108
109struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action);
110int	pfsync_sendout(struct pfsync_softc *sc);
111void	pfsync_timeout(void *v);
112
113#if !defined(__FreeBSD__)
114extern int ifqmaxlen;
115#endif
116
117#if defined(__FreeBSD__)
118static MALLOC_DEFINE(M_PFSYNC, PFSYNCNAME, "Packet Filter State Sync. Interface");
119static LIST_HEAD(pfsync_list, pfsync_softc) pfsync_list;
120struct if_clone pfsync_cloner = IF_CLONE_INITIALIZER(PFSYNCNAME,
121	pfsync_clone_create, pfsync_clone_destroy, 1, IF_MAXUNIT);
122
123void
124pfsync_clone_destroy(struct ifnet *ifp)
125{
126        struct pfsync_softc *sc;
127
128        sc = ifp->if_softc;
129	callout_stop(&sc->sc_tmo);
130
131	/*
132	 * Does we really need this?
133	 */
134	IF_DRAIN(&ifp->if_snd);
135
136#if NBPFILTER > 0
137        bpfdetach(ifp);
138#endif
139        if_detach(ifp);
140        LIST_REMOVE(sc, sc_next);
141        free(sc, M_PFSYNC);
142}
143#endif /* __FreeBSD__ */
144
145#if defined(__FreeBSD__)
146int
147pfsync_clone_create(struct if_clone *ifc, int unit)
148{
149	struct pfsync_softc *sc;
150
151	MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC,
152		M_WAITOK|M_ZERO);
153
154	sc->sc_count = 8;
155#if (__FreeBSD_version < 501113)
156	sc->sc_if.if_name = PFSYNCNAME;
157	sc->sc_if.if_unit = unit;
158#else
159	if_initname(&sc->sc_if, ifc->ifc_name, unit);
160#endif
161	sc->sc_if.if_ioctl = pfsyncioctl;
162	sc->sc_if.if_output = pfsyncoutput;
163	sc->sc_if.if_start = pfsyncstart;
164	sc->sc_if.if_type = IFT_PFSYNC;
165	sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen;
166	sc->sc_if.if_hdrlen = PFSYNC_HDRLEN;
167	sc->sc_if.if_baudrate = IF_Mbps(100);
168        sc->sc_if.if_softc = sc;
169	pfsync_setmtu(sc, MCLBYTES);
170	/*
171	 * XXX
172	 *  The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE
173	 * if Gaint lock is removed from the network stack.
174	 */
175	callout_init(&sc->sc_tmo, 0);
176	if_attach(&sc->sc_if);
177
178	LIST_INSERT_HEAD(&pfsync_list, sc, sc_next);
179#if NBPFILTER > 0
180	bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN);
181#endif
182
183	return (0);
184}
185#else /* !__FreeBSD__ */
186void
187pfsyncattach(int npfsync)
188{
189	struct ifnet *ifp;
190
191	pfsyncif.sc_mbuf = NULL;
192	pfsyncif.sc_ptr = NULL;
193	pfsyncif.sc_count = 8;
194	ifp = &pfsyncif.sc_if;
195	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
196	ifp->if_softc = &pfsyncif;
197	ifp->if_ioctl = pfsyncioctl;
198	ifp->if_output = pfsyncoutput;
199	ifp->if_start = pfsyncstart;
200	ifp->if_type = IFT_PFSYNC;
201	ifp->if_snd.ifq_maxlen = ifqmaxlen;
202	ifp->if_hdrlen = PFSYNC_HDRLEN;
203	ifp->if_baudrate = IF_Mbps(100);
204	pfsync_setmtu(&pfsyncif, MCLBYTES);
205	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
206	if_attach(ifp);
207	if_alloc_sadl(ifp);
208
209#if NBPFILTER > 0
210	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
211#endif
212}
213#endif
214
215/*
216 * Start output on the pfsync interface.
217 */
218void
219pfsyncstart(struct ifnet *ifp)
220{
221	struct mbuf *m;
222#if defined(__FreeBSD__) && defined(ALTQ)
223	struct ifaltq *ifq;
224#else
225	struct ifqueue *ifq;
226#endif
227	int s;
228
229#if defined(__FreeBSD__)
230	ifq = &ifp->if_snd;
231#endif
232	for (;;) {
233		s = splimp();
234#if defined(__FreeBSD__)
235		IF_LOCK(ifq);
236		_IF_DROP(ifq);
237		_IF_DEQUEUE(ifq, m);
238		IF_UNLOCK(ifq);
239#else
240		IF_DROP(&ifp->if_snd);
241		IF_DEQUEUE(&ifp->if_snd, m);
242#endif
243		splx(s);
244
245		if (m == NULL)
246			return;
247		else
248			m_freem(m);
249	}
250}
251
252int
253pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
254	struct rtentry *rt)
255{
256	m_freem(m);
257	return (0);
258}
259
260/* ARGSUSED */
261int
262pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
263{
264	struct pfsync_softc *sc = ifp->if_softc;
265	struct ifreq *ifr = (struct ifreq *)data;
266	int s;
267
268	switch (cmd) {
269	case SIOCSIFADDR:
270	case SIOCAIFADDR:
271	case SIOCSIFDSTADDR:
272	case SIOCSIFFLAGS:
273		if (ifp->if_flags & IFF_UP)
274			ifp->if_flags |= IFF_RUNNING;
275		else
276			ifp->if_flags &= ~IFF_RUNNING;
277		break;
278	case SIOCSIFMTU:
279		if (ifr->ifr_mtu < PFSYNC_MINMTU)
280			return (EINVAL);
281		if (ifr->ifr_mtu > MCLBYTES)
282			ifr->ifr_mtu = MCLBYTES;
283		s = splnet();
284		if (ifr->ifr_mtu < ifp->if_mtu)
285			pfsync_sendout(sc);
286		pfsync_setmtu(sc, ifr->ifr_mtu);
287		splx(s);
288		break;
289	default:
290		return (ENOTTY);
291	}
292
293	return (0);
294}
295
296void
297pfsync_setmtu(sc, mtu)
298	struct pfsync_softc *sc;
299	int mtu;
300{
301	sc->sc_count = (mtu - sizeof(struct pfsync_header)) /
302	    sizeof(struct pf_state);
303	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
304	    sc->sc_count * sizeof(struct pf_state);
305}
306
307struct mbuf *
308pfsync_get_mbuf(sc, action)
309	struct pfsync_softc *sc;
310	u_int8_t action;
311{
312#if !defined(__FreeBSD__)
313	extern int hz;
314#endif
315	struct pfsync_header *h;
316	struct mbuf *m;
317	int len;
318
319	MGETHDR(m, M_DONTWAIT, MT_DATA);
320	if (m == NULL) {
321		sc->sc_if.if_oerrors++;
322		return (NULL);
323	}
324
325	len = sc->sc_if.if_mtu;
326	if (len > MHLEN) {
327		MCLGET(m, M_DONTWAIT);
328		if ((m->m_flags & M_EXT) == 0) {
329			m_free(m);
330			sc->sc_if.if_oerrors++;
331			return (NULL);
332		}
333	}
334	m->m_pkthdr.rcvif = NULL;
335	m->m_pkthdr.len = m->m_len = len;
336
337	h = mtod(m, struct pfsync_header *);
338	h->version = PFSYNC_VERSION;
339	h->af = 0;
340	h->count = 0;
341	h->action = action;
342
343	sc->sc_mbuf = m;
344	sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN);
345#if defined(__FreeBSD__)
346	callout_reset(&sc->sc_tmo, hz, pfsync_timeout,
347	    LIST_FIRST(&pfsync_list));
348#else
349	timeout_add(&sc->sc_tmo, hz);
350#endif
351
352	return (m);
353}
354
355/*
356 * XXX: This function should be called with PF_LOCK held as it references
357 * pf_state.
358 */
359int
360pfsync_pack_state(action, st)
361	u_int8_t action;
362	struct pf_state *st;
363{
364#if defined(__FreeBSD__)
365	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
366#else
367	extern struct timeval time;
368	struct ifnet *ifp = &pfsyncif.sc_if;
369	struct pfsync_softc *sc = ifp->if_softc;
370#endif
371	struct pfsync_header *h;
372	struct pf_state *sp;
373	struct pf_rule *r = st->rule.ptr;
374	struct mbuf *m;
375	u_long secs;
376	int s, ret;
377
378	if (action >= PFSYNC_ACT_MAX)
379		return (EINVAL);
380
381#if defined(__FreeBSD__)
382	/*
383	 * XXX
384	 *  If we need to check mutex owned, PF_LOCK should be
385	 * declared in pflog.ko. :-(
386	 *
387	 * PF_LOCK_ASSERT();
388	 */
389	KASSERT((!LIST_EMPTY(&pfsync_list)), ("pfsync: no interface"));
390#endif
391	s = splnet();
392	m = sc->sc_mbuf;
393	if (m == NULL) {
394		if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
395			splx(s);
396			return (ENOMEM);
397		}
398		h = mtod(m, struct pfsync_header *);
399	} else {
400		h = mtod(m, struct pfsync_header *);
401		if (h->action != action) {
402			pfsync_sendout(sc);
403			if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
404				splx(s);
405				return (ENOMEM);
406			}
407			h = mtod(m, struct pfsync_header *);
408		}
409	}
410
411	sp = sc->sc_ptr++;
412	h->count++;
413	bzero(sp, sizeof(*sp));
414
415	bcopy(&st->lan, &sp->lan, sizeof(sp->lan));
416	bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy));
417	bcopy(&st->ext, &sp->ext, sizeof(sp->ext));
418
419	pf_state_peer_hton(&st->src, &sp->src);
420	pf_state_peer_hton(&st->dst, &sp->dst);
421
422	bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
423#if defined(__FreeBSD__)
424	secs = time_second;
425#else
426	secs = time.tv_sec;
427#endif
428	sp->creation = htonl(secs - st->creation);
429	if (st->expire <= secs)
430		sp->expire = htonl(0);
431	else
432		sp->expire = htonl(st->expire - secs);
433	sp->packets[0] = htonl(st->packets[0]);
434	sp->packets[1] = htonl(st->packets[1]);
435	sp->bytes[0] = htonl(st->bytes[0]);
436	sp->bytes[1] = htonl(st->bytes[1]);
437	if (r == NULL)
438		sp->rule.nr = htonl(-1);
439	else
440		sp->rule.nr = htonl(r->nr);
441	sp->af = st->af;
442	sp->proto = st->proto;
443	sp->direction = st->direction;
444	sp->log = st->log;
445	sp->allow_opts = st->allow_opts;
446
447	ret = 0;
448	if (h->count == sc->sc_count)
449		ret = pfsync_sendout(sc);
450
451	splx(s);
452	return (0);
453}
454
455int
456pfsync_clear_state(st)
457	struct pf_state *st;
458{
459#if defined(__FreeBSD__)
460	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
461#else
462	struct ifnet *ifp = &pfsyncif.sc_if;
463	struct pfsync_softc *sc = ifp->if_softc;
464#endif
465	struct mbuf *m = sc->sc_mbuf;
466	int s, ret;
467
468	s = splnet();
469	if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) {
470		splx(s);
471		return (ENOMEM);
472	}
473
474	ret = (pfsync_sendout(sc));
475	splx(s);
476	return (ret);
477}
478
479void
480pfsync_timeout(void *v)
481{
482	struct pfsync_softc *sc = v;
483	int s;
484
485	/* We don't need PF_LOCK/PF_UNLOCK here! */
486	s = splnet();
487	pfsync_sendout(sc);
488	splx(s);
489}
490
491int
492pfsync_sendout(sc)
493	struct pfsync_softc *sc;
494{
495	struct ifnet *ifp = &sc->sc_if;
496	struct mbuf *m = sc->sc_mbuf;
497
498#if defined(__FreeBSD__)
499	callout_stop(&sc->sc_tmo);
500#else
501	timeout_del(&sc->sc_tmo);
502#endif
503	sc->sc_mbuf = NULL;
504	sc->sc_ptr = NULL;
505
506#if defined(__FreeBSD__)
507	KASSERT(m != NULL, ("pfsync_sendout: null mbuf"));
508#endif
509#if NBPFILTER > 0
510	if (ifp->if_bpf)
511		bpf_mtap(ifp->if_bpf, m);
512#endif
513
514	m_freem(m);
515
516	return (0);
517}
518
519
520#if defined(__FreeBSD__)
521static int
522pfsync_modevent(module_t mod, int type, void *data)
523{
524	int error = 0;
525
526	switch (type) {
527	case MOD_LOAD:
528		LIST_INIT(&pfsync_list);
529		if_clone_attach(&pfsync_cloner);
530		printf("pfsync: $Name:  $\n");
531		break;
532
533	case MOD_UNLOAD:
534		if_clone_detach(&pfsync_cloner);
535		while (!LIST_EMPTY(&pfsync_list))
536			pfsync_clone_destroy(
537				&LIST_FIRST(&pfsync_list)->sc_if);
538		break;
539
540	default:
541		error = EINVAL;
542		break;
543	}
544
545	return error;
546}
547
548static moduledata_t pfsync_mod = {
549	"pfsync",
550	pfsync_modevent,
551	0
552};
553
554#define PFSYNC_MODVER 1
555
556DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
557MODULE_VERSION(pfsync, PFSYNC_MODVER);
558#endif /* __FreeBSD__ */
559