X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/ec799d60f6cd3f41041b57efe3963c28dda94d4a..HEAD:/print-pflog.c diff --git a/print-pflog.c b/print-pflog.c index d1a46156..f65ede29 100644 --- a/print-pflog.c +++ b/print-pflog.c @@ -19,169 +19,523 @@ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ -#ifndef lint -static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/tcpdump/print-pflog.c,v 1.16 2007-09-12 19:36:18 guy Exp $ (LBL)"; -#endif - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HAVE_NET_PFVAR_H -#error "No pf headers available" -#endif -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "interface.h" -#include "addrtoname.h" - -static struct tok pf_reasons[] = { - { 0, "0(match)" }, - { 1, "1(bad-offset)" }, - { 2, "2(fragment)" }, - { 3, "3(short)" }, - { 4, "4(normalize)" }, - { 5, "5(memory)" }, - { 6, "6(bad-timestamp)" }, - { 7, "7(congestion)" }, - { 8, "8(ip-option)" }, - { 9, "9(proto-cksum)" }, - { 10, "10(state-mismatch)" }, - { 11, "11(state-insert)" }, - { 12, "12(state-limit)" }, - { 13, "13(src-limit)" }, - { 14, "14(synproxy)" }, +/* \summary: *BSD/Darwin packet filter log file printer */ + +#include + +#include + +#include "netdissect-stdinc.h" + +#define ND_LONGJMP_FROM_TCHECK +#include "netdissect.h" +#include "extract.h" +#include "af.h" +#include "addrtostr.h" + +/* + * pflog headers, at least as they exist now. + */ +#define PFLOG_IFNAMSIZ 16 +#define PFLOG_RULESET_NAME_SIZE 16 + +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 { + 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; +}; + +/* + * 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 struct tok pf_actions[] = { - { PF_PASS, "pass" }, - { PF_DROP, "block" }, - { PF_SCRUB, "scrub" }, - { PF_NAT, "nat" }, - { PF_NONAT, "nat" }, - { PF_BINAT, "binat" }, - { PF_NOBINAT, "binat" }, - { PF_RDR, "rdr" }, - { PF_NORDR, "rdr" }, - { PF_SYNPROXY_DROP, "synproxy-drop" }, +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 } +}; + +/* + * 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 } }; -static struct tok pf_directions[] = { - { PF_INOUT, "in/out" }, - { PF_IN, "in" }, - { PF_OUT, "out" }, +/* + * 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 const struct tok pf_directions_openbsd[] = { + { PFDIR_INOUT, "in/out" }, + { PFDIR_IN, "in" }, + { PFDIR_OUT, "out" }, + { PFDIR_FWD, "fwd" }, { 0, NULL } }; -/* For reading capture files on other systems */ -#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) { - u_int32_t rulenr, subrulenr; + 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); +} - rulenr = EXTRACT_32BITS(&hdr->rulenr); - subrulenr = EXTRACT_32BITS(&hdr->subrulenr); - if (subrulenr == (u_int32_t)-1) - printf("rule %u/", rulenr); +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); + 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 - printf("rule %u.%s.%u/", rulenr, hdr->ruleset, subrulenr); + 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; - printf("%s: %s %s on %s: ", - tok2str(pf_reasons, "unkn(%u)", hdr->reason), - tok2str(pf_actions, "unkn(%u)", hdr->action), - tok2str(pf_directions, "unkn(%u)", hdr->dir), - hdr->ifname); + 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("; "); + } + } } -u_int -pflog_if_print(const struct pcap_pkthdr *h, register const u_char *p) +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; - u_int8_t af; + uint8_t af; + ndo->ndo_protocol = "pflog"; /* check length */ - if (caplen < sizeof(u_int8_t)) { - printf("[|pflog]"); - return (caplen); - } - -#define MIN_PFLOG_HDRLEN 45 - hdr = (struct pfloghdr *)p; - if (hdr->length < MIN_PFLOG_HDRLEN) { - printf("[pflog: invalid header length!]"); - return (hdr->length); /* XXX: not really */ - } - hdrlen = BPF_WORDALIGN(hdr->length); + ND_ICHECK_U(length, <, MIN_PFLOG_HDRLEN); - if (caplen < hdrlen) { - printf("[|pflog]"); - return (hdrlen); /* XXX: true? */ - } + hdr = (const struct pfloghdr *)p; + 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 */ - hdr = (struct pfloghdr *)p; - TCHECK(*hdr); - if (eflag) - pflog_print(hdr); - + 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 = hdr->af; + af = GET_U_1(hdr->af); length -= hdrlen; caplen -= hdrlen; p += hdrlen; switch (af) { - case AF_INET: -#if OPENBSD_AF_INET != AF_INET - case OPENBSD_AF_INET: /* XXX: read pcap files */ -#endif - ip_print(gndo, p, length); + /* + * 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 AF_INET6: -#if OPENBSD_AF_INET6 != AF_INET6 - case OPENBSD_AF_INET6: /* XXX: read pcap files */ -#endif - ip6_print(p, length); + /* + * 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; -#endif default: /* address family not handled, print raw packet */ - if (!eflag) - pflog_print(hdr); - if (!suppress_default_print) - default_print(p, caplen); + if (!ndo->ndo_eflag) + pflog_print(ndo, hdr); + if (!ndo->ndo_suppress_default_print) + ND_DEFAULTPRINT(p, caplen); } - - return (hdrlen); -trunc: - printf("[|pflog]"); - return (hdrlen); -} -/* - * Local Variables: - * c-style: whitesmith - * c-basic-offset: 8 - * End: - */ + return; + +invalid: + nd_print_invalid(ndo); +}