]> The Tcpdump Group git mirrors - tcpdump/blob - print-pflog.c
pflog: handle all types of pflog files, as best as can be done.
[tcpdump] / print-pflog.c
1 /*
2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22 /* \summary: *BSD/Darwin packet filter log file printer */
23
24 #include <config.h>
25
26 #include <limits.h>
27
28 #include "netdissect-stdinc.h"
29
30 #define ND_LONGJMP_FROM_TCHECK
31 #include "netdissect.h"
32 #include "extract.h"
33 #include "af.h"
34
35 /*
36 * pflog headers, at least as they exist now.
37 */
38 #define PFLOG_IFNAMSIZ 16
39 #define PFLOG_RULESET_NAME_SIZE 16
40
41 struct pf_addr {
42 union {
43 nd_ipv4 v4;
44 nd_ipv6 v6;
45 } pfa; /* 128-bit address */
46 #define v4 pfa.v4
47 #define v6 pfa.v6
48 };
49
50 /*
51 * This header is:
52 *
53 * 61 bytes long on NetBSD, DragonFly BSD. and Darwin;
54 * 84 bytes lon on OpenBSD;
55 * 72 bytes long on FreeBSD;
56 *
57 * which, unfortunately, does not allow us to distinguish, based on
58 * the header length, between the three OSes listed as having 61-byte
59 * headers. As the action values differ between them, this makes it
60 * impossible to correctly dissect the reason values that differ
61 * between NetBSD and Darwin (reason value 15) without having some
62 * way to explicitly tell tcpdump what to do.
63 *
64 * (We could, I guess, label reason value 15 as
65 * "state-locked (NetBSD)/dummynet (macOS etc.)" or something such as
66 * that.)
67 */
68 struct pfloghdr {
69 nd_uint8_t length;
70 nd_uint8_t af;
71 nd_uint8_t action;
72 nd_uint8_t reason;
73 char ifname[PFLOG_IFNAMSIZ];
74 char ruleset[PFLOG_RULESET_NAME_SIZE];
75 nd_uint32_t rulenr;
76 nd_uint32_t subrulenr;
77 nd_uint32_t uid;
78 nd_int32_t pid;
79 nd_uint32_t rule_uid;
80 nd_int32_t rule_pid;
81 nd_uint8_t dir;
82 union {
83 struct pflog_openbsd_only {
84 nd_uint8_t rewritten;
85 nd_uint8_t naf;
86 nd_uint8_t pad[1];
87 struct pf_addr saddr;
88 struct pf_addr daddr;
89 nd_uint16_t sport;
90 nd_uint16_t dport;
91 } openbsd;
92 struct pflog_freebsd_only {
93 nd_uint8_t pad[3];
94 nd_uint32_t ridentifier;
95 nd_uint8_t reserve;
96 } freebsd;
97 } u;
98 };
99
100 /*
101 * FreeBSD header length.
102 */
103 #define PFLOG_HEADER_LEN_FREEBSD 69
104
105 /*
106 * OpenBSD header length.
107 */
108 #define PFLOG_HEADER_LEN_OPENBSD 100
109
110 /*
111 * DragonFly BSD, NetBSD and Darwin header length.
112 * Older versions of FreeBSD and OpenBSD may have used this
113 * as well.
114 *
115 * Unfortunately, this means we can't distinguish between Darwin, NetBSD,
116 * and DragonFly BSD based on the header length.
117 */
118 #define PFLOG_HEADER_LEN_OTHER 61
119
120 /*
121 * These are the minimum and maximum pflog header lengths.
122 */
123 #define MIN_PFLOG_HDRLEN 61
124 #define MAX_PFLOG_HDRLEN 100
125
126 /*
127 * Reason values.
128 */
129 #define PFRES_MATCH 0
130 #define PFRES_BADOFF 1
131 #define PFRES_FRAG 2
132 #define PFRES_SHORT 3
133 #define PFRES_NORM 4
134 #define PFRES_MEMORY 5
135 #define PFRES_TS 6
136 #define PFRES_CONGEST 7
137 #define PFRES_IPOPTIONS 8
138 #define PFRES_PROTCKSUM 9
139 #define PFRES_BADSTATE 10
140 #define PFRES_STATEINS 11
141 #define PFRES_MAXSTATES 12
142 #define PFRES_SRCLIMIT 13
143 #define PFRES_SYNPROXY 14
144
145 /* FreeBSD */
146 #define PFRES_MAPFAILED 15
147
148 /* OpenBSD */
149 #define PFRES_TRANSLATE 15
150 #define PFRES_NOROUTE 16
151
152 /* NetBSD/Darwin */
153 #define PFRES_STATELOCKED_DUMMYNET 15 /* STATELOCKED on NetBSD, DUMMYNET on Darwin */
154 #define PFRES_INVPORT 16 /* INVPORT on Darwin */
155
156 static const struct tok pf_reasons_freebsd[] = {
157 { PFRES_MATCH, "0(match)" },
158 { PFRES_BADOFF, "1(bad-offset)" },
159 { PFRES_FRAG, "2(fragment)" },
160 { PFRES_SHORT, "3(short)" },
161 { PFRES_NORM, "4(normalize)" },
162 { PFRES_MEMORY, "5(memory)" },
163 { PFRES_TS, "6(bad-timestamp)" },
164 { PFRES_CONGEST, "7(congestion)" },
165 { PFRES_IPOPTIONS, "8(ip-option)" },
166 { PFRES_PROTCKSUM, "9(proto-cksum)" },
167 { PFRES_BADSTATE, "10(state-mismatch)" },
168 { PFRES_STATEINS, "11(state-insert)" },
169 { PFRES_MAXSTATES, "12(state-limit)" },
170 { PFRES_SRCLIMIT, "13(src-limit)" },
171 { PFRES_SYNPROXY, "14(synproxy)" },
172 { PFRES_MAPFAILED, "15(map-failed)" },
173 { 0, NULL }
174 };
175
176 static const struct tok pf_reasons_openbsd[] = {
177 { PFRES_MATCH, "0(match)" },
178 { PFRES_BADOFF, "1(bad-offset)" },
179 { PFRES_FRAG, "2(fragment)" },
180 { PFRES_SHORT, "3(short)" },
181 { PFRES_NORM, "4(normalize)" },
182 { PFRES_MEMORY, "5(memory)" },
183 { PFRES_TS, "6(bad-timestamp)" },
184 { PFRES_CONGEST, "7(congestion)" },
185 { PFRES_IPOPTIONS, "8(ip-option)" },
186 { PFRES_PROTCKSUM, "9(proto-cksum)" },
187 { PFRES_BADSTATE, "10(state-mismatch)" },
188 { PFRES_STATEINS, "11(state-insert)" },
189 { PFRES_MAXSTATES, "12(state-limit)" },
190 { PFRES_SRCLIMIT, "13(src-limit)" },
191 { PFRES_SYNPROXY, "14(synproxy)" },
192 { PFRES_TRANSLATE, "15(translate)" },
193 { PFRES_NOROUTE, "16(no-route)" },
194 { 0, NULL }
195 };
196
197 static const struct tok pf_reasons_other[] = {
198 { PFRES_MATCH, "0(match)" },
199 { PFRES_BADOFF, "1(bad-offset)" },
200 { PFRES_FRAG, "2(fragment)" },
201 { PFRES_SHORT, "3(short)" },
202 { PFRES_NORM, "4(normalize)" },
203 { PFRES_MEMORY, "5(memory)" },
204 { PFRES_TS, "6(bad-timestamp)" },
205 { PFRES_CONGEST, "7(congestion)" },
206 { PFRES_IPOPTIONS, "8(ip-option)" },
207 { PFRES_PROTCKSUM, "9(proto-cksum)" },
208 { PFRES_BADSTATE, "10(state-mismatch)" },
209 { PFRES_STATEINS, "11(state-insert)" },
210 { PFRES_MAXSTATES, "12(state-limit)" },
211 { PFRES_SRCLIMIT, "13(src-limit)" },
212 { PFRES_SYNPROXY, "14(synproxy)" },
213 { PFRES_STATELOCKED_DUMMYNET,
214 "15(state-locked (NetBSD)/dummynet(Darwin)" },
215 { PFRES_INVPORT, "16(invalid-port (Darwin))" },
216 { 0, NULL }
217 };
218
219 /*
220 * Action values.
221 */
222 #define PF_PASS 0
223 #define PF_DROP 1
224 #define PF_SCRUB 2
225 #define PF_NOSCRUB 3
226 #define PF_NAT 4
227 #define PF_NONAT 5
228 #define PF_BINAT 6
229 #define PF_NOBINAT 7
230 #define PF_RDR 8
231 #define PF_NORDR 9
232 #define PF_SYNPROXY_DROP 10
233
234 /* FreeBSD and OpenBSD */
235 #define PF_DEFER 11
236
237 /* FreeBSD */
238 #define PF_MATCH 12
239
240 /* OpenBSD */
241 #define PF_MATCH 12
242 #define PF_DIVERT 13
243 #define PF_RT 14
244 #define PF_AFRT 15
245
246 /* Darwin */
247 #define PF_DUMMYNET 11
248 #define PF_NODUMMYNET 12
249 #define PF_NAT64 13
250 #define PF_NONAT64 14
251
252 static const struct tok pf_actions_freebsd[] = {
253 { PF_PASS, "pass" },
254 { PF_DROP, "block" },
255 { PF_SCRUB, "scrub" },
256 { PF_NOSCRUB, "noscrub" },
257 { PF_NAT, "nat" },
258 { PF_NONAT, "nonat" },
259 { PF_BINAT, "binat" },
260 { PF_NOBINAT, "nobinat" },
261 { PF_RDR, "rdr" },
262 { PF_NORDR, "nordr" },
263 { PF_SYNPROXY_DROP, "synproxy-drop" },
264 { PF_DEFER, "defer" },
265 { PF_MATCH, "match" },
266 { 0, NULL }
267 };
268
269 static const struct tok pf_actions_openbsd[] = {
270 { PF_PASS, "pass" },
271 { PF_DROP, "block" },
272 { PF_SCRUB, "scrub" },
273 { PF_NOSCRUB, "noscrub" },
274 { PF_NAT, "nat" },
275 { PF_NONAT, "nonat" },
276 { PF_BINAT, "binat" },
277 { PF_NOBINAT, "nobinat" },
278 { PF_RDR, "rdr" },
279 { PF_NORDR, "nordr" },
280 { PF_SYNPROXY_DROP, "synproxy-drop" },
281 { PF_DEFER, "defer" },
282 { PF_MATCH, "match" },
283 { PF_DIVERT, "divert" },
284 { PF_RT, "rt" },
285 { PF_AFRT, "afrt" },
286 { 0, NULL }
287 };
288
289 static const struct tok pf_actions_darwin[] = {
290 { PF_PASS, "pass" },
291 { PF_DROP, "block" },
292 { PF_SCRUB, "scrub" },
293 { PF_NOSCRUB, "noscrub" },
294 { PF_NAT, "nat" },
295 { PF_NONAT, "nonat" },
296 { PF_BINAT, "binat" },
297 { PF_NOBINAT, "nobinat" },
298 { PF_RDR, "rdr" },
299 { PF_NORDR, "nordr" },
300 { PF_SYNPROXY_DROP, "synproxy-drop" },
301 { PF_DUMMYNET, "dummynet (Darwin)" },
302 { PF_NODUMMYNET, "nodummynet (Darwin)" },
303 { PF_NAT64, "nat64 (Darwin)" },
304 { PF_NONAT64, "nonat64 (Darwin)" },
305 { 0, NULL }
306 };
307
308 /*
309 * Direction values.
310 */
311 #define PF_INOUT 0
312 #define PF_IN 1
313 #define PF_OUT 2
314
315 /* OpenBSD */
316 #define PF_FWD 3
317
318 static const struct tok pf_directions_freebsd[] = {
319 { PF_INOUT, "in/out" },
320 { PF_IN, "in" },
321 { PF_OUT, "out" },
322 { 0, NULL }
323 };
324
325 static const struct tok pf_directions_openbsd[] = {
326 { PF_INOUT, "in/out" },
327 { PF_IN, "in" },
328 { PF_OUT, "out" },
329 { PF_FWD, "fwd" },
330 { 0, NULL }
331 };
332
333 static const struct tok pf_directions_other[] = {
334 { PF_INOUT, "in/out" },
335 { PF_IN, "in" },
336 { PF_OUT, "out" },
337 { 0, NULL }
338 };
339
340 static void
341 pflog_print(netdissect_options *ndo, const struct pfloghdr *hdr)
342 {
343 uint8_t length;
344 uint32_t rulenr, subrulenr;
345 uint32_t uid;
346 uint32_t ridentifier;
347
348 ndo->ndo_protocol = "pflog";
349 length = GET_U_1(hdr->length);
350
351 rulenr = GET_BE_U_4(hdr->rulenr);
352 subrulenr = GET_BE_U_4(hdr->subrulenr);
353 if (subrulenr == (uint32_t)-1)
354 ND_PRINT("rule %u/", rulenr);
355 else {
356 ND_PRINT("rule %u.", rulenr);
357 nd_printjnp(ndo, (const u_char*)hdr->ruleset, PFLOG_RULESET_NAME_SIZE);
358 ND_PRINT(".%u/", subrulenr);
359 }
360
361 if (length == PFLOG_HEADER_LEN_FREEBSD)
362 ND_PRINT("%s", tok2str(pf_reasons_freebsd, "unkn(%u)", GET_U_1(hdr->reason)));
363 else if (length == PFLOG_HEADER_LEN_OPENBSD)
364 ND_PRINT("%s", tok2str(pf_reasons_openbsd, "unkn(%u)", GET_U_1(hdr->reason)));
365 else
366 ND_PRINT("%s", tok2str(pf_reasons_other, "unkn(%u)", GET_U_1(hdr->reason)));
367
368 /*
369 * In Darwin (macOS, etc.) and NetBSD, uid is set to
370 * UID_MAX if there's no UID, and UID_MAX is 2^31-1.
371 * UID_MAX is 2^31-1.
372 *
373 * In OpenBSD, uid is set to -1 if there's no UID, which
374 * means we'll see it as UINT_MAX, as we treat it as
375 * unsigned. UID_MAX is 2^32-1.
376 *
377 * In FreeBSD and DragonFly BSD, uid is set to UID_MAX
378 * if there's no UID. UID_MAX is 2^32-1.
379 *
380 * So:
381 *
382 * For OpenBSD and FreeBSD, check only for 2^32-1 (0xFFFFFFFFU)
383 * if there's no UID.
384 *
385 * For other OSes, it's either NetBSD, DragonFly BSD, or Darwin,
386 * check for both 2^31-1 (0x7FFFFFFFU) (NetBSD and Darwin) and
387 * 2^32-1 (0xFFFFFFFFU) (DragonFly BSD). That runs the risk of
388 * the UID not being printed for a DragonFly BSD log if it's
389 * 0x7FFFFFFF, but that's *probably* not going to be the case.
390 */
391 uid = GET_BE_U_4(hdr->uid);
392 if (length == PFLOG_HEADER_LEN_FREEBSD ||
393 length == PFLOG_HEADER_LEN_OPENBSD) {
394 if (uid != 0xFFFFFFFFU)
395 ND_PRINT(" [uid %u]", uid);
396 } else {
397 if (uid != 0xFFFFFFFFU && uid != 0x7FFFFFFFU)
398 ND_PRINT(" [uid %u]", uid);
399 }
400
401 if (length == PFLOG_HEADER_LEN_FREEBSD) {
402 ridentifier = GET_BE_U_4(hdr->u.freebsd.ridentifier);
403 if (ridentifier != 0)
404 ND_PRINT(" [ridentifier %u]", ridentifier);
405 }
406
407 if (length == PFLOG_HEADER_LEN_FREEBSD) {
408 ND_PRINT(": %s %s on ",
409 tok2str(pf_actions_freebsd, "unkn(%u)", GET_U_1(hdr->action)),
410 tok2str(pf_directions_freebsd, "unkn(%u)", GET_U_1(hdr->dir)));
411 } else if (length == PFLOG_HEADER_LEN_OPENBSD) {
412 ND_PRINT(": %s %s on ",
413 tok2str(pf_actions_openbsd, "unkn(%u)", GET_U_1(hdr->action)),
414 tok2str(pf_directions_openbsd, "unkn(%u)", GET_U_1(hdr->dir)));
415 } else {
416 /*
417 * We use the Darwin set of actions, as it's a superset
418 * of the NetBSD/DragonFly BSD set of actions.
419 */
420 ND_PRINT(": %s %s on ",
421 tok2str(pf_actions_darwin, "unkn(%u)", GET_U_1(hdr->action)),
422 tok2str(pf_directions_other, "unkn(%u)", GET_U_1(hdr->dir)));
423 }
424 nd_printjnp(ndo, (const u_char*)hdr->ifname, PFLOG_IFNAMSIZ);
425 ND_PRINT(": ");
426 }
427
428 void
429 pflog_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
430 const u_char *p)
431 {
432 u_int length = h->len;
433 u_int hdrlen;
434 u_int caplen = h->caplen;
435 const struct pfloghdr *hdr;
436 uint8_t af;
437
438 ndo->ndo_protocol = "pflog";
439 /* check length */
440 ND_ICHECK_U(length, <, MIN_PFLOG_HDRLEN);
441
442 hdr = (const struct pfloghdr *)p;
443 hdrlen = GET_U_1(hdr->length);
444 ND_ICHECK_U(hdrlen, <, MIN_PFLOG_HDRLEN);
445 ND_ICHECK_U(hdrlen, >, MAX_PFLOG_HDRLEN);
446 hdrlen = roundup2(hdrlen, 4);
447
448 /* print what we know */
449 ND_TCHECK_LEN(hdr, hdrlen);
450 ndo->ndo_ll_hdr_len += hdrlen;
451 if (ndo->ndo_eflag)
452 pflog_print(ndo, hdr);
453
454 /* skip to the real packet */
455 af = GET_U_1(hdr->af);
456 length -= hdrlen;
457 caplen -= hdrlen;
458 p += hdrlen;
459 switch (af) {
460
461 /*
462 * If there's a system that doesn't use the AF_INET
463 * from 4.2BSD, feel free to add its value to af.h
464 * and use it here.
465 *
466 * Hopefully, there isn't.
467 */
468 case BSD_AF_INET:
469 ip_print(ndo, p, length);
470 break;
471
472 /*
473 * Try all AF_INET6 values for all systems with pflog,
474 * including Darwin.
475 */
476 case BSD_AF_INET6_BSD:
477 case BSD_AF_INET6_FREEBSD:
478 case BSD_AF_INET6_DARWIN:
479 ip6_print(ndo, p, length);
480 break;
481
482 default:
483 /* address family not handled, print raw packet */
484 if (!ndo->ndo_eflag)
485 pflog_print(ndo, hdr);
486 if (!ndo->ndo_suppress_default_print)
487 ND_DEFAULTPRINT(p, caplen);
488 }
489
490 return;
491
492 invalid:
493 nd_print_invalid(ndo);
494 }