xref: /openbsd-src/sys/net/if_pflog.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: if_pflog.c,v 1.81 2018/01/09 15:24:24 bluhm 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/stdint.h>
46 #include <sys/ioctl.h>
47 
48 #include <net/if.h>
49 #include <net/if_var.h>
50 #include <net/if_types.h>
51 #include <net/bpf.h>
52 
53 #include <netinet/in.h>
54 #include <netinet/ip.h>
55 #include <netinet/ip_icmp.h>
56 #include <netinet/tcp.h>
57 #include <netinet/udp.h>
58 
59 #ifdef INET6
60 #include <netinet/ip6.h>
61 #include <netinet/icmp6.h>
62 #endif /* INET6 */
63 
64 #include <net/pfvar.h>
65 #include <net/pfvar_priv.h>
66 #include <net/if_pflog.h>
67 
68 #define PFLOGMTU	(32768 + MHLEN + MLEN)
69 
70 #ifdef PFLOGDEBUG
71 #define DPRINTF(x)    do { if (pflogdebug) printf x ; } while (0)
72 #else
73 #define DPRINTF(x)
74 #endif
75 
76 void	pflogattach(int);
77 int	pflogifs_resize(size_t);
78 int	pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
79 		       struct rtentry *);
80 int	pflogioctl(struct ifnet *, u_long, caddr_t);
81 void	pflogstart(struct ifnet *);
82 int	pflog_clone_create(struct if_clone *, int);
83 int	pflog_clone_destroy(struct ifnet *);
84 void	pflog_bpfcopy(const void *, void *, size_t);
85 
86 struct if_clone	pflog_cloner =
87     IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
88 
89 int		  npflogifs = 0;
90 struct ifnet	**pflogifs = NULL;	/* for fast access */
91 struct mbuf	 *pflog_mhdr = NULL, *pflog_mptr = NULL;
92 
93 void
94 pflogattach(int npflog)
95 {
96 	if (pflog_mhdr == NULL)
97 		if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL)
98 			panic("pflogattach: no mbuf");
99 	if (pflog_mptr == NULL)
100 		if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL)
101 			panic("pflogattach: no mbuf");
102 	if_clone_attach(&pflog_cloner);
103 }
104 
105 int
106 pflogifs_resize(size_t n)
107 {
108 	struct ifnet	**p;
109 	int		  i;
110 
111 	NET_ASSERT_LOCKED();
112 
113 	if (n > SIZE_MAX / sizeof(*p))
114 		return (EINVAL);
115 	if (n == 0)
116 		p = NULL;
117 	else
118 		if ((p = mallocarray(n, sizeof(*p), M_DEVBUF,
119 		    M_NOWAIT|M_ZERO)) == NULL)
120 			return (ENOMEM);
121 	for (i = 0; i < n; i++)
122 		if (i < npflogifs)
123 			p[i] = pflogifs[i];
124 		else
125 			p[i] = NULL;
126 
127 	if (pflogifs)
128 		free(pflogifs, M_DEVBUF, 0);
129 	pflogifs = p;
130 	npflogifs = n;
131 	return (0);
132 }
133 
134 int
135 pflog_clone_create(struct if_clone *ifc, int unit)
136 {
137 	struct ifnet *ifp;
138 	struct pflog_softc *pflogif;
139 
140 	pflogif = malloc(sizeof(*pflogif), M_DEVBUF, M_WAITOK|M_ZERO);
141 	pflogif->sc_unit = unit;
142 	ifp = &pflogif->sc_if;
143 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
144 	ifp->if_softc = pflogif;
145 	ifp->if_mtu = PFLOGMTU;
146 	ifp->if_ioctl = pflogioctl;
147 	ifp->if_output = pflogoutput;
148 	ifp->if_start = pflogstart;
149 	ifp->if_xflags = IFXF_CLONED;
150 	ifp->if_type = IFT_PFLOG;
151 	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
152 	ifp->if_hdrlen = PFLOG_HDRLEN;
153 	if_attach(ifp);
154 	if_alloc_sadl(ifp);
155 
156 #if NBPFILTER > 0
157 	bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
158 #endif
159 
160 	NET_LOCK();
161 	if (unit + 1 > npflogifs && pflogifs_resize(unit + 1) != 0) {
162 		NET_UNLOCK();
163 		return (ENOMEM);
164 	}
165 	pflogifs[unit] = ifp;
166 	NET_UNLOCK();
167 
168 	return (0);
169 }
170 
171 int
172 pflog_clone_destroy(struct ifnet *ifp)
173 {
174 	struct pflog_softc	*pflogif = ifp->if_softc;
175 	int			 i;
176 
177 	NET_LOCK();
178 	pflogifs[pflogif->sc_unit] = NULL;
179 	for (i = npflogifs; i > 0 && pflogifs[i - 1] == NULL; i--)
180 		; /* nothing */
181 	if (i < npflogifs)
182 		pflogifs_resize(i);	/* error harmless here */
183 	NET_UNLOCK();
184 
185 	if_detach(ifp);
186 	free(pflogif, M_DEVBUF, 0);
187 	return (0);
188 }
189 
190 /*
191  * Start output on the pflog interface.
192  */
193 void
194 pflogstart(struct ifnet *ifp)
195 {
196 	IFQ_PURGE(&ifp->if_snd);
197 }
198 
199 int
200 pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
201 	struct rtentry *rt)
202 {
203 	m_freem(m);	/* drop packet */
204 	return (EAFNOSUPPORT);
205 }
206 
207 int
208 pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
209 {
210 	switch (cmd) {
211 	case SIOCSIFFLAGS:
212 		if (ifp->if_flags & IFF_UP)
213 			ifp->if_flags |= IFF_RUNNING;
214 		else
215 			ifp->if_flags &= ~IFF_RUNNING;
216 		break;
217 	default:
218 		return (ENOTTY);
219 	}
220 
221 	return (0);
222 }
223 
224 int
225 pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
226     struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *trigger)
227 {
228 #if NBPFILTER > 0
229 	struct ifnet *ifn;
230 	struct pfloghdr hdr;
231 
232 	if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL)
233 		return (-1);
234 	if (trigger == NULL)
235 		trigger = rm;
236 
237 	if (trigger->logif >= npflogifs || (ifn = pflogifs[trigger->logif]) ==
238 	    NULL || !ifn->if_bpf)
239 		return (0);
240 
241 	bzero(&hdr, sizeof(hdr));
242 	hdr.length = PFLOG_REAL_HDRLEN;
243 	hdr.action = rm->action;
244 	hdr.reason = reason;
245 	memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
246 
247 	if (am == NULL) {
248 		hdr.rulenr = htonl(rm->nr);
249 		hdr.subrulenr = -1;
250 	} else {
251 		hdr.rulenr = htonl(am->nr);
252 		hdr.subrulenr = htonl(rm->nr);
253 		if (ruleset != NULL && ruleset->anchor != NULL)
254 			strlcpy(hdr.ruleset, ruleset->anchor->name,
255 			    sizeof(hdr.ruleset));
256 	}
257 	if (trigger->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done)
258 		pd->lookup.done = pf_socket_lookup(pd);
259 	if (pd->lookup.done > 0) {
260 		hdr.uid = pd->lookup.uid;
261 		hdr.pid = pd->lookup.pid;
262 	} else {
263 		hdr.uid = UID_MAX;
264 		hdr.pid = NO_PID;
265 	}
266 	hdr.rule_uid = rm->cuid;
267 	hdr.rule_pid = rm->cpid;
268 	hdr.dir = pd->dir;
269 
270 	PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->naf);
271 	PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->naf);
272 	hdr.af = pd->af;
273 	hdr.naf = pd->naf;
274 	hdr.sport = pd->nsport;
275 	hdr.dport = pd->ndport;
276 
277 	ifn->if_opackets++;
278 	ifn->if_obytes += pd->m->m_pkthdr.len;
279 
280 	bpf_mtap_hdr(ifn->if_bpf, (caddr_t)&hdr, PFLOG_HDRLEN, pd->m,
281 	    BPF_DIRECTION_OUT, pflog_bpfcopy);
282 #endif
283 
284 	return (0);
285 }
286 
287 void
288 pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
289 {
290 	struct mbuf		*m, *mp, *mhdr, *mptr;
291 	struct pfloghdr		*pfloghdr;
292 	u_int			 count;
293 	u_char			*dst, *mdst;
294 	int			 afto, hlen, mlen, off;
295 
296 	struct pf_pdesc		 pd;
297 	struct pf_addr		 osaddr, odaddr;
298 	u_int16_t		 osport = 0, odport = 0;
299 	u_int8_t		 proto = 0;
300 
301 	m = (struct mbuf *)src_arg;
302 	dst = dst_arg;
303 
304 	mhdr = pflog_mhdr;
305 	mptr = pflog_mptr;
306 
307 	if (m == NULL)
308 		panic("pflog_bpfcopy got no mbuf");
309 
310 	/* first mbuf holds struct pfloghdr */
311 	pfloghdr = mtod(m, struct pfloghdr *);
312 	afto = pfloghdr->af != pfloghdr->naf;
313 	count = min(m->m_len, len);
314 	bcopy(pfloghdr, dst, count);
315 	pfloghdr = (struct pfloghdr *)dst;
316 	dst += count;
317 	len -= count;
318 	m = m->m_next;
319 
320 	if (len <= 0)
321 		return;
322 
323 	/* second mbuf is pkthdr */
324 	if (m == NULL)
325 		panic("no second mbuf");
326 
327 	/*
328 	 * temporary mbuf will hold an ip/ip6 header and 8 bytes
329 	 * of the protocol header
330 	 */
331 	m_inithdr(mhdr);
332 	mhdr->m_len = 0;	/* XXX not done in m_inithdr() */
333 
334 #ifdef INET6
335 	/* offset for a new header */
336 	if (afto && pfloghdr->af == AF_INET)
337 		mhdr->m_data += sizeof(struct ip6_hdr) -
338 		    sizeof(struct ip);
339 #endif /* INET6 */
340 
341 	mdst = mtod(mhdr, char *);
342 	switch (pfloghdr->af) {
343 	case AF_INET: {
344 		struct ip	*h;
345 
346 		if (m->m_pkthdr.len < sizeof(*h))
347 			goto copy;
348 		m_copydata(m, 0, sizeof(*h), mdst);
349 		h = (struct ip *)mdst;
350 		hlen = h->ip_hl << 2;
351 		if (hlen > sizeof(*h) && (m->m_pkthdr.len >= hlen))
352 			m_copydata(m, sizeof(*h), hlen - sizeof(*h),
353 			    mdst + sizeof(*h));
354 		break;
355 	    }
356 #ifdef INET6
357 	case AF_INET6: {
358 		struct ip6_hdr	*h;
359 
360 		if (m->m_pkthdr.len < sizeof(*h))
361 			goto copy;
362 		hlen = sizeof(struct ip6_hdr);
363 		m_copydata(m, 0, hlen, mdst);
364 		h = (struct ip6_hdr *)mdst;
365 		proto = h->ip6_nxt;
366 		break;
367 	    }
368 #endif /* INET6 */
369 	default:
370 		/* shouldn't happen ever :-) */
371 		goto copy;
372 	}
373 
374 	if (m->m_pkthdr.len < hlen + 8 && proto != IPPROTO_NONE)
375 		goto copy;
376 	else if (proto != IPPROTO_NONE) {
377 		/* copy 8 bytes of the protocol header */
378 		m_copydata(m, hlen, 8, mdst + hlen);
379 		hlen += 8;
380 	}
381 
382 	mhdr->m_len += hlen;
383 	mhdr->m_pkthdr.len = mhdr->m_len;
384 
385 	/* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen) */
386 	mp = m_getptr(m, hlen, &off);
387 	if (mp != NULL) {
388 		bcopy(mp, mptr, sizeof(*mptr));
389 		mptr->m_data += off;
390 		mptr->m_len -= off;
391 		mptr->m_flags &= ~M_PKTHDR;
392 		mhdr->m_next = mptr;
393 		mhdr->m_pkthdr.len += m->m_pkthdr.len - hlen;
394 	}
395 
396 	/*
397 	 * Rewrite addresses if needed. Reason pointer must be NULL to avoid
398 	 * counting the packet here again.
399 	 */
400 	if (pf_setup_pdesc(&pd, pfloghdr->af, pfloghdr->dir, NULL,
401 	    mhdr, NULL) != PF_PASS)
402 		goto copy;
403 	pd.naf = pfloghdr->naf;
404 
405 	PF_ACPY(&osaddr, pd.src, pd.af);
406 	PF_ACPY(&odaddr, pd.dst, pd.af);
407 	if (pd.sport)
408 		osport = *pd.sport;
409 	if (pd.dport)
410 		odport = *pd.dport;
411 
412 	if (pd.virtual_proto != PF_VPROTO_FRAGMENT &&
413 	    (pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
414 	    pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
415 	    pfloghdr->dir))) {
416 		m_copyback(pd.m, pd.off, min(pd.m->m_len - pd.off, pd.hdrlen),
417 		    &pd.hdr, M_NOWAIT);
418 #ifdef INET6
419 		if (afto) {
420 			PF_ACPY(&pd.nsaddr, &pfloghdr->saddr, pd.naf);
421 			PF_ACPY(&pd.ndaddr, &pfloghdr->daddr, pd.naf);
422 		}
423 #endif /* INET6 */
424 		PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
425 		PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af);
426 		pfloghdr->sport = osport;
427 		pfloghdr->dport = odport;
428 	}
429 
430 	pd.tot_len = min(pd.tot_len, len);
431 	pd.tot_len -= pd.m->m_data - pd.m->m_pktdat;
432 
433 #ifdef INET6
434 	if (afto && pfloghdr->rewritten)
435 		pf_translate_af(&pd);
436 #endif /* INET6 */
437 
438 	m = pd.m;
439  copy:
440 	mlen = min(m->m_pkthdr.len, len);
441 	m_copydata(m, 0, mlen, dst);
442 	len -= mlen;
443 	if (len > 0)
444 		bzero(dst + mlen, len);
445 }
446