1026d7285Schristos /* 2026d7285Schristos * Copyright (c) 2013, Petar Alilovic, 3026d7285Schristos * Faculty of Electrical Engineering and Computing, University of Zagreb 4026d7285Schristos * All rights reserved 5026d7285Schristos * 6026d7285Schristos * Redistribution and use in source and binary forms, with or without 7026d7285Schristos * modification, are permitted provided that the following conditions are met: 8026d7285Schristos * 9026d7285Schristos * * Redistributions of source code must retain the above copyright notice, 10026d7285Schristos * this list of conditions and the following disclaimer. 11026d7285Schristos * * Redistributions in binary form must reproduce the above copyright 12026d7285Schristos * notice, this list of conditions and the following disclaimer in the 13026d7285Schristos * documentation and/or other materials provided with the distribution. 14026d7285Schristos * 15026d7285Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 16026d7285Schristos * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17026d7285Schristos * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18026d7285Schristos * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY 19026d7285Schristos * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20026d7285Schristos * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21026d7285Schristos * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22026d7285Schristos * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23026d7285Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24026d7285Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 25026d7285Schristos * DAMAGE. 26026d7285Schristos */ 27026d7285Schristos 28fdccd7e4Schristos #include <sys/cdefs.h> 29fdccd7e4Schristos #ifndef lint 30*26ba0b50Schristos __RCSID("$NetBSD: print-nflog.c,v 1.5 2024/09/02 16:15:32 christos Exp $"); 31fdccd7e4Schristos #endif 32fdccd7e4Schristos 33dc860a36Sspz /* \summary: DLT_NFLOG printer */ 34dc860a36Sspz 35c74ad251Schristos #include <config.h> 36026d7285Schristos 37c74ad251Schristos #include "netdissect-stdinc.h" 38026d7285Schristos 39784088dfSchristos #include "netdissect.h" 40c74ad251Schristos #include "extract.h" 41026d7285Schristos 42c74ad251Schristos #ifdef DLT_NFLOG 43c74ad251Schristos 44c74ad251Schristos /* 45c74ad251Schristos * Structure of an NFLOG header and TLV parts, as described at 46c74ad251Schristos * https://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html 47c74ad251Schristos * 48c74ad251Schristos * The NFLOG header is big-endian. 49c74ad251Schristos * 50c74ad251Schristos * The TLV length and type are in host byte order. The value is either 51c74ad251Schristos * big-endian or is an array of bytes in some externally-specified byte 52c74ad251Schristos * order (text string, link-layer address, link-layer header, packet 53c74ad251Schristos * data, etc.). 54c74ad251Schristos */ 55c74ad251Schristos typedef struct nflog_hdr { 56c74ad251Schristos nd_uint8_t nflog_family; /* address family */ 57c74ad251Schristos nd_uint8_t nflog_version; /* version */ 58c74ad251Schristos nd_uint16_t nflog_rid; /* resource ID */ 59c74ad251Schristos } nflog_hdr_t; 60c74ad251Schristos 61c74ad251Schristos #define NFLOG_HDR_LEN sizeof(nflog_hdr_t) 62c74ad251Schristos 63c74ad251Schristos typedef struct nflog_tlv { 64c74ad251Schristos nd_uint16_t tlv_length; /* tlv length */ 65c74ad251Schristos nd_uint16_t tlv_type; /* tlv type */ 66c74ad251Schristos /* value follows this */ 67c74ad251Schristos } nflog_tlv_t; 68c74ad251Schristos 69c74ad251Schristos #define NFLOG_TLV_LEN sizeof(nflog_tlv_t) 70c74ad251Schristos 71c74ad251Schristos typedef struct nflog_packet_hdr { 72c74ad251Schristos nd_uint16_t hw_protocol; /* hw protocol */ 73c74ad251Schristos nd_uint8_t hook; /* netfilter hook */ 74c74ad251Schristos nd_byte pad[1]; /* padding to 32 bits */ 75c74ad251Schristos } nflog_packet_hdr_t; 76c74ad251Schristos 77c74ad251Schristos typedef struct nflog_hwaddr { 78c74ad251Schristos nd_uint16_t hw_addrlen; /* address length */ 79c74ad251Schristos nd_byte pad[2]; /* padding to 32-bit boundary */ 80c74ad251Schristos nd_byte hw_addr[8]; /* address, up to 8 bytes */ 81c74ad251Schristos } nflog_hwaddr_t; 82c74ad251Schristos 83c74ad251Schristos typedef struct nflog_timestamp { 84c74ad251Schristos nd_uint64_t sec; 85c74ad251Schristos nd_uint64_t usec; 86c74ad251Schristos } nflog_timestamp_t; 87c74ad251Schristos 88c74ad251Schristos /* 89c74ad251Schristos * TLV types. 90c74ad251Schristos */ 91c74ad251Schristos #define NFULA_PACKET_HDR 1 /* nflog_packet_hdr_t */ 92c74ad251Schristos #define NFULA_MARK 2 /* packet mark from skbuff */ 93c74ad251Schristos #define NFULA_TIMESTAMP 3 /* nflog_timestamp_t for skbuff's time stamp */ 94c74ad251Schristos #define NFULA_IFINDEX_INDEV 4 /* ifindex of device on which packet received (possibly bridge group) */ 95c74ad251Schristos #define NFULA_IFINDEX_OUTDEV 5 /* ifindex of device on which packet transmitted (possibly bridge group) */ 96c74ad251Schristos #define NFULA_IFINDEX_PHYSINDEV 6 /* ifindex of physical device on which packet received (not bridge group) */ 97c74ad251Schristos #define NFULA_IFINDEX_PHYSOUTDEV 7 /* ifindex of physical device on which packet transmitted (not bridge group) */ 98c74ad251Schristos #define NFULA_HWADDR 8 /* nflog_hwaddr_t for hardware address */ 99c74ad251Schristos #define NFULA_PAYLOAD 9 /* packet payload */ 100c74ad251Schristos #define NFULA_PREFIX 10 /* text string - null-terminated, count includes NUL */ 101c74ad251Schristos #define NFULA_UID 11 /* UID owning socket on which packet was sent/received */ 102c74ad251Schristos #define NFULA_SEQ 12 /* sequence number of packets on this NFLOG socket */ 103*26ba0b50Schristos #define NFULA_SEQ_GLOBAL 13 /* sequence number of packets on all NFLOG sockets */ 104c74ad251Schristos #define NFULA_GID 14 /* GID owning socket on which packet was sent/received */ 105c74ad251Schristos #define NFULA_HWTYPE 15 /* ARPHRD_ type of skbuff's device */ 106c74ad251Schristos #define NFULA_HWHEADER 16 /* skbuff's MAC-layer header */ 107c74ad251Schristos #define NFULA_HWLEN 17 /* length of skbuff's MAC-layer header */ 108026d7285Schristos 109*26ba0b50Schristos /* 110*26ba0b50Schristos * Define two constants specifically for the two AF code points from the 111*26ba0b50Schristos * LINKTYPE_NFLOG specification above and use these constants instead of 112*26ba0b50Schristos * AF_INET and AF_INET6. This is the only way to dissect the "wire" encoding 113*26ba0b50Schristos * correctly because some BSD systems define AF_INET6 differently from Linux 114*26ba0b50Schristos * (see af.h) and Haiku defines both AF_INET and AF_INET6 differently from 115*26ba0b50Schristos * Linux. 116*26ba0b50Schristos */ 117*26ba0b50Schristos #define NFLOG_AF_INET 2 118*26ba0b50Schristos #define NFLOG_AF_INET6 10 119026d7285Schristos static const struct tok nflog_values[] = { 120*26ba0b50Schristos { NFLOG_AF_INET, "IPv4" }, 121*26ba0b50Schristos { NFLOG_AF_INET6, "IPv6" }, 122026d7285Schristos { 0, NULL } 123026d7285Schristos }; 124026d7285Schristos 125c74ad251Schristos static void 126c47fd378Schristos nflog_hdr_print(netdissect_options *ndo, const nflog_hdr_t *hdr, u_int length) 127026d7285Schristos { 128c74ad251Schristos ND_PRINT("version %u, resource ID %u", 129c74ad251Schristos GET_U_1(hdr->nflog_version), GET_BE_U_2(hdr->nflog_rid)); 130026d7285Schristos 131026d7285Schristos if (!ndo->ndo_qflag) { 132c74ad251Schristos ND_PRINT(", family %s (%u)", 133026d7285Schristos tok2str(nflog_values, "Unknown", 134c74ad251Schristos GET_U_1(hdr->nflog_family)), 135c74ad251Schristos GET_U_1(hdr->nflog_family)); 136026d7285Schristos } else { 137c74ad251Schristos ND_PRINT(", %s", 138026d7285Schristos tok2str(nflog_values, 139026d7285Schristos "Unknown NFLOG (0x%02x)", 140c74ad251Schristos GET_U_1(hdr->nflog_family))); 141026d7285Schristos } 142026d7285Schristos 143c74ad251Schristos ND_PRINT(", length %u: ", length); 144026d7285Schristos } 145026d7285Schristos 146c74ad251Schristos void 147c47fd378Schristos nflog_if_print(netdissect_options *ndo, 148026d7285Schristos const struct pcap_pkthdr *h, const u_char *p) 149026d7285Schristos { 150026d7285Schristos const nflog_hdr_t *hdr = (const nflog_hdr_t *)p; 151c47fd378Schristos uint16_t size; 152c74ad251Schristos uint16_t h_size = NFLOG_HDR_LEN; 153026d7285Schristos u_int caplen = h->caplen; 154026d7285Schristos u_int length = h->len; 155026d7285Schristos 156c74ad251Schristos ndo->ndo_protocol = "nflog"; 157c74ad251Schristos if (caplen < NFLOG_HDR_LEN) { 158c74ad251Schristos nd_print_trunc(ndo); 159c74ad251Schristos ndo->ndo_ll_hdr_len += caplen; 160c74ad251Schristos return; 161026d7285Schristos } 162c74ad251Schristos ndo->ndo_ll_hdr_len += NFLOG_HDR_LEN; 163026d7285Schristos 164c74ad251Schristos ND_TCHECK_SIZE(hdr); 165c74ad251Schristos if (GET_U_1(hdr->nflog_version) != 0) { 166c74ad251Schristos ND_PRINT("version %u (unknown)", GET_U_1(hdr->nflog_version)); 167c74ad251Schristos return; 168026d7285Schristos } 169026d7285Schristos 170026d7285Schristos if (ndo->ndo_eflag) 171026d7285Schristos nflog_hdr_print(ndo, hdr, length); 172026d7285Schristos 173c74ad251Schristos p += NFLOG_HDR_LEN; 174c74ad251Schristos length -= NFLOG_HDR_LEN; 175c74ad251Schristos caplen -= NFLOG_HDR_LEN; 176026d7285Schristos 177c47fd378Schristos while (length > 0) { 178c74ad251Schristos const nflog_tlv_t *tlv; 179c74ad251Schristos 180c47fd378Schristos /* We have some data. Do we have enough for the TLV header? */ 181c74ad251Schristos if (caplen < NFLOG_TLV_LEN) 182c74ad251Schristos goto trunc; /* No. */ 183c47fd378Schristos 184026d7285Schristos tlv = (const nflog_tlv_t *) p; 185c74ad251Schristos ND_TCHECK_SIZE(tlv); 186c74ad251Schristos size = GET_HE_U_2(tlv->tlv_length); 187026d7285Schristos if (size % 4 != 0) 188026d7285Schristos size += 4 - size % 4; 189026d7285Schristos 190c47fd378Schristos /* Is the TLV's length less than the minimum? */ 191c74ad251Schristos if (size < NFLOG_TLV_LEN) 192c74ad251Schristos goto trunc; /* Yes. Give up now. */ 193c47fd378Schristos 194c47fd378Schristos /* Do we have enough data for the full TLV? */ 195c74ad251Schristos if (caplen < size) 196c74ad251Schristos goto trunc; /* No. */ 197c47fd378Schristos 198c74ad251Schristos if (GET_HE_U_2(tlv->tlv_type) == NFULA_PAYLOAD) { 199c47fd378Schristos /* 200c47fd378Schristos * This TLV's data is the packet payload. 201c47fd378Schristos * Skip past the TLV header, and break out 202c47fd378Schristos * of the loop so we print the packet data. 203c47fd378Schristos */ 204c74ad251Schristos p += NFLOG_TLV_LEN; 205c74ad251Schristos h_size += NFLOG_TLV_LEN; 206c74ad251Schristos length -= NFLOG_TLV_LEN; 207c74ad251Schristos caplen -= NFLOG_TLV_LEN; 208c47fd378Schristos break; 209c47fd378Schristos } 210026d7285Schristos 211026d7285Schristos p += size; 212c47fd378Schristos h_size += size; 213c47fd378Schristos length -= size; 214c47fd378Schristos caplen -= size; 215c47fd378Schristos } 216026d7285Schristos 217c74ad251Schristos switch (GET_U_1(hdr->nflog_family)) { 218026d7285Schristos 219*26ba0b50Schristos case NFLOG_AF_INET: 220026d7285Schristos ip_print(ndo, p, length); 221026d7285Schristos break; 222026d7285Schristos 223*26ba0b50Schristos case NFLOG_AF_INET6: 224026d7285Schristos ip6_print(ndo, p, length); 225026d7285Schristos break; 226026d7285Schristos 227026d7285Schristos default: 228026d7285Schristos if (!ndo->ndo_eflag) 229026d7285Schristos nflog_hdr_print(ndo, hdr, 230c74ad251Schristos length + NFLOG_HDR_LEN); 231026d7285Schristos 232026d7285Schristos if (!ndo->ndo_suppress_default_print) 233c47fd378Schristos ND_DEFAULTPRINT(p, caplen); 234026d7285Schristos break; 235026d7285Schristos } 236026d7285Schristos 237c74ad251Schristos ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN; 238c74ad251Schristos return; 239c74ad251Schristos trunc: 240c74ad251Schristos nd_print_trunc(ndo); 241c74ad251Schristos ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN; 242026d7285Schristos } 243026d7285Schristos 244c74ad251Schristos #endif /* DLT_NFLOG */ 245