xref: /openbsd-src/sys/net/if_pflog.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: if_pflog.c,v 1.38 2011/07/07 00:47:18 mcbride 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 <netinet6/nd6.h>
67 #include <netinet/icmp6.h>
68 #endif /* INET6 */
69 
70 #include <net/pfvar.h>
71 #include <net/if_pflog.h>
72 
73 #define PFLOGMTU	(32768 + MHLEN + MLEN)
74 
75 #ifdef PFLOGDEBUG
76 #define DPRINTF(x)    do { if (pflogdebug) printf x ; } while (0)
77 #else
78 #define DPRINTF(x)
79 #endif
80 
81 void	pflogattach(int);
82 int	pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
83 	    	       struct rtentry *);
84 int	pflogioctl(struct ifnet *, u_long, caddr_t);
85 void	pflogstart(struct ifnet *);
86 int	pflog_clone_create(struct if_clone *, int);
87 int	pflog_clone_destroy(struct ifnet *);
88 
89 LIST_HEAD(, pflog_softc)	pflogif_list;
90 struct if_clone	pflog_cloner =
91     IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
92 
93 struct ifnet	*pflogifs[PFLOGIFS_MAX];	/* for fast access */
94 struct mbuf	*mfake = NULL;
95 
96 void
97 pflogattach(int npflog)
98 {
99 	int	i;
100 	LIST_INIT(&pflogif_list);
101 	for (i = 0; i < PFLOGIFS_MAX; i++)
102 		pflogifs[i] = NULL;
103 	if (mfake == NULL)
104 		mfake = m_get(M_DONTWAIT, MT_HEADER);
105 	if_clone_attach(&pflog_cloner);
106 }
107 
108 int
109 pflog_clone_create(struct if_clone *ifc, int unit)
110 {
111 	struct ifnet *ifp;
112 	struct pflog_softc *pflogif;
113 	int s;
114 
115 	if (unit >= PFLOGIFS_MAX)
116 		return (EINVAL);
117 
118 	if ((pflogif = malloc(sizeof(*pflogif),
119 	    M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
120 		return (ENOMEM);
121 
122 	pflogif->sc_unit = unit;
123 	ifp = &pflogif->sc_if;
124 	snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
125 	ifp->if_softc = pflogif;
126 	ifp->if_mtu = PFLOGMTU;
127 	ifp->if_ioctl = pflogioctl;
128 	ifp->if_output = pflogoutput;
129 	ifp->if_start = pflogstart;
130 	ifp->if_type = IFT_PFLOG;
131 	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
132 	ifp->if_hdrlen = PFLOG_HDRLEN;
133 	if_attach(ifp);
134 	if_alloc_sadl(ifp);
135 
136 #if NBPFILTER > 0
137 	bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
138 #endif
139 
140 	s = splnet();
141 	LIST_INSERT_HEAD(&pflogif_list, pflogif, sc_list);
142 	pflogifs[unit] = ifp;
143 	splx(s);
144 
145 	return (0);
146 }
147 
148 int
149 pflog_clone_destroy(struct ifnet *ifp)
150 {
151 	struct pflog_softc	*pflogif = ifp->if_softc;
152 	int			 s;
153 
154 	s = splnet();
155 	pflogifs[pflogif->sc_unit] = NULL;
156 	LIST_REMOVE(pflogif, sc_list);
157 	splx(s);
158 
159 	if_detach(ifp);
160 	free(pflogif, M_DEVBUF);
161 	return (0);
162 }
163 
164 /*
165  * Start output on the pflog interface.
166  */
167 void
168 pflogstart(struct ifnet *ifp)
169 {
170 	struct mbuf *m;
171 	int s;
172 
173 	for (;;) {
174 		s = splnet();
175 		IF_DROP(&ifp->if_snd);
176 		IF_DEQUEUE(&ifp->if_snd, m);
177 		splx(s);
178 
179 		if (m == NULL)
180 			return;
181 		else
182 			m_freem(m);
183 	}
184 }
185 
186 int
187 pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
188 	struct rtentry *rt)
189 {
190 	m_freem(m);
191 	return (0);
192 }
193 
194 /* ARGSUSED */
195 int
196 pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
197 {
198 	switch (cmd) {
199 	case SIOCSIFFLAGS:
200 		if (ifp->if_flags & IFF_UP)
201 			ifp->if_flags |= IFF_RUNNING;
202 		else
203 			ifp->if_flags &= ~IFF_RUNNING;
204 		break;
205 	default:
206 		return (ENOTTY);
207 	}
208 
209 	return (0);
210 }
211 
212 int
213 pflog_packet(struct pfi_kif *kif, struct mbuf *m, u_int8_t dir,
214     u_int8_t reason, struct pf_rule *rm, struct pf_rule *am,
215     struct pf_ruleset *ruleset, struct pf_pdesc *pd)
216 {
217 #if NBPFILTER > 0
218 	struct ifnet *ifn;
219 	struct pfloghdr hdr;
220 
221 	if (kif == NULL || m == NULL || rm == NULL || pd == NULL)
222 		return (-1);
223 
224 	if ((ifn = pflogifs[rm->logif]) == NULL || !ifn->if_bpf)
225 		return (0);
226 
227 	bzero(&hdr, sizeof(hdr));
228 	hdr.length = PFLOG_REAL_HDRLEN;
229 	hdr.af = pd->af;
230 	hdr.action = rm->action;
231 	hdr.reason = reason;
232 	memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname));
233 
234 	if (am == NULL) {
235 		hdr.rulenr = htonl(rm->nr);
236 		hdr.subrulenr = -1;
237 	} else {
238 		hdr.rulenr = htonl(am->nr);
239 		hdr.subrulenr = htonl(rm->nr);
240 		if (ruleset != NULL && ruleset->anchor != NULL)
241 			strlcpy(hdr.ruleset, ruleset->anchor->name,
242 			    sizeof(hdr.ruleset));
243 	}
244 	if (rm->log & PF_LOG_SOCKET_LOOKUP && !pd->lookup.done)
245 		pd->lookup.done = pf_socket_lookup(dir, pd);
246 	if (pd->lookup.done > 0) {
247 		hdr.uid = pd->lookup.uid;
248 		hdr.pid = pd->lookup.pid;
249 	} else {
250 		hdr.uid = UID_MAX;
251 		hdr.pid = NO_PID;
252 	}
253 	hdr.rule_uid = rm->cuid;
254 	hdr.rule_pid = rm->cpid;
255 	hdr.dir = dir;
256 
257 	PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->af);
258 	PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->af);
259 	hdr.sport = pd->nsport;
260 	hdr.dport = pd->ndport;
261 
262 	ifn->if_opackets++;
263 	ifn->if_obytes += m->m_pkthdr.len;
264 
265 	bpf_mtap_pflog(ifn->if_bpf, (caddr_t)&hdr, m);
266 #endif
267 
268 	return (0);
269 }
270 
271 void
272 pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
273 {
274 	const struct mbuf	*m;
275 	struct pfloghdr		*pfloghdr;
276 	struct pf_state		*s = NULL;
277 	u_int			 count;
278 	u_char			*dst;
279 	u_short			 action, reason;
280 	int			 off = 0, hdrlen = 0;
281 	union {
282 		struct tcphdr		tcp;
283 		struct udphdr		udp;
284 		struct icmp		icmp;
285 #ifdef INET6
286 		struct icmp6_hdr	icmp6;
287 		struct mld_hdr		mld;
288 		struct nd_neighbor_solicit nd_ns;
289 #endif /* INET6 */
290 	} pf_hdrs;
291 
292 	struct pf_pdesc		 pd;
293 	struct pf_addr		 osaddr, odaddr;
294 	u_int16_t		 osport = 0, odport = 0;
295 
296 	m = src_arg;
297 	dst = dst_arg;
298 
299 	if (m == NULL)
300 		panic("pflog_bpfcopy got no mbuf");
301 
302 	/* first mbuf holds struct pfloghdr */
303 	pfloghdr = mtod(m, struct pfloghdr *);
304 	count = min(m->m_len, len);
305 	bcopy(pfloghdr, dst, count);
306 	pfloghdr = (struct pfloghdr *)dst;
307 	dst += count;
308 	len -= count;
309 	m = m->m_next;
310 
311 	/* second mbuf is pkthdr */
312 	if (len > 0) {
313 		if (m == NULL)
314 			panic("no second mbuf");
315 		bcopy(m, mfake, sizeof(*mfake));
316 		mfake->m_flags &= ~(M_EXT|M_CLUSTER);
317 		mfake->m_next = NULL;
318 		mfake->m_nextpkt = NULL;
319 		mfake->m_data = dst;
320 		mfake->m_len = len;
321 	} else
322 		return;
323 
324 	while (len > 0) {
325 		if (m == 0)
326 			panic("bpf_mcopy");
327 		count = min(m->m_len, len);
328 		bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
329 		m = m->m_next;
330 		dst += count;
331 		len -= count;
332 	}
333 
334 	if (mfake->m_flags & M_PKTHDR)
335 		mfake->m_pkthdr.len = min(mfake->m_pkthdr.len, mfake->m_len);
336 
337 	/* rewrite addresses if needed */
338 	memset(&pd, 0, sizeof(pd));
339 	pd.hdr.any = &pf_hdrs;
340 	if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, &mfake, &action,
341 	    &reason, NULL, NULL, NULL, &s, NULL, &off, &hdrlen) == -1)
342 		return;
343 
344 	PF_ACPY(&osaddr, pd.src, pd.af);
345 	PF_ACPY(&odaddr, pd.dst, pd.af);
346 	if (pd.sport)
347 		osport = *pd.sport;
348 	if (pd.dport)
349 		odport = *pd.dport;
350 
351 	if ((pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
352 	    pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
353 	    pfloghdr->dir))) {
354 		m_copyback(mfake, off, min(mfake->m_len - off, hdrlen),
355 		    pd.hdr.any, M_NOWAIT);
356 		PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
357 		PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af);
358 		pfloghdr->sport = osport;
359 		pfloghdr->dport = odport;
360 	}
361 }
362