xref: /openbsd-src/sys/net/if_pflog.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: if_pflog.c,v 1.50 2012/07/08 07:58:09 henning 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  * Copyright (c) 2002 - 2010 Henning Brauer
20  *
21  * Permission to use, copy, and modify this software with or without fee
22  * is hereby granted, provided that this entire notice is included in
23  * all copies of any software which is or includes a copy or
24  * modification of this software.
25  * You may use this code under the GNU public license if you so wish. Please
26  * contribute changes back to the authors under this freer than GPL license
27  * so that we may further the use of strong encryption without limitations to
28  * all.
29  *
30  * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
31  * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
32  * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
33  * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
34  * PURPOSE.
35  */
36 
37 #include "bpfilter.h"
38 #include "pflog.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/mbuf.h>
43 #include <sys/proc.h>
44 #include <sys/socket.h>
45 #include <sys/ioctl.h>
46 
47 #include <net/if.h>
48 #include <net/if_types.h>
49 #include <net/route.h>
50 #include <net/bpf.h>
51 
52 #ifdef	INET
53 #include <netinet/in.h>
54 #include <netinet/in_var.h>
55 #include <netinet/in_systm.h>
56 #include <netinet/ip.h>
57 #include <netinet/tcp.h>
58 #include <netinet/udp.h>
59 #include <netinet/ip_icmp.h>
60 #endif
61 
62 #ifdef INET6
63 #ifndef INET
64 #include <netinet/in.h>
65 #endif
66 #include <netinet/ip6.h>
67 #include <netinet6/nd6.h>
68 #include <netinet/icmp6.h>
69 #endif /* INET6 */
70 
71 #include <net/pfvar.h>
72 #include <net/if_pflog.h>
73 
74 #define PFLOGMTU	(32768 + MHLEN + MLEN)
75 
76 #ifdef PFLOGDEBUG
77 #define DPRINTF(x)    do { if (pflogdebug) printf x ; } while (0)
78 #else
79 #define DPRINTF(x)
80 #endif
81 
82 void	pflogattach(int);
83 int	pflogifs_resize(size_t);
84 int	pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
85 	    	       struct rtentry *);
86 int	pflogioctl(struct ifnet *, u_long, caddr_t);
87 void	pflogstart(struct ifnet *);
88 int	pflog_clone_create(struct if_clone *, int);
89 int	pflog_clone_destroy(struct ifnet *);
90 
91 LIST_HEAD(, pflog_softc)	pflogif_list;
92 struct if_clone	pflog_cloner =
93     IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
94 
95 int		  npflogifs = 0;
96 struct ifnet	**pflogifs = NULL;	/* for fast access */
97 struct mbuf	 *pflog_mhdr = NULL, *pflog_mptr = NULL;
98 
99 void
100 pflogattach(int npflog)
101 {
102 	LIST_INIT(&pflogif_list);
103 	if (pflog_mhdr == NULL)
104 		if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL)
105 			panic("pflogattach: no mbuf");
106 	if (pflog_mptr == NULL)
107 		if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL)
108 			panic("pflogattach: no mbuf");
109 	if_clone_attach(&pflog_cloner);
110 }
111 
112 int
113 pflogifs_resize(size_t n)
114 {
115 	struct ifnet	**p;
116 	int		  i;
117 
118 	if (n > SIZE_MAX / sizeof(*p))
119 		return (EINVAL);
120 	if (n == 0)
121 		p = NULL;
122 	else
123 		if ((p = malloc(n * sizeof(*p), M_DEVBUF,
124 		    M_NOWAIT|M_ZERO)) == NULL)
125 			return (ENOMEM);
126 	for (i = 0; i < n; i++)
127 		if (i < npflogifs)
128 			p[i] = pflogifs[i];
129 		else
130 			p[i] = NULL;
131 
132 	if (pflogifs)
133 		free(pflogifs, M_DEVBUF);
134 	pflogifs = p;
135 	npflogifs = n;
136 	return (0);
137 }
138 
139 int
140 pflog_clone_create(struct if_clone *ifc, int unit)
141 {
142 	struct ifnet *ifp;
143 	struct pflog_softc *pflogif;
144 	int s;
145 
146 	if ((pflogif = malloc(sizeof(*pflogif),
147 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
148 		return (ENOMEM);
149 
150 	pflogif->sc_unit = unit;
151 	ifp = &pflogif->sc_if;
152 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
153 	ifp->if_softc = pflogif;
154 	ifp->if_mtu = PFLOGMTU;
155 	ifp->if_ioctl = pflogioctl;
156 	ifp->if_output = pflogoutput;
157 	ifp->if_start = pflogstart;
158 	ifp->if_type = IFT_PFLOG;
159 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
160 	ifp->if_hdrlen = PFLOG_HDRLEN;
161 	if_attach(ifp);
162 	if_alloc_sadl(ifp);
163 
164 #if NBPFILTER > 0
165 	bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
166 #endif
167 
168 	s = splnet();
169 	LIST_INSERT_HEAD(&pflogif_list, pflogif, sc_list);
170 	if (unit + 1 > npflogifs && pflogifs_resize(unit + 1) != 0) {
171 		splx(s);
172 		return (ENOMEM);
173 	}
174 	pflogifs[unit] = ifp;
175 	splx(s);
176 
177 	return (0);
178 }
179 
180 int
181 pflog_clone_destroy(struct ifnet *ifp)
182 {
183 	struct pflog_softc	*pflogif = ifp->if_softc;
184 	int			 s, i;
185 
186 	s = splnet();
187 	pflogifs[pflogif->sc_unit] = NULL;
188 	LIST_REMOVE(pflogif, sc_list);
189 
190 	for (i = npflogifs; i > 0 && pflogifs[i - 1] == NULL; i--)
191 		; /* nothing */
192 	if (i < npflogifs)
193 		pflogifs_resize(i);	/* error harmless here */
194 	splx(s);
195 
196 	if_detach(ifp);
197 	free(pflogif, M_DEVBUF);
198 	return (0);
199 }
200 
201 /*
202  * Start output on the pflog interface.
203  */
204 void
205 pflogstart(struct ifnet *ifp)
206 {
207 	struct mbuf *m;
208 	int s;
209 
210 	for (;;) {
211 		s = splnet();
212 		IF_DROP(&ifp->if_snd);
213 		IF_DEQUEUE(&ifp->if_snd, m);
214 		splx(s);
215 
216 		if (m == NULL)
217 			return;
218 		else
219 			m_freem(m);
220 	}
221 }
222 
223 int
224 pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
225 	struct rtentry *rt)
226 {
227 	m_freem(m);
228 	return (0);
229 }
230 
231 /* ARGSUSED */
232 int
233 pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
234 {
235 	switch (cmd) {
236 	case SIOCSIFFLAGS:
237 		if (ifp->if_flags & IFF_UP)
238 			ifp->if_flags |= IFF_RUNNING;
239 		else
240 			ifp->if_flags &= ~IFF_RUNNING;
241 		break;
242 	default:
243 		return (ENOTTY);
244 	}
245 
246 	return (0);
247 }
248 
249 int
250 pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
251     struct pf_rule *am, struct pf_ruleset *ruleset)
252 {
253 #if NBPFILTER > 0
254 	struct ifnet *ifn;
255 	struct pfloghdr hdr;
256 
257 	if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL)
258 		return (-1);
259 
260 	if (rm->logif >= npflogifs || (ifn = pflogifs[rm->logif]) == NULL ||
261 	    !ifn->if_bpf)
262 		return (0);
263 
264 	bzero(&hdr, sizeof(hdr));
265 	hdr.length = PFLOG_REAL_HDRLEN;
266 	hdr.action = rm->action;
267 	hdr.reason = reason;
268 	memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
269 
270 	if (am == NULL) {
271 		hdr.rulenr = htonl(rm->nr);
272 		hdr.subrulenr = -1;
273 	} else {
274 		hdr.rulenr = htonl(am->nr);
275 		hdr.subrulenr = htonl(rm->nr);
276 		if (ruleset != NULL && ruleset->anchor != NULL)
277 			strlcpy(hdr.ruleset, ruleset->anchor->name,
278 			    sizeof(hdr.ruleset));
279 	}
280 	if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done)
281 		pd->lookup.done = pf_socket_lookup(pd);
282 	if (pd->lookup.done > 0) {
283 		hdr.uid = pd->lookup.uid;
284 		hdr.pid = pd->lookup.pid;
285 	} else {
286 		hdr.uid = UID_MAX;
287 		hdr.pid = NO_PID;
288 	}
289 	hdr.rule_uid = rm->cuid;
290 	hdr.rule_pid = rm->cpid;
291 	hdr.dir = pd->dir;
292 
293 	PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->naf);
294 	PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->naf);
295 	hdr.af = pd->af;
296 	hdr.naf = pd->naf;
297 	hdr.sport = pd->nsport;
298 	hdr.dport = pd->ndport;
299 
300 	ifn->if_opackets++;
301 	ifn->if_obytes += pd->m->m_pkthdr.len;
302 
303 	bpf_mtap_pflog(ifn->if_bpf, (caddr_t)&hdr, pd->m);
304 #endif
305 
306 	return (0);
307 }
308 
309 void
310 pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
311 {
312 	struct mbuf		*m, *mp, *mhdr, *mptr;
313 	struct pfloghdr		*pfloghdr;
314 	u_int			 count;
315 	u_char			*dst, *mdst, *cp;
316 	u_short			 reason;
317 	int			 afto, hlen, mlen, off;
318 	union pf_headers {
319 		struct tcphdr		tcp;
320 		struct udphdr		udp;
321 		struct icmp		icmp;
322 #ifdef INET6
323 		struct icmp6_hdr	icmp6;
324 		struct mld_hdr		mld;
325 		struct nd_neighbor_solicit nd_ns;
326 #endif /* INET6 */
327 	} pdhdrs;
328 
329 	struct pf_pdesc		 pd;
330 	struct pf_addr		 osaddr, odaddr;
331 	u_int16_t		 osport = 0, odport = 0;
332 	u_int8_t		 proto = 0;
333 
334 	m = (struct mbuf *)src_arg;
335 	dst = dst_arg;
336 
337 	mhdr = pflog_mhdr;
338 	mptr = pflog_mptr;
339 
340 	if (m == NULL)
341 		panic("pflog_bpfcopy got no mbuf");
342 
343 	/* first mbuf holds struct pfloghdr */
344 	pfloghdr = mtod(m, struct pfloghdr *);
345 	afto = pfloghdr->af != pfloghdr->naf;
346 	count = min(m->m_len, len);
347 	bcopy(pfloghdr, dst, count);
348 	pfloghdr = (struct pfloghdr *)dst;
349 	dst += count;
350 	len -= count;
351 	m = m->m_next;
352 
353 	if (len <= 0)
354 		return;
355 
356 	/* second mbuf is pkthdr */
357 	if (m == NULL)
358 		panic("no second mbuf");
359 
360 	/*
361 	 * temporary mbuf will hold an ip/ip6 header and 8 bytes
362 	 * of the protocol header
363 	 */
364 	m_inithdr(mhdr);
365 	mhdr->m_len = 0;	/* XXX not done in m_inithdr() */
366 
367 #if INET && INET6
368 	/* offset for a new header */
369 	if (afto && pfloghdr->af == AF_INET)
370 		mhdr->m_data += sizeof(struct ip6_hdr) -
371 		    sizeof(struct ip);
372 #endif /* INET && INET6 */
373 
374 	mdst = mtod(mhdr, char *);
375 	switch (pfloghdr->af) {
376 	case AF_INET: {
377 		struct ip	*h;
378 
379 		if (m->m_pkthdr.len < sizeof(*h))
380 			goto copy;
381 		m_copydata(m, 0, sizeof(*h), mdst);
382 		h = (struct ip *)mdst;
383 		hlen = h->ip_hl << 2;
384 		if (hlen > sizeof(*h) && (m->m_pkthdr.len >= hlen))
385 			m_copydata(m, sizeof(*h), hlen - sizeof(*h),
386 			    mdst + sizeof(*h));
387 		break;
388 	    }
389 #ifdef INET6
390 	case AF_INET6: {
391 		struct ip6_hdr	*h;
392 
393 		if (m->m_pkthdr.len < sizeof(*h))
394 			goto copy;
395 		hlen = sizeof(struct ip6_hdr);
396 		m_copydata(m, 0, hlen, mdst);
397 		h = (struct ip6_hdr *)mdst;
398 		proto = h->ip6_nxt;
399 		break;
400 	    }
401 #endif /* INET6 */
402 	default:
403 		/* shouldn't happen ever :-) */
404 		goto copy;
405 	}
406 
407 	if (m->m_pkthdr.len < hlen + 8 && proto != IPPROTO_NONE)
408 		goto copy;
409 	else if (proto != IPPROTO_NONE) {
410 		/* copy 8 bytes of the protocol header */
411 		m_copydata(m, hlen, 8, mdst + hlen);
412 		hlen += 8;
413 	}
414 
415 	mhdr->m_len += hlen;
416 	mhdr->m_pkthdr.len = mhdr->m_len;
417 
418 	/* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen) */
419 	mp = m_getptr(m, hlen, &off);
420 	if (mp != NULL) {
421 		bcopy(mp, mptr, sizeof(*mptr));
422 		cp = mtod(mp, char *);
423 		mptr->m_data += off;
424 		mptr->m_len -= off;
425 		mptr->m_flags &= ~M_PKTHDR;
426 		mhdr->m_next = mptr;
427 		mhdr->m_pkthdr.len += m->m_pkthdr.len - hlen;
428 	}
429 
430 	/* rewrite addresses if needed */
431 	if (pf_setup_pdesc(&pd, &pdhdrs, pfloghdr->af, pfloghdr->dir, NULL,
432 	    mhdr, &reason) != PF_PASS)
433 		goto copy;
434 	pd.naf = pfloghdr->naf;
435 
436 	PF_ACPY(&osaddr, pd.src, pd.af);
437 	PF_ACPY(&odaddr, pd.dst, pd.af);
438 	if (pd.sport)
439 		osport = *pd.sport;
440 	if (pd.dport)
441 		odport = *pd.dport;
442 
443 	if (pd.virtual_proto != PF_VPROTO_FRAGMENT &&
444 	    (pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
445 	    pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
446 	    pfloghdr->dir))) {
447 		m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen),
448 		    pd.hdr.any, M_NOWAIT);
449 #if INET && INET6
450 		if (afto) {
451 			PF_ACPY(&pd.nsaddr, &pfloghdr->saddr, pd.naf);
452 			PF_ACPY(&pd.ndaddr, &pfloghdr->daddr, pd.naf);
453 		}
454 #endif /* INET && INET6 */
455 		PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
456 		PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af);
457 		pfloghdr->sport = osport;
458 		pfloghdr->dport = odport;
459 	}
460 
461 	pd.tot_len = min(pd.tot_len, len);
462 	pd.tot_len -= pd.m->m_data - pd.m->m_pktdat;
463 
464 #if INET && INET6
465 	if (afto && pfloghdr->rewritten)
466 		pf_translate_af(&pd);
467 #endif /* INET && INET6 */
468 
469 	m = pd.m;
470  copy:
471 	mlen = min(m->m_pkthdr.len, len);
472 	m_copydata(m, 0, mlen, dst);
473 	len -= mlen;
474 	if (len > 0)
475 		bzero(dst + mlen, len);
476 }
477