if_pfsync.c revision 126258
1/*	$OpenBSD: if_pfsync.c,v 1.6 2003/06/21 09:07:01 djm Exp $	*/
2
3/*
4 * Copyright (c) 2002 Michael Shalayeff
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "bpfilter.h"
30#include "pfsync.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/time.h>
35#include <sys/mbuf.h>
36#include <sys/socket.h>
37#include <sys/ioctl.h>
38#include <sys/timeout.h>
39
40#include <net/if.h>
41#include <net/if_types.h>
42#include <net/route.h>
43#include <net/bpf.h>
44
45#ifdef	INET
46#include <netinet/in.h>
47#include <netinet/in_var.h>
48#endif
49
50#ifdef INET6
51#ifndef INET
52#include <netinet/in.h>
53#endif
54#include <netinet6/nd6.h>
55#endif /* INET6 */
56
57#include <net/pfvar.h>
58#include <net/if_pfsync.h>
59
60#define PFSYNC_MINMTU	\
61    (sizeof(struct pfsync_header) + sizeof(struct pf_state))
62
63#ifdef PFSYNCDEBUG
64#define DPRINTF(x)    do { if (pfsyncdebug) printf x ; } while (0)
65int pfsyncdebug;
66#else
67#define DPRINTF(x)
68#endif
69
70struct pfsync_softc pfsyncif;
71
72void	pfsyncattach(int);
73void	pfsync_setmtu(struct pfsync_softc *sc, int);
74int	pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
75	       struct rtentry *);
76int	pfsyncioctl(struct ifnet *, u_long, caddr_t);
77void	pfsyncstart(struct ifnet *);
78
79struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action);
80int	pfsync_sendout(struct pfsync_softc *sc);
81void	pfsync_timeout(void *v);
82
83extern int ifqmaxlen;
84
85void
86pfsyncattach(int npfsync)
87{
88	struct ifnet *ifp;
89
90	pfsyncif.sc_mbuf = NULL;
91	pfsyncif.sc_ptr = NULL;
92	pfsyncif.sc_count = 8;
93	ifp = &pfsyncif.sc_if;
94	strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname);
95	ifp->if_softc = &pfsyncif;
96	ifp->if_ioctl = pfsyncioctl;
97	ifp->if_output = pfsyncoutput;
98	ifp->if_start = pfsyncstart;
99	ifp->if_type = IFT_PFSYNC;
100	ifp->if_snd.ifq_maxlen = ifqmaxlen;
101	ifp->if_hdrlen = PFSYNC_HDRLEN;
102	ifp->if_baudrate = IF_Mbps(100);
103	pfsync_setmtu(&pfsyncif, MCLBYTES);
104	timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif);
105	if_attach(ifp);
106	if_alloc_sadl(ifp);
107
108#if NBPFILTER > 0
109	bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN);
110#endif
111}
112
113/*
114 * Start output on the pfsync interface.
115 */
116void
117pfsyncstart(struct ifnet *ifp)
118{
119	struct mbuf *m;
120	int s;
121
122	for (;;) {
123		s = splimp();
124		IF_DROP(&ifp->if_snd);
125		IF_DEQUEUE(&ifp->if_snd, m);
126		splx(s);
127
128		if (m == NULL)
129			return;
130		else
131			m_freem(m);
132	}
133}
134
135int
136pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
137	struct rtentry *rt)
138{
139	m_freem(m);
140	return (0);
141}
142
143/* ARGSUSED */
144int
145pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
146{
147	struct pfsync_softc *sc = ifp->if_softc;
148	struct ifreq *ifr = (struct ifreq *)data;
149	int s;
150
151	switch (cmd) {
152	case SIOCSIFADDR:
153	case SIOCAIFADDR:
154	case SIOCSIFDSTADDR:
155	case SIOCSIFFLAGS:
156		if (ifp->if_flags & IFF_UP)
157			ifp->if_flags |= IFF_RUNNING;
158		else
159			ifp->if_flags &= ~IFF_RUNNING;
160		break;
161	case SIOCSIFMTU:
162		if (ifr->ifr_mtu < PFSYNC_MINMTU)
163			return (EINVAL);
164		if (ifr->ifr_mtu > MCLBYTES)
165			ifr->ifr_mtu = MCLBYTES;
166		s = splnet();
167		if (ifr->ifr_mtu < ifp->if_mtu)
168			pfsync_sendout(sc);
169		pfsync_setmtu(sc, ifr->ifr_mtu);
170		splx(s);
171		break;
172	default:
173		return (ENOTTY);
174	}
175
176	return (0);
177}
178
179void
180pfsync_setmtu(sc, mtu)
181	struct pfsync_softc *sc;
182	int mtu;
183{
184	sc->sc_count = (mtu - sizeof(struct pfsync_header)) /
185	    sizeof(struct pf_state);
186	sc->sc_if.if_mtu = sizeof(struct pfsync_header) +
187	    sc->sc_count * sizeof(struct pf_state);
188}
189
190struct mbuf *
191pfsync_get_mbuf(sc, action)
192	struct pfsync_softc *sc;
193	u_int8_t action;
194{
195	extern int hz;
196	struct pfsync_header *h;
197	struct mbuf *m;
198	int len;
199
200	MGETHDR(m, M_DONTWAIT, MT_DATA);
201	if (m == NULL) {
202		sc->sc_if.if_oerrors++;
203		return (NULL);
204	}
205
206	len = sc->sc_if.if_mtu;
207	if (len > MHLEN) {
208		MCLGET(m, M_DONTWAIT);
209		if ((m->m_flags & M_EXT) == 0) {
210			m_free(m);
211			sc->sc_if.if_oerrors++;
212			return (NULL);
213		}
214	}
215	m->m_pkthdr.rcvif = NULL;
216	m->m_pkthdr.len = m->m_len = len;
217
218	h = mtod(m, struct pfsync_header *);
219	h->version = PFSYNC_VERSION;
220	h->af = 0;
221	h->count = 0;
222	h->action = action;
223
224	sc->sc_mbuf = m;
225	sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN);
226	timeout_add(&sc->sc_tmo, hz);
227
228	return (m);
229}
230
231int
232pfsync_pack_state(action, st)
233	u_int8_t action;
234	struct pf_state *st;
235{
236	extern struct timeval time;
237	struct ifnet *ifp = &pfsyncif.sc_if;
238	struct pfsync_softc *sc = ifp->if_softc;
239	struct pfsync_header *h;
240	struct pf_state *sp;
241	struct pf_rule *r = st->rule.ptr;
242	struct mbuf *m;
243	u_long secs;
244	int s, ret;
245
246	if (action >= PFSYNC_ACT_MAX)
247		return (EINVAL);
248
249	s = splnet();
250	m = sc->sc_mbuf;
251	if (m == NULL) {
252		if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
253			splx(s);
254			return (ENOMEM);
255		}
256		h = mtod(m, struct pfsync_header *);
257	} else {
258		h = mtod(m, struct pfsync_header *);
259		if (h->action != action) {
260			pfsync_sendout(sc);
261			if ((m = pfsync_get_mbuf(sc, action)) == NULL) {
262				splx(s);
263				return (ENOMEM);
264			}
265			h = mtod(m, struct pfsync_header *);
266		}
267	}
268
269	sp = sc->sc_ptr++;
270	h->count++;
271	bzero(sp, sizeof(*sp));
272
273	bcopy(&st->lan, &sp->lan, sizeof(sp->lan));
274	bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy));
275	bcopy(&st->ext, &sp->ext, sizeof(sp->ext));
276
277	pf_state_peer_hton(&st->src, &sp->src);
278	pf_state_peer_hton(&st->dst, &sp->dst);
279
280	bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
281	secs = time.tv_sec;
282	sp->creation = htonl(secs - st->creation);
283	if (st->expire <= secs)
284		sp->expire = htonl(0);
285	else
286		sp->expire = htonl(st->expire - secs);
287	sp->packets[0] = htonl(st->packets[0]);
288	sp->packets[1] = htonl(st->packets[1]);
289	sp->bytes[0] = htonl(st->bytes[0]);
290	sp->bytes[1] = htonl(st->bytes[1]);
291	if (r == NULL)
292		sp->rule.nr = htonl(-1);
293	else
294		sp->rule.nr = htonl(r->nr);
295	sp->af = st->af;
296	sp->proto = st->proto;
297	sp->direction = st->direction;
298	sp->log = st->log;
299	sp->allow_opts = st->allow_opts;
300
301	ret = 0;
302	if (h->count == sc->sc_count)
303		ret = pfsync_sendout(sc);
304
305	splx(s);
306	return (0);
307}
308
309int
310pfsync_clear_state(st)
311	struct pf_state *st;
312{
313	struct ifnet *ifp = &pfsyncif.sc_if;
314	struct pfsync_softc *sc = ifp->if_softc;
315	struct mbuf *m = sc->sc_mbuf;
316	int s, ret;
317
318	s = splnet();
319	if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) {
320		splx(s);
321		return (ENOMEM);
322	}
323
324	ret = (pfsync_sendout(sc));
325	splx(s);
326	return (ret);
327}
328
329void
330pfsync_timeout(void *v)
331{
332	struct pfsync_softc *sc = v;
333	int s;
334
335	s = splnet();
336	pfsync_sendout(sc);
337	splx(s);
338}
339
340int
341pfsync_sendout(sc)
342	struct pfsync_softc *sc;
343{
344	struct ifnet *ifp = &sc->sc_if;
345	struct mbuf *m = sc->sc_mbuf;
346
347	timeout_del(&sc->sc_tmo);
348	sc->sc_mbuf = NULL;
349	sc->sc_ptr = NULL;
350
351#if NBPFILTER > 0
352	if (ifp->if_bpf)
353		bpf_mtap(ifp->if_bpf, m);
354#endif
355
356	m_freem(m);
357
358	return (0);
359}
360