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