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