Conditional replacement of ETHERTYPE_IPV6.
[darkstat] / decode.c
1 /* darkstat 3
2 * copyright (c) 2001-2009 Emil Mikulic.
3 *
4 * decode.c: packet decoding.
5 *
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
8 *
9 * You may use, modify and redistribute this file under the terms of the
10 * GNU General Public License version 2. (see COPYING.GPL)
11 */
12
13 #include "darkstat.h"
14 #include "acct.h"
15 #include "cap.h"
16
17 #include <sys/ioctl.h>
18 #include <sys/socket.h>
19 #include <assert.h>
20 #include "err.h"
21 #include <pcap.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <arpa/inet.h> /* inet_ntoa() */
25
26 /* need struct ether_header */
27 #if defined(__NetBSD__) || defined(__OpenBSD__)
28 # include <sys/queue.h>
29 # include <net/if.h>
30 # include <net/if_arp.h>
31 # include <netinet/if_ether.h>
32 #else
33 # ifdef __sun
34 # include <sys/ethernet.h>
35 # define ETHER_HDR_LEN 14
36 # else
37 # ifdef _AIX
38 # include <netinet/if_ether.h>
39 # define ETHER_HDR_LEN 14
40 # else
41 # include <net/ethernet.h>
42 # endif
43 # endif
44 #endif
45 #ifndef ETHERTYPE_PPPOE
46 #define ETHERTYPE_PPPOE 0x8864
47 #endif
48
49 #ifndef ETHERTYPE_IPV6
50 # ifdef HAVE_NET_IF_ETHER_H
51 # include <net/if_ether.h> /* ETH_P_IPV6 for GNU/kfreebsd */
52 # endif
53 # ifdef ETH_P_IPV6
54 # define ETHERTYPE_IPV6 ETH_P_IPV6
55 # endif
56 #endif
57
58 #include <net/if.h> /* struct ifreq */
59 #include <netinet/in_systm.h> /* n_long */
60 #include <netinet/ip.h> /* struct ip */
61 #include <netinet/ip6.h> /* struct ip6_hdr */
62 #define __FAVOR_BSD
63 #include <netinet/tcp.h> /* struct tcphdr */
64 #include <netinet/udp.h> /* struct udphdr */
65
66 extern int want_pppoe;
67
68 static void decode_ether(u_char *, const struct pcap_pkthdr *,
69 const u_char *);
70 static void decode_loop(u_char *, const struct pcap_pkthdr *,
71 const u_char *);
72 static void decode_ppp(u_char *, const struct pcap_pkthdr *,
73 const u_char *);
74 static void decode_pppoe(u_char *, const struct pcap_pkthdr *,
75 const u_char *);
76 static void decode_pppoe_real(const u_char *pdata, const uint32_t len,
77 pktsummary *sm);
78 static void decode_linux_sll(u_char *, const struct pcap_pkthdr *,
79 const u_char *);
80 static void decode_raw(u_char *, const struct pcap_pkthdr *,
81 const u_char *);
82 static void decode_ip(const u_char *pdata, const uint32_t len,
83 pktsummary *sm);
84 static void decode_ipv6(const u_char *pdata, const uint32_t len,
85 pktsummary *sm);
86
87 /* Link-type header information */
88 static const linkhdr_t linkhdrs[] = {
89 /* linktype hdrlen handler */
90 { DLT_EN10MB, ETHER_HDR_LEN, decode_ether },
91 { DLT_LOOP, NULL_HDR_LEN, decode_loop },
92 { DLT_NULL, NULL_HDR_LEN, decode_loop },
93 { DLT_PPP, PPP_HDR_LEN, decode_ppp },
94 #if defined(__NetBSD__)
95 { DLT_PPP_SERIAL, PPP_HDR_LEN, decode_ppp },
96 #endif
97 { DLT_FDDI, FDDI_HDR_LEN, NULL },
98 { DLT_PPP_ETHER, PPPOE_HDR_LEN, decode_pppoe },
99 #ifdef DLT_LINUX_SLL
100 { DLT_LINUX_SLL, SLL_HDR_LEN, decode_linux_sll },
101 #endif
102 { DLT_RAW, RAW_HDR_LEN, decode_raw },
103 { -1, -1, NULL }
104 };
105
106 /*
107 * Returns a pointer to the linkhdr_t record matching the given linktype, or
108 * NULL if no matching entry found.
109 */
110 const linkhdr_t *
111 getlinkhdr(int linktype)
112 {
113 int i;
114
115 for (i=0; linkhdrs[i].linktype != -1; i++)
116 if (linkhdrs[i].linktype == linktype)
117 return (&(linkhdrs[i]));
118 return (NULL);
119 }
120
121 /*
122 * Returns the minimum snaplen needed to decode everything up to the TCP/UDP
123 * packet headers. Argument lh is not allowed to be NULL.
124 */
125 int
126 getsnaplen(const linkhdr_t *lh)
127 {
128 assert(lh != NULL);
129 return (lh->hdrlen + IP_HDR_LEN + max(TCP_HDR_LEN, UDP_HDR_LEN));
130 }
131
132 /*
133 * Convert IP address to a numbers-and-dots notation in a static buffer
134 * provided by inet_ntoa().
135 */
136 char *
137 ip_to_str(const in_addr_t ip)
138 {
139 struct in_addr in;
140
141 in.s_addr = ip;
142 return (inet_ntoa(in));
143 }
144
145 char ip6str[INET6_ADDRSTRLEN];
146
147 char *
148 ip6_to_str(const struct in6_addr *ip6)
149 {
150 ip6str[0] = '\0';
151 inet_ntop(AF_INET6, ip6, ip6str, sizeof(ip6str));
152
153 return (ip6str);
154 }
155
156 /* Decoding functions. */
157 static void
158 decode_ether(u_char *user _unused_,
159 const struct pcap_pkthdr *pheader,
160 const u_char *pdata)
161 {
162 u_short type;
163 const struct ether_header *hdr = (const struct ether_header *)pdata;
164 pktsummary sm;
165 memset(&sm, 0, sizeof(sm));
166 sm.time = pheader->ts.tv_sec;
167
168 if (pheader->caplen < ETHER_HDR_LEN) {
169 verbosef("ether: packet too short (%u bytes)", pheader->caplen);
170 return;
171 }
172
173 #ifdef __sun
174 memcpy(sm.src_mac, hdr->ether_shost.ether_addr_octet, sizeof(sm.src_mac));
175 memcpy(sm.dst_mac, hdr->ether_dhost.ether_addr_octet, sizeof(sm.dst_mac));
176 #else
177 memcpy(sm.src_mac, hdr->ether_shost, sizeof(sm.src_mac));
178 memcpy(sm.dst_mac, hdr->ether_dhost, sizeof(sm.dst_mac));
179 #endif
180
181 type = ntohs( hdr->ether_type );
182 switch (type) {
183 case ETHERTYPE_IP:
184 case ETHERTYPE_IPV6:
185 if (!want_pppoe) {
186 decode_ip(pdata + ETHER_HDR_LEN,
187 pheader->caplen - ETHER_HDR_LEN, &sm);
188 acct_for(&sm);
189 } else
190 verbosef("ether: discarded IP packet, expecting PPPoE instead");
191 break;
192 case ETHERTYPE_ARP:
193 /* known protocol, don't complain about it. */
194 break;
195 case ETHERTYPE_PPPOE:
196 if (want_pppoe)
197 decode_pppoe_real(pdata + ETHER_HDR_LEN,
198 pheader->caplen - ETHER_HDR_LEN, &sm);
199 else
200 verbosef("ether: got PPPoE frame: maybe you want --pppoe");
201 break;
202 default:
203 verbosef("ether: unknown protocol (0x%04x)", type);
204 }
205 }
206
207 static void
208 decode_loop(u_char *user _unused_,
209 const struct pcap_pkthdr *pheader,
210 const u_char *pdata)
211 {
212 uint32_t family;
213 pktsummary sm;
214 memset(&sm, 0, sizeof(sm));
215
216 if (pheader->caplen < NULL_HDR_LEN) {
217 verbosef("loop: packet too short (%u bytes)", pheader->caplen);
218 return;
219 }
220 family = *(const uint32_t *)pdata;
221 #ifdef __OpenBSD__
222 family = ntohl(family);
223 #endif
224 if (family == AF_INET) {
225 /* OpenBSD tun or FreeBSD tun or FreeBSD lo */
226 decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
227 sm.time = pheader->ts.tv_sec;
228 acct_for(&sm);
229 }
230 else if (family == AF_INET6) {
231 /* XXX: Check this! */
232 decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
233 sm.time = pheader->ts.tv_sec;
234 acct_for(&sm);
235 }
236 else
237 verbosef("loop: unknown family (%x)", family);
238 }
239
240 static void
241 decode_ppp(u_char *user _unused_,
242 const struct pcap_pkthdr *pheader,
243 const u_char *pdata)
244 {
245 pktsummary sm;
246 memset(&sm, 0, sizeof(sm));
247
248 if (pheader->caplen < PPPOE_HDR_LEN) {
249 verbosef("ppp: packet too short (%u bytes)", pheader->caplen);
250 return;
251 }
252
253 if (pdata[2] == 0x00 && pdata[3] == 0x21) {
254 decode_ip(pdata + PPP_HDR_LEN, pheader->caplen - PPP_HDR_LEN, &sm);
255 sm.time = pheader->ts.tv_sec;
256 acct_for(&sm);
257 } else
258 verbosef("non-IP PPP packet; ignoring.");
259 }
260
261 static void
262 decode_pppoe(u_char *user _unused_,
263 const struct pcap_pkthdr *pheader,
264 const u_char *pdata)
265 {
266 pktsummary sm;
267 memset(&sm, 0, sizeof(sm));
268 sm.time = pheader->ts.tv_sec;
269 decode_pppoe_real(pdata, pheader->caplen, &sm);
270 }
271
272 static void
273 decode_pppoe_real(const u_char *pdata, const uint32_t len,
274 pktsummary *sm)
275 {
276 if (len < PPPOE_HDR_LEN) {
277 verbosef("pppoe: packet too short (%u bytes)", len);
278 return;
279 }
280
281 if (pdata[1] != 0x00) {
282 verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]);
283 return;
284 }
285
286 if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return; /* LCP */
287 if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return; /* LQR */
288
289 if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) {
290 decode_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm);
291 acct_for(sm);
292 } else
293 verbosef("pppoe: non-IP PPPoE packet (0x%02x%02x); ignoring.",
294 pdata[6], pdata[7]);
295 }
296
297 /* very similar to decode_ether ... */
298 static void
299 decode_linux_sll(u_char *user _unused_,
300 const struct pcap_pkthdr *pheader,
301 const u_char *pdata)
302 {
303 const struct sll_header {
304 uint16_t packet_type;
305 uint16_t device_type;
306 uint16_t addr_length;
307 #define SLL_MAX_ADDRLEN 8
308 uint8_t addr[SLL_MAX_ADDRLEN];
309 uint16_t ether_type;
310 } *hdr = (const struct sll_header *)pdata;
311 u_short type;
312 pktsummary sm;
313 memset(&sm, 0, sizeof(sm));
314
315 if (pheader->caplen < SLL_HDR_LEN) {
316 verbosef("linux_sll: packet too short (%u bytes)", pheader->caplen);
317 return;
318 }
319
320 type = ntohs( hdr->ether_type );
321 switch (type) {
322 case ETHERTYPE_IP:
323 case ETHERTYPE_IPV6:
324 decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm);
325 sm.time = pheader->ts.tv_sec;
326 acct_for(&sm);
327 break;
328 case ETHERTYPE_ARP:
329 /* known protocol, don't complain about it. */
330 break;
331 default:
332 verbosef("linux_sll: unknown protocol (%04x)", type);
333 }
334 }
335
336 static void
337 decode_raw(u_char *user _unused_,
338 const struct pcap_pkthdr *pheader,
339 const u_char *pdata)
340 {
341 pktsummary sm;
342 memset(&sm, 0, sizeof(sm));
343
344 decode_ip(pdata, pheader->caplen, &sm);
345 sm.time = pheader->ts.tv_sec;
346 acct_for(&sm);
347 }
348
349 static void
350 decode_ip(const u_char *pdata, const uint32_t len, pktsummary *sm)
351 {
352 const struct ip *hdr = (const struct ip *)pdata;
353
354 if (hdr->ip_v == 6) {
355 /* Redirect parsing of IPv6 packets. */
356 decode_ipv6(pdata, len, sm);
357 return;
358 }
359 if (len < IP_HDR_LEN) {
360 verbosef("ip: packet too short (%u bytes)", len);
361 return;
362 }
363 if (hdr->ip_v != 4) {
364 verbosef("ip: version %d (expecting 4)", hdr->ip_v);
365 return;
366 }
367
368 sm->len = ntohs(hdr->ip_len);
369 sm->af = AF_INET;
370 sm->proto = hdr->ip_p;
371 sm->src_ip = hdr->ip_src.s_addr;
372 sm->dest_ip = hdr->ip_dst.s_addr;
373
374 switch (sm->proto) {
375 case IPPROTO_TCP: {
376 const struct tcphdr *thdr =
377 (const struct tcphdr *)(pdata + IP_HDR_LEN);
378 if (len < IP_HDR_LEN + TCP_HDR_LEN) {
379 verbosef("tcp: packet too short (%u bytes)", len);
380 return;
381 }
382 sm->src_port = ntohs(thdr->th_sport);
383 sm->dest_port = ntohs(thdr->th_dport);
384 sm->tcp_flags = thdr->th_flags &
385 (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG);
386 break;
387 }
388
389 case IPPROTO_UDP: {
390 const struct udphdr *uhdr =
391 (const struct udphdr *)(pdata + IP_HDR_LEN);
392 if (len < IP_HDR_LEN + UDP_HDR_LEN) {
393 verbosef("udp: packet too short (%u bytes)", len);
394 return;
395 }
396 sm->src_port = ntohs(uhdr->uh_sport);
397 sm->dest_port = ntohs(uhdr->uh_dport);
398 break;
399 }
400
401 case IPPROTO_ICMP:
402 /* known protocol, don't complain about it */
403 break;
404
405 default:
406 verbosef("ip: unknown protocol %d", sm->proto);
407 }
408 }
409
410 static void
411 decode_ipv6(const u_char *pdata, const uint32_t len, pktsummary *sm)
412 {
413 const struct ip6_hdr *hdr = (const struct ip6_hdr *)pdata;
414
415 if (len < IPV6_HDR_LEN) {
416 verbosef("ipv6: packet too short (%u bytes)", len);
417 return;
418 }
419
420 sm->len = ntohs(hdr->ip6_plen) + IPV6_HDR_LEN;
421 sm->af = AF_INET6;
422 sm->proto = hdr->ip6_nxt;
423 memcpy(&sm->src_ip6, &hdr->ip6_src, sizeof(sm->src_ip6));
424 memcpy(&sm->dest_ip6, &hdr->ip6_dst, sizeof(sm->dest_ip6));
425
426 switch (sm->proto) {
427 case IPPROTO_TCP: {
428 const struct tcphdr *thdr =
429 (const struct tcphdr *)(pdata + IPV6_HDR_LEN);
430 if (len < IPV6_HDR_LEN + TCP_HDR_LEN) {
431 verbosef("tcp6: packet too short (%u bytes)", len);
432 return;
433 }
434 sm->src_port = ntohs(thdr->th_sport);
435 sm->dest_port = ntohs(thdr->th_dport);
436 sm->tcp_flags = thdr->th_flags &
437 (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG);
438 break;
439 }
440
441 case IPPROTO_UDP: {
442 const struct udphdr *uhdr =
443 (const struct udphdr *)(pdata + IPV6_HDR_LEN);
444 if (len < IPV6_HDR_LEN + UDP_HDR_LEN) {
445 verbosef("udp6: packet too short (%u bytes)", len);
446 return;
447 }
448 sm->src_port = ntohs(uhdr->uh_sport);
449 sm->dest_port = ntohs(uhdr->uh_dport);
450 break;
451 }
452
453 case IPPROTO_ICMPV6:
454 /* known protocol, don't complain about it */
455 break;
456
457 default:
458 verbosef("ipv6: unknown protocol %d", sm->proto);
459 }
460 }
461
462 /* vim:set ts=3 sw=3 tw=78 expandtab: */