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