/* \summary: *BSD/Darwin packet filter log file printer */
-#ifdef HAVE_CONFIG_H
#include <config.h>
-#endif
+
+#include <limits.h>
#include "netdissect-stdinc.h"
#include "netdissect.h"
#include "extract.h"
#include "af.h"
+#include "addrtostr.h"
/*
* pflog headers, at least as they exist now.
#define v6 pfa.v6
};
+/*
+ * This header is:
+ *
+ * 61 bytes long on NetBSD, DragonFly BSD. and Darwin;
+ * 84 bytes lon on OpenBSD;
+ * 72 bytes long on FreeBSD;
+ *
+ * which, unfortunately, does not allow us to distinguish, based on
+ * the header length, between the three OSes listed as having 61-byte
+ * headers. As the action values differ between them, this makes it
+ * impossible to correctly dissect the reason values that differ
+ * between NetBSD and Darwin (reason value 15) without having some
+ * way to explicitly tell tcpdump what to do.
+ *
+ * (We could, I guess, label reason value 15 as
+ * "state-locked (NetBSD)/dummynet (macOS etc.)" or something such as
+ * that.)
+ */
struct pfloghdr {
nd_uint8_t length;
nd_uint8_t af;
nd_uint32_t rule_uid;
nd_int32_t rule_pid;
nd_uint8_t dir;
-/* Minimum header length (without padding): 61 */
-#define MIN_PFLOG_HDRLEN 61
-#if defined(__OpenBSD__)
- nd_uint8_t rewritten;
- nd_uint8_t naf;
- nd_uint8_t pad[1];
-#else
- nd_uint8_t pad[3];
-#endif
-#if defined(__FreeBSD__)
- nd_uint32_t ridentifier;
- nd_uint8_t reserve;
- nd_uint8_t pad2[3];
-#elif defined(__OpenBSD__)
- struct pf_addr saddr;
- struct pf_addr daddr;
- nd_uint16_t sport;
- nd_uint16_t dport;
-#endif
+ union {
+ struct pflog_openbsd_only {
+ nd_uint8_t rewritten;
+ nd_uint8_t naf;
+ nd_uint8_t pad[1];
+ struct pf_addr saddr;
+ struct pf_addr daddr;
+ nd_uint16_t sport;
+ nd_uint16_t dport;
+ } openbsd;
+ struct pflog_freebsd_only {
+ nd_uint8_t pad[3];
+ nd_uint32_t ridentifier;
+ nd_uint8_t reserve;
+ } freebsd;
+ } u;
};
-#define MAX_PFLOG_HDRLEN 100 /* 61 + 3 + 16 + 16 + 2 + 2 */
+
+/*
+ * FreeBSD header length.
+ */
+#define PFLOG_HEADER_LEN_FREEBSD 69
+
+/*
+ * OpenBSD header length.
+ */
+#define PFLOG_HEADER_LEN_OPENBSD 100
+
+/*
+ * DragonFly BSD, NetBSD and Darwin header length.
+ * Older versions of FreeBSD and OpenBSD may have used this
+ * as well.
+ *
+ * Unfortunately, this means we can't distinguish between Darwin, NetBSD,
+ * and DragonFly BSD based on the header length.
+ */
+#define PFLOG_HEADER_LEN_OTHER 61
+
+/*
+ * These are the minimum and maximum pflog header lengths.
+ */
+#define MIN_PFLOG_HDRLEN 61
+#define MAX_PFLOG_HDRLEN 100
/*
* Reason values.
#define PFRES_MAXSTATES 12
#define PFRES_SRCLIMIT 13
#define PFRES_SYNPROXY 14
-#if defined(__FreeBSD__)
+
+/* FreeBSD */
#define PFRES_MAPFAILED 15
-#elif defined(__NetBSD__)
-#define PFRES_STATELOCKED 15
-#elif defined(__OpenBSD__)
+
+/* OpenBSD */
#define PFRES_TRANSLATE 15
#define PFRES_NOROUTE 16
-#elif defined(__APPLE__)
-#define PFRES_DUMMYNET 15
-#endif
-static const struct tok pf_reasons[] = {
+/* NetBSD/Darwin */
+#define PFRES_STATELOCKED_DUMMYNET 15 /* STATELOCKED on NetBSD, DUMMYNET on Darwin */
+#define PFRES_INVPORT 16 /* INVPORT on Darwin */
+
+static const struct tok pf_reasons_freebsd[] = {
{ PFRES_MATCH, "0(match)" },
{ PFRES_BADOFF, "1(bad-offset)" },
{ PFRES_FRAG, "2(fragment)" },
{ PFRES_MAXSTATES, "12(state-limit)" },
{ PFRES_SRCLIMIT, "13(src-limit)" },
{ PFRES_SYNPROXY, "14(synproxy)" },
-#if defined(__FreeBSD__)
{ PFRES_MAPFAILED, "15(map-failed)" },
-#elif defined(__NetBSD__)
- { PFRES_STATELOCKED, "15(state-locked)" },
-#elif defined(__OpenBSD__)
+ { 0, NULL }
+};
+
+static const struct tok pf_reasons_openbsd[] = {
+ { PFRES_MATCH, "0(match)" },
+ { PFRES_BADOFF, "1(bad-offset)" },
+ { PFRES_FRAG, "2(fragment)" },
+ { PFRES_SHORT, "3(short)" },
+ { PFRES_NORM, "4(normalize)" },
+ { PFRES_MEMORY, "5(memory)" },
+ { PFRES_TS, "6(bad-timestamp)" },
+ { PFRES_CONGEST, "7(congestion)" },
+ { PFRES_IPOPTIONS, "8(ip-option)" },
+ { PFRES_PROTCKSUM, "9(proto-cksum)" },
+ { PFRES_BADSTATE, "10(state-mismatch)" },
+ { PFRES_STATEINS, "11(state-insert)" },
+ { PFRES_MAXSTATES, "12(state-limit)" },
+ { PFRES_SRCLIMIT, "13(src-limit)" },
+ { PFRES_SYNPROXY, "14(synproxy)" },
{ PFRES_TRANSLATE, "15(translate)" },
{ PFRES_NOROUTE, "16(no-route)" },
-#elif defined(__APPLE__)
- { PFRES_DUMMYNET, "15(dummynet)" },
-#endif
+ { 0, NULL }
+};
+
+static const struct tok pf_reasons_other[] = {
+ { PFRES_MATCH, "0(match)" },
+ { PFRES_BADOFF, "1(bad-offset)" },
+ { PFRES_FRAG, "2(fragment)" },
+ { PFRES_SHORT, "3(short)" },
+ { PFRES_NORM, "4(normalize)" },
+ { PFRES_MEMORY, "5(memory)" },
+ { PFRES_TS, "6(bad-timestamp)" },
+ { PFRES_CONGEST, "7(congestion)" },
+ { PFRES_IPOPTIONS, "8(ip-option)" },
+ { PFRES_PROTCKSUM, "9(proto-cksum)" },
+ { PFRES_BADSTATE, "10(state-mismatch)" },
+ { PFRES_STATEINS, "11(state-insert)" },
+ { PFRES_MAXSTATES, "12(state-limit)" },
+ { PFRES_SRCLIMIT, "13(src-limit)" },
+ { PFRES_SYNPROXY, "14(synproxy)" },
+ { PFRES_STATELOCKED_DUMMYNET,
+ "15(state-locked (NetBSD)/dummynet(Darwin)" },
+ { PFRES_INVPORT, "16(invalid-port (Darwin))" },
{ 0, NULL }
};
/*
* Action values.
*/
-#define PF_PASS 0
-#define PF_DROP 1
-#define PF_SCRUB 2
-#define PF_NOSCRUB 3
-#define PF_NAT 4
-#define PF_NONAT 5
-#define PF_BINAT 6
-#define PF_NOBINAT 7
-#define PF_RDR 8
-#define PF_NORDR 9
-#define PF_SYNPROXY_DROP 10
-#if defined(__FreeBSD__)
-#define PF_DEFER 11
-#elif defined(__OpenBSD__)
-#define PF_DEFER 11
-#define PF_MATCH 12
-#define PF_DIVERT 13
-#define PF_RT 14
-#define PF_AFRT 15
-#elif defined(__APPLE__)
-#define PF_DUMMYNET 11
-#define PF_NODUMMYNET 12
-#define PF_NAT64 13
-#define PF_NONAT64 14
-#endif
-
-static const struct tok pf_actions[] = {
- { PF_PASS, "pass" },
- { PF_DROP, "block" },
- { PF_SCRUB, "scrub" },
- { PF_NAT, "nat" },
- { PF_NONAT, "nonat" },
- { PF_BINAT, "binat" },
- { PF_NOBINAT, "nobinat" },
- { PF_RDR, "rdr" },
- { PF_NORDR, "nordr" },
- { PF_SYNPROXY_DROP, "synproxy-drop" },
-#if defined(__FreeBSD__)
- { PF_DEFER, "defer" },
-#elif defined(__OpenBSD__)
- { PF_DEFER, "defer" },
- { PF_MATCH, "match" },
- { PF_DIVERT, "divert" },
- { PF_RT, "rt" },
- { PF_AFRT, "afrt" },
-#elif defined(__APPLE__)
- { PF_DUMMYNET, "dummynet" },
- { PF_NODUMMYNET, "nodummynet" },
- { PF_NAT64, "nat64" },
- { PF_NONAT64, "nonat64" },
-#endif
+#define PFACT_PASS 0
+#define PFACT_DROP 1
+#define PFACT_SCRUB 2
+#define PFACT_NOSCRUB 3
+#define PFACT_NAT 4
+#define PFACT_NONAT 5
+#define PFACT_BINAT 6
+#define PFACT_NOBINAT 7
+#define PFACT_RDR 8
+#define PFACT_NORDR 9
+#define PFACT_SYNPROXY_DROP 10
+
+/* FreeBSD and OpenBSD */
+#define PFACT_DEFER 11
+
+/* FreeBSD */
+#define PFACT_MATCH 12
+
+/* OpenBSD */
+#define PFACT_MATCH 12
+#define PFACT_DIVERT 13
+#define PFACT_RT 14
+#define PFACT_AFRT 15
+
+/* Darwin */
+#define PFACT_DUMMYNET 11
+#define PFACT_NODUMMYNET 12
+#define PFACT_NAT64 13
+#define PFACT_NONAT64 14
+
+static const struct tok pf_actions_freebsd[] = {
+ { PFACT_PASS, "pass" },
+ { PFACT_DROP, "block" },
+ { PFACT_SCRUB, "scrub" },
+ { PFACT_NOSCRUB, "noscrub" },
+ { PFACT_NAT, "nat" },
+ { PFACT_NONAT, "nonat" },
+ { PFACT_BINAT, "binat" },
+ { PFACT_NOBINAT, "nobinat" },
+ { PFACT_RDR, "rdr" },
+ { PFACT_NORDR, "nordr" },
+ { PFACT_SYNPROXY_DROP, "synproxy-drop" },
+ { PFACT_DEFER, "defer" },
+ { PFACT_MATCH, "match" },
+ { 0, NULL }
+};
+
+static const struct tok pf_actions_openbsd[] = {
+ { PFACT_PASS, "pass" },
+ { PFACT_DROP, "block" },
+ { PFACT_SCRUB, "scrub" },
+ { PFACT_NOSCRUB, "noscrub" },
+ { PFACT_NAT, "nat" },
+ { PFACT_NONAT, "nonat" },
+ { PFACT_BINAT, "binat" },
+ { PFACT_NOBINAT, "nobinat" },
+ { PFACT_RDR, "rdr" },
+ { PFACT_NORDR, "nordr" },
+ { PFACT_SYNPROXY_DROP, "synproxy-drop" },
+ { PFACT_DEFER, "defer" },
+ { PFACT_MATCH, "match" },
+ { PFACT_DIVERT, "divert" },
+ { PFACT_RT, "rt" },
+ { PFACT_AFRT, "afrt" },
+ { 0, NULL }
+};
+
+static const struct tok pf_actions_darwin[] = {
+ { PFACT_PASS, "pass" },
+ { PFACT_DROP, "block" },
+ { PFACT_SCRUB, "scrub" },
+ { PFACT_NOSCRUB, "noscrub" },
+ { PFACT_NAT, "nat" },
+ { PFACT_NONAT, "nonat" },
+ { PFACT_BINAT, "binat" },
+ { PFACT_NOBINAT, "nobinat" },
+ { PFACT_RDR, "rdr" },
+ { PFACT_NORDR, "nordr" },
+ { PFACT_SYNPROXY_DROP, "synproxy-drop" },
+ { PFACT_DUMMYNET, "dummynet (Darwin)" },
+ { PFACT_NODUMMYNET, "nodummynet (Darwin)" },
+ { PFACT_NAT64, "nat64 (Darwin)" },
+ { PFACT_NONAT64, "nonat64 (Darwin)" },
{ 0, NULL }
};
/*
* Direction values.
*/
-#define PF_INOUT 0
-#define PF_IN 1
-#define PF_OUT 2
-#if defined(__OpenBSD__)
-#define PF_FWD 3
-#endif
-
-static const struct tok pf_directions[] = {
- { PF_INOUT, "in/out" },
- { PF_IN, "in" },
- { PF_OUT, "out" },
-#if defined(__OpenBSD__)
- { PF_FWD, "fwd" },
-#endif
+#define PFDIR_INOUT 0
+#define PFDIR_IN 1
+#define PFDIR_OUT 2
+
+/* OpenBSD */
+#define PFDIR_FWD 3
+
+static const struct tok pf_directions_freebsd[] = {
+ { PFDIR_INOUT, "in/out" },
+ { PFDIR_IN, "in" },
+ { PFDIR_OUT, "out" },
+ { 0, NULL }
+};
+
+static const struct tok pf_directions_openbsd[] = {
+ { PFDIR_INOUT, "in/out" },
+ { PFDIR_IN, "in" },
+ { PFDIR_OUT, "out" },
+ { PFDIR_FWD, "fwd" },
+ { 0, NULL }
+};
+
+static const struct tok pf_directions_other[] = {
+ { PFDIR_INOUT, "in/out" },
+ { PFDIR_IN, "in" },
+ { PFDIR_OUT, "out" },
{ 0, NULL }
};
+static void
+print_pf_addr(netdissect_options *ndo, const char *tag, u_int naf,
+ const struct pf_addr *addr, const nd_uint16_t port)
+{
+ char buf[INET6_ADDRSTRLEN];
+ uint16_t portnum;
+
+ ND_PRINT("%s ", tag);
+ ND_TCHECK_SIZE(addr);
+ switch (naf) {
+
+ case BSD_AF_INET:
+ addrtostr(addr->v4, buf, sizeof(buf));
+ break;
+
+ case BSD_AF_INET6_BSD:
+ addrtostr6(addr->v6, buf, sizeof(buf));
+ break;
+
+ default:
+ strlcpy(buf, "?", sizeof(buf));
+ break;
+ }
+ ND_PRINT("%s:", buf);
+ portnum = GET_BE_U_2(port);
+ ND_PRINT("%u", portnum);
+}
+
static void
pflog_print(netdissect_options *ndo, const struct pfloghdr *hdr)
{
+ uint8_t length;
uint32_t rulenr, subrulenr;
+ uint32_t uid;
+ uint32_t ridentifier;
ndo->ndo_protocol = "pflog";
+ length = GET_U_1(hdr->length);
+
rulenr = GET_BE_U_4(hdr->rulenr);
subrulenr = GET_BE_U_4(hdr->subrulenr);
- if (subrulenr == (uint32_t)-1)
- ND_PRINT("rule %u/", rulenr);
- else {
- ND_PRINT("rule %u.", rulenr);
- nd_printjnp(ndo, (const u_char*)hdr->ruleset, PFLOG_RULESET_NAME_SIZE);
- ND_PRINT(".%u/", subrulenr);
+ ND_PRINT("rule ");
+ if (rulenr != (uint32_t)-1) {
+ ND_PRINT("%u", rulenr);
+ if (hdr->ruleset[0] != '\0') {
+ ND_PRINT(".");
+ nd_printjnp(ndo, (const u_char*)hdr->ruleset, PFLOG_RULESET_NAME_SIZE);
+ }
+ if (subrulenr != (uint32_t)-1)
+ ND_PRINT(".%u", subrulenr);
}
+ ND_PRINT("/");
- ND_PRINT("%s: %s %s on ",
- tok2str(pf_reasons, "unkn(%u)", GET_U_1(hdr->reason)),
- tok2str(pf_actions, "unkn(%u)", GET_U_1(hdr->action)),
- tok2str(pf_directions, "unkn(%u)", GET_U_1(hdr->dir)));
+ if (length == PFLOG_HEADER_LEN_FREEBSD)
+ ND_PRINT("%s", tok2str(pf_reasons_freebsd, "unkn(%u)", GET_U_1(hdr->reason)));
+ else if (length == PFLOG_HEADER_LEN_OPENBSD)
+ ND_PRINT("%s", tok2str(pf_reasons_openbsd, "unkn(%u)", GET_U_1(hdr->reason)));
+ else
+ ND_PRINT("%s", tok2str(pf_reasons_other, "unkn(%u)", GET_U_1(hdr->reason)));
+
+ /*
+ * In Darwin (macOS, etc.) and NetBSD, uid is set to
+ * UID_MAX if there's no UID, and UID_MAX is 2^31-1.
+ * UID_MAX is 2^31-1.
+ *
+ * In OpenBSD, uid is set to -1 if there's no UID, which
+ * means we'll see it as UINT_MAX, as we treat it as
+ * unsigned. UID_MAX is 2^32-1.
+ *
+ * In FreeBSD and DragonFly BSD, uid is set to UID_MAX
+ * if there's no UID. UID_MAX is 2^32-1.
+ *
+ * So:
+ *
+ * For OpenBSD and FreeBSD, check only for 2^32-1 (0xFFFFFFFFU)
+ * if there's no UID.
+ *
+ * For other OSes, it's either NetBSD, DragonFly BSD, or Darwin,
+ * check for both 2^31-1 (0x7FFFFFFFU) (NetBSD and Darwin) and
+ * 2^32-1 (0xFFFFFFFFU) (DragonFly BSD). That runs the risk of
+ * the UID not being printed for a DragonFly BSD log if it's
+ * 0x7FFFFFFF, but that's *probably* not going to be the case.
+ */
+ uid = GET_BE_U_4(hdr->uid);
+ if (length == PFLOG_HEADER_LEN_FREEBSD ||
+ length == PFLOG_HEADER_LEN_OPENBSD) {
+ if (uid != 0xFFFFFFFFU)
+ ND_PRINT(" [uid %u]", uid);
+ } else {
+ if (uid != 0xFFFFFFFFU && uid != 0x7FFFFFFFU)
+ ND_PRINT(" [uid %u]", uid);
+ }
+
+ if (length == PFLOG_HEADER_LEN_FREEBSD) {
+ ridentifier = GET_BE_U_4(hdr->u.freebsd.ridentifier);
+ if (ridentifier != 0)
+ ND_PRINT(" [ridentifier %u]", ridentifier);
+ }
+
+ if (length == PFLOG_HEADER_LEN_FREEBSD) {
+ ND_PRINT(": %s %s on ",
+ tok2str(pf_actions_freebsd, "unkn(%u)", GET_U_1(hdr->action)),
+ tok2str(pf_directions_freebsd, "unkn(%u)", GET_U_1(hdr->dir)));
+ } else if (length == PFLOG_HEADER_LEN_OPENBSD) {
+ ND_PRINT(": %s %s on ",
+ tok2str(pf_actions_openbsd, "unkn(%u)", GET_U_1(hdr->action)),
+ tok2str(pf_directions_openbsd, "unkn(%u)", GET_U_1(hdr->dir)));
+ } else {
+ /*
+ * We use the Darwin set of actions, as it's a superset
+ * of the NetBSD/DragonFly BSD set of actions.
+ */
+ ND_PRINT(": %s %s on ",
+ tok2str(pf_actions_darwin, "unkn(%u)", GET_U_1(hdr->action)),
+ tok2str(pf_directions_other, "unkn(%u)", GET_U_1(hdr->dir)));
+ }
nd_printjnp(ndo, (const u_char*)hdr->ifname, PFLOG_IFNAMSIZ);
ND_PRINT(": ");
+ if (length == PFLOG_HEADER_LEN_OPENBSD) {
+ if (ndo->ndo_vflag && GET_U_1(hdr->u.openbsd.rewritten)) {
+ uint8_t naf;
+
+ ND_PRINT("[rewritten: ");
+ naf = GET_U_1(hdr->u.openbsd.naf);
+ print_pf_addr(ndo, "src", naf, &hdr->u.openbsd.saddr,
+ hdr->u.openbsd.sport);
+ ND_PRINT(", ");
+ print_pf_addr(ndo, "src", naf, &hdr->u.openbsd.daddr,
+ hdr->u.openbsd.dport);
+ ND_PRINT("; ");
+ }
+ }
}
void
hdr = (const struct pfloghdr *)p;
hdrlen = GET_U_1(hdr->length);
ND_ICHECK_U(hdrlen, <, MIN_PFLOG_HDRLEN);
- hdrlen = roundup2(hdrlen, 4);
ND_ICHECK_U(hdrlen, >, MAX_PFLOG_HDRLEN);
+ hdrlen = roundup2(hdrlen, 4);
/* print what we know */
ND_TCHECK_LEN(hdr, hdrlen);