2 * copyright (c) 2001-2012 Emil Mikulic.
4 * decode.c: packet decoding.
6 * Given a captured packet, decode it and fill out a pktsummary struct which
7 * will be sent to the accounting code in acct.c
9 * You may use, modify and redistribute this file under the terms of the
10 * GNU General Public License version 2. (see COPYING.GPL)
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
24 #include <arpa/inet.h> /* inet_ntoa() */
25 #include <net/if.h> /* struct ifreq */
27 /* need struct ether_header */
28 #ifdef __NetBSD__ /* works for NetBSD 5.1.2 */
29 # include <netinet/if_ether.h>
32 # include <sys/queue.h>
33 # include <net/if_arp.h>
34 # include <netinet/if_ether.h>
37 # include <sys/ethernet.h>
38 # define ETHER_HDR_LEN 14
41 # include <netinet/if_ether.h>
42 # define ETHER_HDR_LEN 14
44 # include <net/ethernet.h>
50 #ifndef ETHERTYPE_PPPOE
51 # define ETHERTYPE_PPPOE 0x8864
53 #ifndef ETHERTYPE_IPV6
54 # define ETHERTYPE_IPV6 0x86DD
57 #include <netinet/in_systm.h> /* n_long */
58 #include <netinet/ip.h> /* struct ip */
59 #include <netinet/ip6.h> /* struct ip6_hdr */
61 #include <netinet/tcp.h> /* struct tcphdr */
62 #include <netinet/udp.h> /* struct udphdr */
65 #define FDDI_HDR_LEN 21
66 #define IP_HDR_LEN sizeof(struct ip)
67 #define IPV6_HDR_LEN sizeof(struct ip6_hdr)
68 #define TCP_HDR_LEN sizeof(struct tcphdr)
69 #define UDP_HDR_LEN sizeof(struct udphdr)
70 #define NULL_HDR_LEN 4
71 #define SLL_HDR_LEN 16
75 # define IPV6_VERSION 0x60
78 #ifndef IPV6_VERSION_MASK
79 # define IPV6_VERSION_MASK 0xF0
82 static int decode_ether(DECODER_ARGS
);
83 static int decode_loop(DECODER_ARGS
);
84 static int decode_null(DECODER_ARGS
);
85 static int decode_ppp(DECODER_ARGS
);
86 static int decode_pppoe(DECODER_ARGS
);
88 static int decode_linux_sll(DECODER_ARGS
);
90 static int decode_raw(DECODER_ARGS
);
92 #define HELPER_ARGS const u_char *pdata, \
96 static int helper_pppoe(HELPER_ARGS
);
97 static int helper_ip(HELPER_ARGS
);
98 static int helper_ipv6(HELPER_ARGS
);
99 static void helper_ip_deeper(HELPER_ARGS
); /* protocols like TCP/UDP */
101 /* Link-type header information */
102 static const struct linkhdr linkhdrs
[] = {
103 /* linktype hdrlen handler */
104 { DLT_EN10MB
, ETHER_HDR_LEN
, decode_ether
},
105 { DLT_LOOP
, NULL_HDR_LEN
, decode_loop
},
106 { DLT_NULL
, NULL_HDR_LEN
, decode_null
},
107 { DLT_PPP
, PPP_HDR_LEN
, decode_ppp
},
108 #if defined(__NetBSD__)
109 { DLT_PPP_SERIAL
, PPP_HDR_LEN
, decode_ppp
},
111 { DLT_FDDI
, FDDI_HDR_LEN
, NULL
},
112 { DLT_PPP_ETHER
, PPPOE_HDR_LEN
, decode_pppoe
},
114 { DLT_LINUX_SLL
, SLL_HDR_LEN
, decode_linux_sll
},
116 { DLT_RAW
, RAW_HDR_LEN
, decode_raw
},
120 /* Returns a pointer to the linkhdr record matching the given linktype, or
121 * NULL if no matching entry found.
123 const struct linkhdr
*getlinkhdr(const int linktype
) {
126 for (i
=0; linkhdrs
[i
].linktype
!= -1; i
++)
127 if (linkhdrs
[i
].linktype
== linktype
)
128 return (&(linkhdrs
[i
]));
132 /* Returns the minimum snaplen needed to decode everything up to and including
133 * the TCP/UDP packet headers.
135 int getsnaplen(const struct linkhdr
*lh
) {
136 return (int)(lh
->hdrlen
+ IPV6_HDR_LEN
+ MAX(TCP_HDR_LEN
, UDP_HDR_LEN
));
139 static int decode_ether(DECODER_ARGS
) {
141 const struct ether_header
*hdr
= (const struct ether_header
*)pdata
;
143 if (pheader
->caplen
< ETHER_HDR_LEN
) {
144 verbosef("ether: packet too short (%u bytes)", pheader
->caplen
);
148 memcpy(sm
->src_mac
, hdr
->ether_shost
.ether_addr_octet
, sizeof(sm
->src_mac
));
149 memcpy(sm
->dst_mac
, hdr
->ether_dhost
.ether_addr_octet
, sizeof(sm
->dst_mac
));
151 memcpy(sm
->src_mac
, hdr
->ether_shost
, sizeof(sm
->src_mac
));
152 memcpy(sm
->dst_mac
, hdr
->ether_dhost
, sizeof(sm
->dst_mac
));
154 type
= ntohs(hdr
->ether_type
);
159 return helper_ip(pdata
+ ETHER_HDR_LEN
,
160 pheader
->caplen
- ETHER_HDR_LEN
,
162 verbosef("ether: discarded IP packet, expecting PPPoE instead");
164 case ETHERTYPE_PPPOE
:
166 return helper_pppoe(pdata
+ ETHER_HDR_LEN
,
167 pheader
->caplen
- ETHER_HDR_LEN
,
169 verbosef("ether: got PPPoE frame: maybe you want --pppoe");
172 /* known protocol, don't complain about it. */
175 verbosef("ether: unknown protocol (0x%04x)", type
);
180 /* Very similar to decode_null, except on OpenBSD we need to think
181 * about family endianness.
183 static int decode_loop(DECODER_ARGS
) {
186 if (pheader
->caplen
< NULL_HDR_LEN
) {
187 verbosef("loop: packet too short (%u bytes)", pheader
->caplen
);
190 family
= *(const uint32_t *)pdata
;
192 family
= ntohl(family
);
194 if (family
== AF_INET
)
195 return helper_ip(pdata
+ NULL_HDR_LEN
,
196 pheader
->caplen
- NULL_HDR_LEN
, sm
);
197 if (family
== AF_INET6
)
198 return helper_ipv6(pdata
+ NULL_HDR_LEN
,
199 pheader
->caplen
- NULL_HDR_LEN
, sm
);
200 verbosef("loop: unknown family (0x%04x)", family
);
204 static int decode_null(DECODER_ARGS
) {
207 if (pheader
->caplen
< NULL_HDR_LEN
) {
208 verbosef("null: packet too short (%u bytes)", pheader
->caplen
);
211 family
= *(const uint32_t *)pdata
;
212 if (family
== AF_INET
)
213 return helper_ip(pdata
+ NULL_HDR_LEN
,
214 pheader
->caplen
- NULL_HDR_LEN
,
216 if (family
== AF_INET6
)
217 return helper_ipv6(pdata
+ NULL_HDR_LEN
,
218 pheader
->caplen
- NULL_HDR_LEN
,
220 verbosef("null: unknown family (0x%04x)", family
);
224 static int decode_ppp(DECODER_ARGS
) {
225 if (pheader
->caplen
< PPPOE_HDR_LEN
) {
226 verbosef("ppp: packet too short (%u bytes)", pheader
->caplen
);
229 if (pdata
[2] == 0x00 && pdata
[3] == 0x21)
230 return helper_ip(pdata
+ PPP_HDR_LEN
,
231 pheader
->caplen
- PPP_HDR_LEN
,
233 verbosef("ppp: non-IP PPP packet; ignoring.");
237 static int decode_pppoe(DECODER_ARGS
) {
238 return helper_pppoe(pdata
, pheader
->caplen
, sm
);
242 /* very similar to decode_ether ... */
243 static int decode_linux_sll(DECODER_ARGS
) {
244 const struct sll_header
{
245 uint16_t packet_type
;
246 uint16_t device_type
;
247 uint16_t addr_length
;
248 #define SLL_MAX_ADDRLEN 8
249 uint8_t addr
[SLL_MAX_ADDRLEN
];
251 } *hdr
= (const struct sll_header
*)pdata
;
254 if (pheader
->caplen
< SLL_HDR_LEN
) {
255 verbosef("linux_sll: packet too short (%u bytes)", pheader
->caplen
);
258 type
= ntohs(hdr
->ether_type
);
262 return helper_ip(pdata
+ SLL_HDR_LEN
,
263 pheader
->caplen
- SLL_HDR_LEN
,
266 /* known protocol, don't complain about it. */
269 verbosef("linux_sll: unknown protocol (0x%04x)", type
);
273 #endif /* DLT_LINUX_SLL */
275 static int decode_raw(DECODER_ARGS
) {
276 return helper_ip(pdata
, pheader
->caplen
, sm
);
279 static int helper_pppoe(HELPER_ARGS
) {
280 if (len
< PPPOE_HDR_LEN
) {
281 verbosef("pppoe: packet too short (%u bytes)", len
);
285 if (pdata
[1] != 0x00) {
286 verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata
[1]);
290 if ((pdata
[6] == 0xc0) && (pdata
[7] == 0x21)) return 0; /* LCP */
291 if ((pdata
[6] == 0xc0) && (pdata
[7] == 0x25)) return 0; /* LQR */
293 if ((pdata
[6] == 0x00) && (pdata
[7] == 0x21))
294 return helper_ip(pdata
+ PPPOE_HDR_LEN
, len
- PPPOE_HDR_LEN
, sm
);
296 verbosef("pppoe: ignoring non-IP PPPoE packet (0x%02x%02x)",
301 static int helper_ip(HELPER_ARGS
) {
302 const struct ip
*hdr
= (const struct ip
*)pdata
;
304 if (len
< IP_HDR_LEN
) {
305 verbosef("ip: packet too short (%u bytes)", len
);
308 if (hdr
->ip_v
== 6) {
309 return helper_ipv6(pdata
, len
, sm
);
311 if (hdr
->ip_v
!= 4) {
312 verbosef("ip: version %d (expecting 4 or 6)", hdr
->ip_v
);
316 sm
->len
= ntohs(hdr
->ip_len
);
317 sm
->proto
= hdr
->ip_p
;
319 sm
->src
.family
= IPv4
;
320 sm
->src
.ip
.v4
= hdr
->ip_src
.s_addr
;
322 sm
->dst
.family
= IPv4
;
323 sm
->dst
.ip
.v4
= hdr
->ip_dst
.s_addr
;
325 helper_ip_deeper(pdata
+ IP_HDR_LEN
, len
- IP_HDR_LEN
, sm
);
329 static int helper_ipv6(HELPER_ARGS
) {
330 const struct ip6_hdr
*hdr
= (const struct ip6_hdr
*)pdata
;
332 if (len
< IPV6_HDR_LEN
) {
333 verbosef("ipv6: packet too short (%u bytes)", len
);
336 if ((hdr
->ip6_vfc
& IPV6_VERSION_MASK
) != IPV6_VERSION
) {
337 verbosef("ipv6: bad version (%02x, expecting %02x)",
338 hdr
->ip6_vfc
& IPV6_VERSION_MASK
, IPV6_VERSION
);
342 /* IPv4 has "total length," but IPv6 has "payload length" which doesn't
343 * count the header bytes.
345 sm
->len
= ntohs(hdr
->ip6_plen
) + IPV6_HDR_LEN
;
346 sm
->proto
= hdr
->ip6_nxt
;
347 sm
->src
.family
= IPv6
;
348 memcpy(&sm
->src
.ip
.v6
, &hdr
->ip6_src
, sizeof(sm
->src
.ip
.v6
));
349 sm
->dst
.family
= IPv6
;
350 memcpy(&sm
->dst
.ip
.v6
, &hdr
->ip6_dst
, sizeof(sm
->dst
.ip
.v6
));
352 helper_ip_deeper(pdata
+ IPV6_HDR_LEN
, len
- IPV6_HDR_LEN
, sm
);
356 static void helper_ip_deeper(HELPER_ARGS
) {
357 /* At this stage we have IP addresses so we can do host accounting.
359 * If proto decode fails, we set IPPROTO_INVALID to skip accounting of port
362 * We don't need to "return 0" like other helpers.
366 const struct tcphdr
*thdr
= (const struct tcphdr
*)pdata
;
367 if (len
< TCP_HDR_LEN
) {
368 verbosef("tcp: packet too short (%u bytes)", len
);
369 sm
->proto
= IPPROTO_INVALID
; /* don't do accounting! */
372 sm
->src_port
= ntohs(thdr
->th_sport
);
373 sm
->dst_port
= ntohs(thdr
->th_dport
);
374 sm
->tcp_flags
= thdr
->th_flags
&
375 (TH_FIN
|TH_SYN
|TH_RST
|TH_PUSH
|TH_ACK
|TH_URG
);
380 const struct udphdr
*uhdr
= (const struct udphdr
*)pdata
;
381 if (len
< UDP_HDR_LEN
) {
382 verbosef("udp: packet too short (%u bytes)", len
);
383 sm
->proto
= IPPROTO_INVALID
; /* don't do accounting! */
386 sm
->src_port
= ntohs(uhdr
->uh_sport
);
387 sm
->dst_port
= ntohs(uhdr
->uh_dport
);
393 /* vim:set ts=3 sw=3 tw=78 expandtab: */