1 /*
2  * Copyright (c) 2013, Petar Alilovic,
3  * Faculty of Electrical Engineering and Computing, University of Zagreb
4  * All rights reserved
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice,
10  *         this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  *         notice, this list of conditions and the following disclaimer in the
13  *         documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
25  * DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: print-nflog.c,v 1.5 2024/09/02 16:15:32 christos Exp $");
31 #endif
32 
33 /* \summary: DLT_NFLOG printer */
34 
35 #include <config.h>
36 
37 #include "netdissect-stdinc.h"
38 
39 #include "netdissect.h"
40 #include "extract.h"
41 
42 #ifdef DLT_NFLOG
43 
44 /*
45  * Structure of an NFLOG header and TLV parts, as described at
46  * https://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html
47  *
48  * The NFLOG header is big-endian.
49  *
50  * The TLV length and type are in host byte order.  The value is either
51  * big-endian or is an array of bytes in some externally-specified byte
52  * order (text string, link-layer address, link-layer header, packet
53  * data, etc.).
54  */
55 typedef struct nflog_hdr {
56           nd_uint8_t          nflog_family;                 /* address family */
57           nd_uint8_t          nflog_version;                /* version */
58           nd_uint16_t         nflog_rid;                    /* resource ID */
59 } nflog_hdr_t;
60 
61 #define NFLOG_HDR_LEN sizeof(nflog_hdr_t)
62 
63 typedef struct nflog_tlv {
64           nd_uint16_t         tlv_length;                   /* tlv length */
65           nd_uint16_t         tlv_type;           /* tlv type */
66           /* value follows this */
67 } nflog_tlv_t;
68 
69 #define NFLOG_TLV_LEN sizeof(nflog_tlv_t)
70 
71 typedef struct nflog_packet_hdr {
72           nd_uint16_t         hw_protocol;        /* hw protocol */
73           nd_uint8_t          hook;               /* netfilter hook */
74           nd_byte             pad[1];             /* padding to 32 bits */
75 } nflog_packet_hdr_t;
76 
77 typedef struct nflog_hwaddr {
78           nd_uint16_t         hw_addrlen;         /* address length */
79           nd_byte             pad[2];             /* padding to 32-bit boundary */
80           nd_byte             hw_addr[8];         /* address, up to 8 bytes */
81 } nflog_hwaddr_t;
82 
83 typedef struct nflog_timestamp {
84           nd_uint64_t         sec;
85           nd_uint64_t         usec;
86 } nflog_timestamp_t;
87 
88 /*
89  * TLV types.
90  */
91 #define NFULA_PACKET_HDR                1         /* nflog_packet_hdr_t */
92 #define NFULA_MARK                      2         /* packet mark from skbuff */
93 #define NFULA_TIMESTAMP                           3         /* nflog_timestamp_t for skbuff's time stamp */
94 #define NFULA_IFINDEX_INDEV             4         /* ifindex of device on which packet received (possibly bridge group) */
95 #define NFULA_IFINDEX_OUTDEV            5         /* ifindex of device on which packet transmitted (possibly bridge group) */
96 #define NFULA_IFINDEX_PHYSINDEV                   6         /* ifindex of physical device on which packet received (not bridge group) */
97 #define NFULA_IFINDEX_PHYSOUTDEV        7         /* ifindex of physical device on which packet transmitted (not bridge group) */
98 #define NFULA_HWADDR                              8         /* nflog_hwaddr_t for hardware address */
99 #define NFULA_PAYLOAD                             9         /* packet payload */
100 #define NFULA_PREFIX                              10        /* text string - null-terminated, count includes NUL */
101 #define NFULA_UID                       11        /* UID owning socket on which packet was sent/received */
102 #define NFULA_SEQ                       12        /* sequence number of packets on this NFLOG socket */
103 #define NFULA_SEQ_GLOBAL                13        /* sequence number of packets on all NFLOG sockets */
104 #define NFULA_GID                       14        /* GID owning socket on which packet was sent/received */
105 #define NFULA_HWTYPE                              15        /* ARPHRD_ type of skbuff's device */
106 #define NFULA_HWHEADER                            16        /* skbuff's MAC-layer header */
107 #define NFULA_HWLEN                     17        /* length of skbuff's MAC-layer header */
108 
109 /*
110  * Define two constants specifically for the two AF code points from the
111  * LINKTYPE_NFLOG specification above and use these constants instead of
112  * AF_INET and AF_INET6.  This is the only way to dissect the "wire" encoding
113  * correctly because some BSD systems define AF_INET6 differently from Linux
114  * (see af.h) and Haiku defines both AF_INET and AF_INET6 differently from
115  * Linux.
116  */
117 #define NFLOG_AF_INET   2
118 #define NFLOG_AF_INET6 10
119 static const struct tok nflog_values[] = {
120           { NFLOG_AF_INET,    "IPv4" },
121           { NFLOG_AF_INET6,   "IPv6" },
122           { 0,                          NULL }
123 };
124 
125 static void
nflog_hdr_print(netdissect_options * ndo,const nflog_hdr_t * hdr,u_int length)126 nflog_hdr_print(netdissect_options *ndo, const nflog_hdr_t *hdr, u_int length)
127 {
128           ND_PRINT("version %u, resource ID %u",
129               GET_U_1(hdr->nflog_version), GET_BE_U_2(hdr->nflog_rid));
130 
131           if (!ndo->ndo_qflag) {
132                     ND_PRINT(", family %s (%u)",
133                                tok2str(nflog_values, "Unknown",
134                                          GET_U_1(hdr->nflog_family)),
135                                GET_U_1(hdr->nflog_family));
136                     } else {
137                     ND_PRINT(", %s",
138                                tok2str(nflog_values,
139                                          "Unknown NFLOG (0x%02x)",
140                                GET_U_1(hdr->nflog_family)));
141                     }
142 
143           ND_PRINT(", length %u: ", length);
144 }
145 
146 void
nflog_if_print(netdissect_options * ndo,const struct pcap_pkthdr * h,const u_char * p)147 nflog_if_print(netdissect_options *ndo,
148                  const struct pcap_pkthdr *h, const u_char *p)
149 {
150           const nflog_hdr_t *hdr = (const nflog_hdr_t *)p;
151           uint16_t size;
152           uint16_t h_size = NFLOG_HDR_LEN;
153           u_int caplen = h->caplen;
154           u_int length = h->len;
155 
156           ndo->ndo_protocol = "nflog";
157           if (caplen < NFLOG_HDR_LEN) {
158                     nd_print_trunc(ndo);
159                     ndo->ndo_ll_hdr_len += caplen;
160                     return;
161           }
162           ndo->ndo_ll_hdr_len += NFLOG_HDR_LEN;
163 
164           ND_TCHECK_SIZE(hdr);
165           if (GET_U_1(hdr->nflog_version) != 0) {
166                     ND_PRINT("version %u (unknown)", GET_U_1(hdr->nflog_version));
167                     return;
168           }
169 
170           if (ndo->ndo_eflag)
171                     nflog_hdr_print(ndo, hdr, length);
172 
173           p += NFLOG_HDR_LEN;
174           length -= NFLOG_HDR_LEN;
175           caplen -= NFLOG_HDR_LEN;
176 
177           while (length > 0) {
178                     const nflog_tlv_t *tlv;
179 
180                     /* We have some data.  Do we have enough for the TLV header? */
181                     if (caplen < NFLOG_TLV_LEN)
182                               goto trunc;         /* No. */
183 
184                     tlv = (const nflog_tlv_t *) p;
185                     ND_TCHECK_SIZE(tlv);
186                     size = GET_HE_U_2(tlv->tlv_length);
187                     if (size % 4 != 0)
188                               size += 4 - size % 4;
189 
190                     /* Is the TLV's length less than the minimum? */
191                     if (size < NFLOG_TLV_LEN)
192                               goto trunc;         /* Yes. Give up now. */
193 
194                     /* Do we have enough data for the full TLV? */
195                     if (caplen < size)
196                               goto trunc;         /* No. */
197 
198                     if (GET_HE_U_2(tlv->tlv_type) == NFULA_PAYLOAD) {
199                               /*
200                                * This TLV's data is the packet payload.
201                                * Skip past the TLV header, and break out
202                                * of the loop so we print the packet data.
203                                */
204                               p += NFLOG_TLV_LEN;
205                               h_size += NFLOG_TLV_LEN;
206                               length -= NFLOG_TLV_LEN;
207                               caplen -= NFLOG_TLV_LEN;
208                               break;
209                     }
210 
211                     p += size;
212                     h_size += size;
213                     length -= size;
214                     caplen -= size;
215           }
216 
217           switch (GET_U_1(hdr->nflog_family)) {
218 
219           case NFLOG_AF_INET:
220                     ip_print(ndo, p, length);
221                     break;
222 
223           case NFLOG_AF_INET6:
224                     ip6_print(ndo, p, length);
225                     break;
226 
227           default:
228                     if (!ndo->ndo_eflag)
229                               nflog_hdr_print(ndo, hdr,
230                                                   length + NFLOG_HDR_LEN);
231 
232                     if (!ndo->ndo_suppress_default_print)
233                               ND_DEFAULTPRINT(p, caplen);
234                     break;
235           }
236 
237           ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN;
238           return;
239 trunc:
240           nd_print_trunc(ndo);
241           ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN;
242 }
243 
244 #endif /* DLT_NFLOG */
245