if_pfsync.c revision 128209
1/*	$FreeBSD: head/sys/contrib/pf/net/if_pfsync.c 128209 2004-04-14 00:57:49Z brooks $	*/
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#ifdef __FreeBSD__
31#include "opt_inet.h"
32#include "opt_inet6.h"
33#endif
34
35#ifndef __FreeBSD__
36#include "bpfilter.h"
37#include "pfsync.h"
38#elif __FreeBSD__ >= 5
39#include "opt_bpf.h"
40#include "opt_pf.h"
41#define	NBPFILTER	DEV_BPF
42#define	NPFSYNC		DEV_PFSYNC
43#endif
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/time.h>
48#include <sys/mbuf.h>
49#include <sys/socket.h>
50#ifdef __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#ifdef __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#ifndef __FreeBSD__
94struct pfsync_softc pfsyncif;
95#endif
96
97#ifdef __FreeBSD__
98static void	pfsync_clone_destroy(struct ifnet *);
99static int	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#ifndef __FreeBSD__
114extern int ifqmaxlen;
115#endif
116
117#ifdef __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
123static void
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
144static int
145pfsync_clone_create(struct if_clone *ifc, int unit)
146{
147	struct pfsync_softc *sc;
148
149	MALLOC(sc, struct pfsync_softc *, sizeof(*sc), M_PFSYNC,
150		M_WAITOK|M_ZERO);
151
152	sc->sc_count = 8;
153#if (__FreeBSD_version < 501113)
154	sc->sc_if.if_name = PFSYNCNAME;
155	sc->sc_if.if_unit = unit;
156#else
157	if_initname(&sc->sc_if, ifc->ifc_name, unit);
158#endif
159	sc->sc_if.if_ioctl = pfsyncioctl;
160	sc->sc_if.if_output = pfsyncoutput;
161	sc->sc_if.if_start = pfsyncstart;
162	sc->sc_if.if_type = IFT_PFSYNC;
163	sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen;
164	sc->sc_if.if_hdrlen = PFSYNC_HDRLEN;
165	sc->sc_if.if_baudrate = IF_Mbps(100);
166        sc->sc_if.if_softc = sc;
167	pfsync_setmtu(sc, MCLBYTES);
168	/*
169	 * XXX
170	 *  The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE
171	 * if Gaint lock is removed from the network stack.
172	 */
173	callout_init(&sc->sc_tmo, 0);
174	if_attach(&sc->sc_if);
175
176	LIST_INSERT_HEAD(&pfsync_list, sc, sc_next);
177#if NBPFILTER > 0
178	bpfattach(&sc->sc_if, DLT_PFSYNC, PFSYNC_HDRLEN);
179#endif
180
181	return (0);
182}
183#else /* !__FreeBSD__ */
184void
185pfsyncattach(int npfsync)
186{
187	struct ifnet *ifp;
188
189	pfsyncif.sc_mbuf = NULL;
190	pfsyncif.sc_ptr = NULL;
191	pfsyncif.sc_count = 8;
192	ifp = &pfsyncif.sc_if;
193	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
194	ifp->if_softc = &pfsyncif;
195	ifp->if_ioctl = pfsyncioctl;
196	ifp->if_output = pfsyncoutput;
197	ifp->if_start = pfsyncstart;
198	ifp->if_type = IFT_PFSYNC;
199	ifp->if_snd.ifq_maxlen = ifqmaxlen;
200	ifp->if_hdrlen = PFSYNC_HDRLEN;
201	ifp->if_baudrate = IF_Mbps(100);
202	pfsync_setmtu(&pfsyncif, MCLBYTES);
203	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
204	if_attach(ifp);
205	if_alloc_sadl(ifp);
206
207#if NBPFILTER > 0
208	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
209#endif
210}
211#endif
212
213/*
214 * Start output on the pfsync interface.
215 */
216void
217pfsyncstart(struct ifnet *ifp)
218{
219	struct mbuf *m;
220#if defined(__FreeBSD__) && defined(ALTQ)
221	struct ifaltq *ifq;
222#else
223	struct ifqueue *ifq;
224#endif
225	int s;
226
227#ifdef __FreeBSD__
228	ifq = &ifp->if_snd;
229#endif
230	for (;;) {
231		s = splimp();
232#ifdef __FreeBSD__
233		IF_LOCK(ifq);
234		_IF_DROP(ifq);
235		_IF_DEQUEUE(ifq, m);
236		IF_UNLOCK(ifq);
237#else
238		IF_DROP(&ifp->if_snd);
239		IF_DEQUEUE(&ifp->if_snd, m);
240#endif
241		splx(s);
242
243		if (m == NULL)
244			return;
245		else
246			m_freem(m);
247	}
248}
249
250int
251pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
252	struct rtentry *rt)
253{
254	m_freem(m);
255	return (0);
256}
257
258/* ARGSUSED */
259int
260pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
261{
262	struct pfsync_softc *sc = ifp->if_softc;
263	struct ifreq *ifr = (struct ifreq *)data;
264	int s;
265
266	switch (cmd) {
267	case SIOCSIFADDR:
268	case SIOCAIFADDR:
269	case SIOCSIFDSTADDR:
270	case SIOCSIFFLAGS:
271		if (ifp->if_flags & IFF_UP)
272			ifp->if_flags |= IFF_RUNNING;
273		else
274			ifp->if_flags &= ~IFF_RUNNING;
275		break;
276	case SIOCSIFMTU:
277		if (ifr->ifr_mtu < PFSYNC_MINMTU)
278			return (EINVAL);
279		if (ifr->ifr_mtu > MCLBYTES)
280			ifr->ifr_mtu = MCLBYTES;
281		s = splnet();
282		if (ifr->ifr_mtu < ifp->if_mtu)
283			pfsync_sendout(sc);
284		pfsync_setmtu(sc, ifr->ifr_mtu);
285		splx(s);
286		break;
287	default:
288		return (ENOTTY);
289	}
290
291	return (0);
292}
293
294void
295pfsync_setmtu(sc, mtu)
296	struct pfsync_softc *sc;
297	int mtu;
298{
299	sc->sc_count = (mtu - sizeof(struct pfsync_header)) /
300	    sizeof(struct pf_state);
301	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
302	    sc->sc_count * sizeof(struct pf_state);
303}
304
305struct mbuf *
306pfsync_get_mbuf(sc, action)
307	struct pfsync_softc *sc;
308	u_int8_t action;
309{
310#ifndef __FreeBSD__
311	extern int hz;
312#endif
313	struct pfsync_header *h;
314	struct mbuf *m;
315	int len;
316
317	MGETHDR(m, M_DONTWAIT, MT_DATA);
318	if (m == NULL) {
319		sc->sc_if.if_oerrors++;
320		return (NULL);
321	}
322
323	len = sc->sc_if.if_mtu;
324	if (len > MHLEN) {
325		MCLGET(m, M_DONTWAIT);
326		if ((m->m_flags & M_EXT) == 0) {
327			m_free(m);
328			sc->sc_if.if_oerrors++;
329			return (NULL);
330		}
331	}
332	m->m_pkthdr.rcvif = NULL;
333	m->m_pkthdr.len = m->m_len = len;
334
335	h = mtod(m, struct pfsync_header *);
336	h->version = PFSYNC_VERSION;
337	h->af = 0;
338	h->count = 0;
339	h->action = action;
340
341	sc->sc_mbuf = m;
342	sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN);
343#ifdef __FreeBSD__
344	callout_reset(&sc->sc_tmo, hz, pfsync_timeout,
345	    LIST_FIRST(&pfsync_list));
346#else
347	timeout_add(&sc->sc_tmo, hz);
348#endif
349
350	return (m);
351}
352
353/*
354 * XXX: This function should be called with PF_LOCK held as it references
355 * pf_state.
356 */
357int
358pfsync_pack_state(action, st)
359	u_int8_t action;
360	struct pf_state *st;
361{
362#ifdef __FreeBSD__
363	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
364#else
365	extern struct timeval time;
366	struct ifnet *ifp = &pfsyncif.sc_if;
367	struct pfsync_softc *sc = ifp->if_softc;
368#endif
369	struct pfsync_header *h;
370	struct pf_state *sp;
371	struct pf_rule *r = st->rule.ptr;
372	struct mbuf *m;
373	u_long secs;
374	int s, ret;
375
376	if (action >= PFSYNC_ACT_MAX)
377		return (EINVAL);
378
379#ifdef __FreeBSD__
380	/*
381	 * XXX
382	 *  If we need to check mutex owned, PF_LOCK should be
383	 * declared in pflog.ko.
384	 *
385	 * PF_LOCK_ASSERT();
386	 */
387	KASSERT((!LIST_EMPTY(&pfsync_list)), ("pfsync: no interface"));
388#endif
389	s = splnet();
390	m = sc->sc_mbuf;
391	if (m == NULL) {
392		if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
393			splx(s);
394			return (ENOMEM);
395		}
396		h = mtod(m, struct pfsync_header *);
397	} else {
398		h = mtod(m, struct pfsync_header *);
399		if (h->action != action) {
400			pfsync_sendout(sc);
401			if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
402				splx(s);
403				return (ENOMEM);
404			}
405			h = mtod(m, struct pfsync_header *);
406		}
407	}
408
409	sp = sc->sc_ptr++;
410	h->count++;
411	bzero(sp, sizeof(*sp));
412
413	bcopy(&st->lan, &sp->lan, sizeof(sp->lan));
414	bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy));
415	bcopy(&st->ext, &sp->ext, sizeof(sp->ext));
416
417	pf_state_peer_hton(&st->src, &sp->src);
418	pf_state_peer_hton(&st->dst, &sp->dst);
419
420	bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
421#ifdef __FreeBSD__
422	secs = time_second;
423#else
424	secs = time.tv_sec;
425#endif
426	sp->creation = htonl(secs - st->creation);
427	if (st->expire <= secs)
428		sp->expire = htonl(0);
429	else
430		sp->expire = htonl(st->expire - secs);
431	sp->packets[0] = htonl(st->packets[0]);
432	sp->packets[1] = htonl(st->packets[1]);
433	sp->bytes[0] = htonl(st->bytes[0]);
434	sp->bytes[1] = htonl(st->bytes[1]);
435	if (r == NULL)
436		sp->rule.nr = htonl(-1);
437	else
438		sp->rule.nr = htonl(r->nr);
439	sp->af = st->af;
440	sp->proto = st->proto;
441	sp->direction = st->direction;
442	sp->log = st->log;
443	sp->allow_opts = st->allow_opts;
444
445	ret = 0;
446	if (h->count == sc->sc_count)
447		ret = pfsync_sendout(sc);
448
449	splx(s);
450	return (0);
451}
452
453int
454pfsync_clear_state(st)
455	struct pf_state *st;
456{
457#ifdef __FreeBSD__
458	struct pfsync_softc *sc = LIST_FIRST(&pfsync_list);
459#else
460	struct ifnet *ifp = &pfsyncif.sc_if;
461	struct pfsync_softc *sc = ifp->if_softc;
462#endif
463	struct mbuf *m = sc->sc_mbuf;
464	int s, ret;
465
466	s = splnet();
467	if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) {
468		splx(s);
469		return (ENOMEM);
470	}
471
472	ret = (pfsync_sendout(sc));
473	splx(s);
474	return (ret);
475}
476
477void
478pfsync_timeout(void *v)
479{
480	struct pfsync_softc *sc = v;
481	int s;
482
483	/* We don't need PF_LOCK/PF_UNLOCK here! */
484	s = splnet();
485	pfsync_sendout(sc);
486	splx(s);
487}
488
489int
490pfsync_sendout(sc)
491	struct pfsync_softc *sc;
492{
493	struct ifnet *ifp = &sc->sc_if;
494	struct mbuf *m = sc->sc_mbuf;
495
496#ifdef __FreeBSD__
497	callout_stop(&sc->sc_tmo);
498#else
499	timeout_del(&sc->sc_tmo);
500#endif
501	sc->sc_mbuf = NULL;
502	sc->sc_ptr = NULL;
503
504#ifdef __FreeBSD__
505	KASSERT(m != NULL, ("pfsync_sendout: null mbuf"));
506#endif
507#if NBPFILTER > 0
508	if (ifp->if_bpf)
509		bpf_mtap(ifp->if_bpf, m);
510#endif
511
512	m_freem(m);
513
514	return (0);
515}
516
517
518#ifdef __FreeBSD__
519static int
520pfsync_modevent(module_t mod, int type, void *data)
521{
522	int error = 0;
523
524	switch (type) {
525	case MOD_LOAD:
526		LIST_INIT(&pfsync_list);
527		if_clone_attach(&pfsync_cloner);
528		break;
529
530	case MOD_UNLOAD:
531		if_clone_detach(&pfsync_cloner);
532		while (!LIST_EMPTY(&pfsync_list))
533			pfsync_clone_destroy(
534				&LIST_FIRST(&pfsync_list)->sc_if);
535		break;
536
537	default:
538		error = EINVAL;
539		break;
540	}
541
542	return error;
543}
544
545static moduledata_t pfsync_mod = {
546	"pfsync",
547	pfsync_modevent,
548	0
549};
550
551#define PFSYNC_MODVER 1
552
553DECLARE_MODULE(pfsync, pfsync_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
554MODULE_VERSION(pfsync, PFSYNC_MODVER);
555#endif /* __FreeBSD__ */
556