X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/e070cf232ffadbdbbca68b132ef369675890e566..HEAD:/print-pflog.c diff --git a/print-pflog.c b/print-pflog.c index f122a5a3..f65ede29 100644 --- a/print-pflog.c +++ b/print-pflog.c @@ -1,5 +1,3 @@ -/* $OpenBSD: print-pflog.c,v 1.9 2001/09/18 14:52:53 jakob Exp $ */ - /* * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996 * The Regents of the University of California. All rights reserved. @@ -21,136 +19,523 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static const char rcsid[] = - "@(#) $Header: /tcpdump/master/tcpdump/print-pflog.c,v 1.5 2002-12-18 08:53:22 guy Exp $ (LBL)"; -#endif +/* \summary: *BSD/Darwin packet filter log file printer */ + +#include -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include -#include +#include "netdissect-stdinc.h" -#include -#include +#define ND_LONGJMP_FROM_TCHECK +#include "netdissect.h" +#include "extract.h" +#include "af.h" +#include "addrtostr.h" -#include "interface.h" -#include "addrtoname.h" +/* + * pflog headers, at least as they exist now. + */ +#define PFLOG_IFNAMSIZ 16 +#define PFLOG_RULESET_NAME_SIZE 16 -/* The header in OpenBSD pflog files. */ +struct pf_addr { + union { + nd_ipv4 v4; + nd_ipv6 v6; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#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 { - u_int32_t af; - char ifname[16]; - int16_t rnr; - u_int16_t reason; - u_int16_t action; - u_int16_t dir; + nd_uint8_t length; + nd_uint8_t af; + nd_uint8_t action; + nd_uint8_t reason; + char ifname[PFLOG_IFNAMSIZ]; + char ruleset[PFLOG_RULESET_NAME_SIZE]; + nd_uint32_t rulenr; + nd_uint32_t subrulenr; + nd_uint32_t uid; + nd_int32_t pid; + nd_uint32_t rule_uid; + nd_int32_t rule_pid; + nd_uint8_t dir; + 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 PFLOG_HDRLEN sizeof(struct pfloghdr) - -/* Actions */ -#define PF_PASS 0 -#define PF_DROP 1 -#define PF_SCRUB 2 - -/* Directions */ -#define PF_IN 0 -#define PF_OUT 1 - -static struct tok pf_reasons[] = { - { 0, "match" }, - { 1, "bad-offset" }, - { 2, "fragment" }, - { 3, "short" }, - { 4, "normalize" }, - { 5, "memory" }, + +/* + * 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_MATCH 0 +#define PFRES_BADOFF 1 +#define PFRES_FRAG 2 +#define PFRES_SHORT 3 +#define PFRES_NORM 4 +#define PFRES_MEMORY 5 +#define PFRES_TS 6 +#define PFRES_CONGEST 7 +#define PFRES_IPOPTIONS 8 +#define PFRES_PROTCKSUM 9 +#define PFRES_BADSTATE 10 +#define PFRES_STATEINS 11 +#define PFRES_MAXSTATES 12 +#define PFRES_SRCLIMIT 13 +#define PFRES_SYNPROXY 14 + +/* FreeBSD */ +#define PFRES_MAPFAILED 15 + +/* OpenBSD */ +#define PFRES_TRANSLATE 15 +#define PFRES_NOROUTE 16 + +/* 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_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_MAPFAILED, "15(map-failed)" }, + { 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)" }, + { 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 } }; -static struct tok pf_actions[] = { - { PF_PASS, "pass" }, - { PF_DROP, "drop" }, - { PF_SCRUB, "scrub" }, +/* + * Action values. + */ +#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 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 struct tok pf_directions[] = { - { PF_IN, "in" }, - { PF_OUT, "out" }, +static const struct tok pf_directions_openbsd[] = { + { PFDIR_INOUT, "in/out" }, + { PFDIR_IN, "in" }, + { PFDIR_OUT, "out" }, + { PFDIR_FWD, "fwd" }, { 0, NULL } }; -#define OPENBSD_AF_INET 2 -#define OPENBSD_AF_INET6 24 +static const struct tok pf_directions_other[] = { + { PFDIR_INOUT, "in/out" }, + { PFDIR_IN, "in" }, + { PFDIR_OUT, "out" }, + { 0, NULL } +}; static void -pflog_print(const struct pfloghdr *hdr) +print_pf_addr(netdissect_options *ndo, const char *tag, u_int naf, + const struct pf_addr *addr, const nd_uint16_t port) { - printf("rule %d/%s: %s %s on %s: ", - (short)ntohs(hdr->rnr), - tok2str(pf_reasons, "unkn(%u)", ntohs(hdr->reason)), - tok2str(pf_actions, "unkn(%u)", ntohs(hdr->action)), - tok2str(pf_directions, "unkn(%u)", ntohs(hdr->dir)), - hdr->ifname); + 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); } -void -pflog_if_print(u_char *user _U_, const struct pcap_pkthdr *h, - register const u_char *p) +static void +pflog_print(netdissect_options *ndo, const struct pfloghdr *hdr) { - u_int length = h->len; - u_int caplen = h->caplen; - const struct pfloghdr *hdr; - u_int8_t af; + uint8_t length; + uint32_t rulenr, subrulenr; + uint32_t uid; + uint32_t ridentifier; - ts_print(&h->ts); + ndo->ndo_protocol = "pflog"; + length = GET_U_1(hdr->length); - if (caplen < PFLOG_HDRLEN) { - printf("[|pflog]"); - goto out; + rulenr = GET_BE_U_4(hdr->rulenr); + subrulenr = GET_BE_U_4(hdr->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("/"); + + 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))); /* - * Some printers want to check that they're not walking off the - * end of the packet. - * Rather than pass it all the way down, we set this global. + * 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. */ - snapend = p + caplen; + 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 +pflog_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, + const u_char *p) +{ + u_int length = h->len; + u_int hdrlen; + u_int caplen = h->caplen; + const struct pfloghdr *hdr; + uint8_t af; + + ndo->ndo_protocol = "pflog"; + /* check length */ + ND_ICHECK_U(length, <, MIN_PFLOG_HDRLEN); hdr = (const struct pfloghdr *)p; - if (eflag) - pflog_print(hdr); - af = ntohl(hdr->af); - length -= PFLOG_HDRLEN; - caplen -= PFLOG_HDRLEN; - p += PFLOG_HDRLEN; + hdrlen = GET_U_1(hdr->length); + ND_ICHECK_U(hdrlen, <, MIN_PFLOG_HDRLEN); + ND_ICHECK_U(hdrlen, >, MAX_PFLOG_HDRLEN); + hdrlen = roundup2(hdrlen, 4); + + /* print what we know */ + ND_TCHECK_LEN(hdr, hdrlen); + ndo->ndo_ll_hdr_len += hdrlen; + if (ndo->ndo_eflag) + pflog_print(ndo, hdr); + + /* skip to the real packet */ + af = GET_U_1(hdr->af); + length -= hdrlen; + caplen -= hdrlen; + p += hdrlen; switch (af) { - case OPENBSD_AF_INET: - ip_print(p, length); - break; + /* + * If there's a system that doesn't use the AF_INET + * from 4.2BSD, feel free to add its value to af.h + * and use it here. + * + * Hopefully, there isn't. + */ + case BSD_AF_INET: + ip_print(ndo, p, length); + break; -#ifdef INET6 - case OPENBSD_AF_INET6: - ip6_print(p, length); - break; -#endif + /* + * Try all AF_INET6 values for all systems with pflog, + * including Darwin. + */ + case BSD_AF_INET6_BSD: + case BSD_AF_INET6_FREEBSD: + case BSD_AF_INET6_DARWIN: + ip6_print(ndo, p, length); + break; default: /* address family not handled, print raw packet */ - if (!eflag) - pflog_print(hdr); - if (!xflag && !qflag) - default_print(p, caplen); + if (!ndo->ndo_eflag) + pflog_print(ndo, hdr); + if (!ndo->ndo_suppress_default_print) + ND_DEFAULTPRINT(p, caplen); } - if (xflag) - default_print(p, caplen); -out: - putchar('\n'); - --infodelay; - if (infoprint) - info(0); + return; + +invalid: + nd_print_invalid(ndo); }