1/*	$OpenBSD: if_pflog.c,v 1.26 2007/10/18 21:58:18 mpf Exp $	*/
2/*
3 * The authors of this code are John Ioannidis (ji@tla.org),
4 * Angelos D. Keromytis (kermit@csd.uch.gr) and
5 * Niels Provos (provos@physnet.uni-hamburg.de).
6 *
7 * This code was written by John Ioannidis for BSD/OS in Athens, Greece,
8 * in November 1995.
9 *
10 * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
11 * by Angelos D. Keromytis.
12 *
13 * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
14 * and Niels Provos.
15 *
16 * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
17 * and Niels Provos.
18 * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos.
19 *
20 * Permission to use, copy, and modify this software with or without fee
21 * is hereby granted, provided that this entire notice is included in
22 * all copies of any software which is or includes a copy or
23 * modification of this software.
24 * You may use this code under the GNU public license if you so wish. Please
25 * contribute changes back to the authors under this freer than GPL license
26 * so that we may further the use of strong encryption without limitations to
27 * all.
28 *
29 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
30 * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
31 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
32 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
33 * PURPOSE.
34 */
35
36#ifdef __FreeBSD__
37#include "opt_inet.h"
38#include "opt_inet6.h"
39#include "opt_bpf.h"
40#include "opt_pf.h"
41
42#include <sys/cdefs.h>
43__FBSDID("$FreeBSD$");
44
45#ifdef DEV_BPF
46#define	NBPFILTER	DEV_BPF
47#else
48#define	NBPFILTER	0
49#endif
50
51#ifdef DEV_PFLOG
52#define	NPFLOG		DEV_PFLOG
53#else
54#define	NPFLOG		0
55#endif
56
57#else /* ! __FreeBSD__ */
58#include "bpfilter.h"
59#include "pflog.h"
60#endif /* __FreeBSD__ */
61
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/mbuf.h>
65#include <sys/proc.h>
66#include <sys/socket.h>
67#ifdef __FreeBSD__
68#include <sys/kernel.h>
69#include <sys/limits.h>
70#include <sys/malloc.h>
71#include <sys/module.h>
72#include <sys/sockio.h>
73#else
74#include <sys/ioctl.h>
75#endif
76
77#include <net/if.h>
78#ifdef __FreeBSD__
79#include <net/if_clone.h>
80#endif
81#include <net/if_types.h>
82#include <net/route.h>
83#include <net/bpf.h>
84
85#if defined(INET) || defined(INET6)
86#include <netinet/in.h>
87#endif
88#ifdef	INET
89#include <netinet/in_var.h>
90#include <netinet/in_systm.h>
91#include <netinet/ip.h>
92#endif
93
94#ifdef INET6
95#include <netinet6/in6_var.h>
96#include <netinet6/nd6.h>
97#endif /* INET6 */
98
99#include <net/pfvar.h>
100#include <net/if_pflog.h>
101
102#ifdef __FreeBSD__
103#ifdef INET
104#include <machine/in_cksum.h>
105#endif /* INET */
106#endif /* __FreeBSD__ */
107
108#define PFLOGMTU	(32768 + MHLEN + MLEN)
109
110#ifdef PFLOGDEBUG
111#define DPRINTF(x)    do { if (pflogdebug) printf x ; } while (0)
112#else
113#define DPRINTF(x)
114#endif
115
116void	pflogattach(int);
117int	pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
118#ifdef __FreeBSD__
119	    struct route *);
120#else
121	    struct rtentry *);
122#endif
123int	pflogioctl(struct ifnet *, u_long, caddr_t);
124void	pflogstart(struct ifnet *);
125#ifdef __FreeBSD__
126static int pflog_clone_create(struct if_clone *, int, caddr_t);
127static void pflog_clone_destroy(struct ifnet *);
128#else
129int	pflog_clone_create(struct if_clone *, int);
130int	pflog_clone_destroy(struct ifnet *);
131#endif
132
133LIST_HEAD(, pflog_softc)	pflogif_list;
134#ifdef __FreeBSD__
135IFC_SIMPLE_DECLARE(pflog, 1);
136#else
137struct if_clone	pflog_cloner =
138    IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
139#endif
140
141struct ifnet	*pflogifs[PFLOGIFS_MAX];	/* for fast access */
142
143void
144pflogattach(int npflog)
145{
146	int	i;
147	LIST_INIT(&pflogif_list);
148	for (i = 0; i < PFLOGIFS_MAX; i++)
149		pflogifs[i] = NULL;
150	if_clone_attach(&pflog_cloner);
151}
152
153#ifdef __FreeBSD__
154static int
155pflog_clone_create(struct if_clone *ifc, int unit, caddr_t param)
156#else
157int
158pflog_clone_create(struct if_clone *ifc, int unit)
159#endif
160{
161	struct ifnet *ifp;
162	struct pflog_softc *pflogif;
163	int s;
164
165	if (unit >= PFLOGIFS_MAX)
166		return (EINVAL);
167
168	if ((pflogif = malloc(sizeof(*pflogif),
169	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
170		return (ENOMEM);
171
172	pflogif->sc_unit = unit;
173#ifdef __FreeBSD__
174	ifp = pflogif->sc_ifp = if_alloc(IFT_PFLOG);
175	if (ifp == NULL) {
176		free(pflogif, M_DEVBUF);
177		return (ENOSPC);
178	}
179	if_initname(ifp, ifc->ifc_name, unit);
180#else
181	ifp = &pflogif->sc_if;
182	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
183#endif
184	ifp->if_softc = pflogif;
185	ifp->if_mtu = PFLOGMTU;
186	ifp->if_ioctl = pflogioctl;
187	ifp->if_output = pflogoutput;
188	ifp->if_start = pflogstart;
189#ifndef __FreeBSD__
190	ifp->if_type = IFT_PFLOG;
191#endif
192	ifp->if_snd.ifq_maxlen = ifqmaxlen;
193	ifp->if_hdrlen = PFLOG_HDRLEN;
194	if_attach(ifp);
195#ifndef __FreeBSD__
196	if_alloc_sadl(ifp);
197#endif
198
199#if NBPFILTER > 0
200#ifdef __FreeBSD__
201	bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN);
202#else
203	bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
204#endif
205#endif
206
207	s = splnet();
208#ifdef __FreeBSD__
209	/* XXX: Why pf(4) lock?! Better add a pflog lock?! */
210	PF_LOCK();
211#endif
212	LIST_INSERT_HEAD(&pflogif_list, pflogif, sc_list);
213	pflogifs[unit] = ifp;
214#ifdef __FreeBSD__
215	PF_UNLOCK();
216#endif
217	splx(s);
218
219	return (0);
220}
221
222#ifdef __FreeBSD__
223static void
224pflog_clone_destroy(struct ifnet *ifp)
225#else
226int
227pflog_clone_destroy(struct ifnet *ifp)
228#endif
229{
230	struct pflog_softc	*pflogif = ifp->if_softc;
231	int			 s;
232
233	s = splnet();
234#ifdef __FreeBSD__
235	PF_LOCK();
236#endif
237	pflogifs[pflogif->sc_unit] = NULL;
238	LIST_REMOVE(pflogif, sc_list);
239#ifdef __FreeBSD__
240	PF_UNLOCK();
241#endif
242	splx(s);
243
244#if NBPFILTER > 0
245	bpfdetach(ifp);
246#endif
247	if_detach(ifp);
248#ifdef __FreeBSD__
249	if_free(ifp);
250#endif
251	free(pflogif, M_DEVBUF);
252#ifndef __FreeBSD__
253	return (0);
254#endif
255}
256
257/*
258 * Start output on the pflog interface.
259 */
260void
261pflogstart(struct ifnet *ifp)
262{
263	struct mbuf *m;
264#ifndef __FreeBSD__
265	int s;
266#endif
267
268	for (;;) {
269#ifdef __FreeBSD__
270		IF_LOCK(&ifp->if_snd);
271		_IF_DROP(&ifp->if_snd);
272		_IF_DEQUEUE(&ifp->if_snd, m);
273		IF_UNLOCK(&ifp->if_snd);
274#else
275		s = splnet();
276		IF_DROP(&ifp->if_snd);
277		IF_DEQUEUE(&ifp->if_snd, m);
278		splx(s);
279#endif
280
281		if (m == NULL)
282			return;
283		else
284			m_freem(m);
285	}
286}
287
288int
289pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
290#ifdef __FreeBSD__
291	struct route *rt)
292#else
293	struct rtentry *rt)
294#endif
295{
296	m_freem(m);
297	return (0);
298}
299
300/* ARGSUSED */
301int
302pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
303{
304	switch (cmd) {
305	case SIOCSIFFLAGS:
306#ifdef __FreeBSD__
307		if (ifp->if_flags & IFF_UP)
308			ifp->if_drv_flags |= IFF_DRV_RUNNING;
309		else
310			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
311#else
312		if (ifp->if_flags & IFF_UP)
313			ifp->if_flags |= IFF_RUNNING;
314		else
315			ifp->if_flags &= ~IFF_RUNNING;
316#endif
317		break;
318	default:
319		return (ENOTTY);
320	}
321
322	return (0);
323}
324
325int
326pflog_packet(struct pfi_kif *kif, struct mbuf *m, sa_family_t af, u_int8_t dir,
327    u_int8_t reason, struct pf_rule *rm, struct pf_rule *am,
328    struct pf_ruleset *ruleset, struct pf_pdesc *pd)
329{
330#if NBPFILTER > 0
331	struct ifnet *ifn;
332	struct pfloghdr hdr;
333
334	if (kif == NULL || m == NULL || rm == NULL || pd == NULL)
335		return ( 1);
336
337	if ((ifn = pflogifs[rm->logif]) == NULL || !ifn->if_bpf)
338		return (0);
339
340	bzero(&hdr, sizeof(hdr));
341	hdr.length = PFLOG_REAL_HDRLEN;
342	hdr.af = af;
343	hdr.action = rm->action;
344	hdr.reason = reason;
345	memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname));
346
347	if (am == NULL) {
348		hdr.rulenr = htonl(rm->nr);
349		hdr.subrulenr =  1;
350	} else {
351		hdr.rulenr = htonl(am->nr);
352		hdr.subrulenr = htonl(rm->nr);
353		if (ruleset != NULL && ruleset->anchor != NULL)
354			strlcpy(hdr.ruleset, ruleset->anchor->name,
355			    sizeof(hdr.ruleset));
356	}
357	if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done)
358#ifdef __FreeBSD__
359		/*
360		 * XXX: This should not happen as we force an early lookup
361		 * via debug.pfugidhack
362		 */
363		; /* empty */
364#else
365		pd->lookup.done = pf_socket_lookup(dir, pd);
366#endif
367	if (pd->lookup.done > 0) {
368		hdr.uid = pd->lookup.uid;
369		hdr.pid = pd->lookup.pid;
370	} else {
371		hdr.uid = UID_MAX;
372		hdr.pid = NO_PID;
373	}
374	hdr.rule_uid = rm->cuid;
375	hdr.rule_pid = rm->cpid;
376	hdr.dir = dir;
377
378#ifdef INET
379	if (af == AF_INET && dir == PF_OUT) {
380		struct ip *ip;
381
382		ip = mtod(m, struct ip *);
383		ip->ip_sum = 0;
384		ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
385	}
386#endif /* INET */
387
388	ifn->if_opackets++;
389	ifn->if_obytes += m->m_pkthdr.len;
390#ifdef __FreeBSD__
391	BPF_MTAP2(ifn, &hdr, PFLOG_HDRLEN, m);
392#else
393	bpf_mtap_hdr(ifn->if_bpf, (char *)&hdr, PFLOG_HDRLEN, m,
394	    BPF_DIRECTION_OUT);
395#endif
396#endif
397
398	return (0);
399}
400
401#ifdef __FreeBSD__
402static int
403pflog_modevent(module_t mod, int type, void *data)
404{
405	int error = 0;
406
407	switch (type) {
408	case MOD_LOAD:
409		pflogattach(1);
410		PF_LOCK();
411		pflog_packet_ptr = pflog_packet;
412		PF_UNLOCK();
413		break;
414	case MOD_UNLOAD:
415		PF_LOCK();
416		pflog_packet_ptr = NULL;
417		PF_UNLOCK();
418		if_clone_detach(&pflog_cloner);
419		break;
420	default:
421		error = EINVAL;
422		break;
423	}
424
425	return error;
426}
427
428static moduledata_t pflog_mod = { "pflog", pflog_modevent, 0 };
429
430#define PFLOG_MODVER 1
431
432DECLARE_MODULE(pflog, pflog_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
433MODULE_VERSION(pflog, PFLOG_MODVER);
434MODULE_DEPEND(pflog, pf, PF_MODVER, PF_MODVER, PF_MODVER);
435#endif /* __FreeBSD__ */
436