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