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