Import darkstat 3.0.712
[darkstat] / decode.c
1 /* darkstat 3
2 * copyright (c) 2001-2007 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 #define __FAVOR_BSD
53 #include <netinet/tcp.h> /* struct tcphdr */
54 #include <netinet/udp.h> /* struct udphdr */
55
56 extern int want_pppoe;
57
58 static void decode_ether(u_char *, const struct pcap_pkthdr *,
59 const u_char *);
60 static void decode_loop(u_char *, const struct pcap_pkthdr *,
61 const u_char *);
62 static void decode_ppp(u_char *, const struct pcap_pkthdr *,
63 const u_char *);
64 static void decode_pppoe(u_char *, const struct pcap_pkthdr *,
65 const u_char *);
66 static void decode_pppoe_real(const u_char *pdata, const uint32_t len,
67 pktsummary *sm);
68 static void decode_linux_sll(u_char *, const struct pcap_pkthdr *,
69 const u_char *);
70 static void decode_ip(const u_char *pdata, const uint32_t len,
71 pktsummary *sm);
72
73 /* Link-type header information */
74 static const linkhdr_t linkhdrs[] = {
75 /* linktype hdrlen handler */
76 { DLT_EN10MB, ETHER_HDR_LEN, decode_ether },
77 { DLT_LOOP, NULL_HDR_LEN, decode_loop },
78 { DLT_NULL, NULL_HDR_LEN, decode_loop },
79 { DLT_PPP, PPP_HDR_LEN, decode_ppp },
80 #if defined(__NetBSD__)
81 { DLT_PPP_SERIAL, PPP_HDR_LEN, decode_ppp },
82 #endif
83 { DLT_FDDI, FDDI_HDR_LEN, NULL },
84 { DLT_PPP_ETHER, PPPOE_HDR_LEN, decode_pppoe },
85 #ifdef DLT_LINUX_SLL
86 { DLT_LINUX_SLL, SLL_HDR_LEN, decode_linux_sll },
87 #endif
88 { -1, -1, NULL }
89 };
90
91 /*
92 * Returns a pointer to the linkhdr_t record matching the given linktype, or
93 * NULL if no matching entry found.
94 */
95 const linkhdr_t *
96 getlinkhdr(int linktype)
97 {
98 int i;
99
100 for (i=0; linkhdrs[i].linktype != -1; i++)
101 if (linkhdrs[i].linktype == linktype)
102 return (&(linkhdrs[i]));
103 return (NULL);
104 }
105
106 /*
107 * Returns the minimum caplen needed to decode everything up to the TCP/UDP
108 * packet headers. Argument lh is not allowed to be NULL.
109 */
110 int
111 getcaplen(const linkhdr_t *lh)
112 {
113 assert(lh != NULL);
114 return (lh->hdrlen + IP_HDR_LEN + max(TCP_HDR_LEN, UDP_HDR_LEN));
115 }
116
117 /*
118 * Convert IP address to a numbers-and-dots notation in a static buffer
119 * provided by inet_ntoa().
120 */
121 char *
122 ip_to_str(const in_addr_t ip)
123 {
124 struct in_addr in;
125
126 in.s_addr = ip;
127 return (inet_ntoa(in));
128 }
129
130 /* Decoding functions. */
131 static void
132 decode_ether(u_char *user _unused_,
133 const struct pcap_pkthdr *pheader,
134 const u_char *pdata)
135 {
136 u_short type;
137 const struct ether_header *hdr = (const struct ether_header *)pdata;
138 pktsummary sm;
139 memset(&sm, 0, sizeof(sm));
140 sm.time = pheader->ts.tv_sec;
141
142 if (pheader->caplen < ETHER_HDR_LEN) {
143 verbosef("ether: packet too short (%u bytes)", pheader->caplen);
144 return;
145 }
146
147 #ifdef __sun
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));
150 #else
151 memcpy(sm.src_mac, hdr->ether_shost, sizeof(sm.src_mac));
152 memcpy(sm.dst_mac, hdr->ether_dhost, sizeof(sm.dst_mac));
153 #endif
154
155 type = ntohs( hdr->ether_type );
156 switch (type) {
157 case ETHERTYPE_IP:
158 if (!want_pppoe) {
159 decode_ip(pdata + ETHER_HDR_LEN,
160 pheader->caplen - ETHER_HDR_LEN, &sm);
161 acct_for(&sm);
162 } else
163 verbosef("ether: discarded IP packet, expecting PPPoE instead");
164 break;
165 case ETHERTYPE_ARP:
166 /* known protocol, don't complain about it. */
167 break;
168 case ETHERTYPE_PPPOE:
169 if (want_pppoe)
170 decode_pppoe_real(pdata + ETHER_HDR_LEN,
171 pheader->caplen - ETHER_HDR_LEN, &sm);
172 else
173 verbosef("ether: got PPPoE frame: maybe you want --pppoe");
174 break;
175 default:
176 verbosef("ether: unknown protocol (0x%04x)", type);
177 }
178 }
179
180 static void
181 decode_loop(u_char *user _unused_,
182 const struct pcap_pkthdr *pheader,
183 const u_char *pdata)
184 {
185 uint32_t family;
186 pktsummary sm;
187 memset(&sm, 0, sizeof(sm));
188
189 if (pheader->caplen < NULL_HDR_LEN) {
190 verbosef("loop: packet too short (%u bytes)", pheader->caplen);
191 return;
192 }
193 family = *(const uint32_t *)pdata;
194 #ifdef __OpenBSD__
195 family = ntohl(family);
196 #endif
197 if (family == AF_INET) {
198 /* OpenBSD tun or FreeBSD tun or FreeBSD lo */
199 decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
200 sm.time = pheader->ts.tv_sec;
201 acct_for(&sm);
202 }
203 else
204 verbosef("loop: unknown family (%x)", family);
205 }
206
207 static void
208 decode_ppp(u_char *user _unused_,
209 const struct pcap_pkthdr *pheader,
210 const u_char *pdata)
211 {
212 pktsummary sm;
213 memset(&sm, 0, sizeof(sm));
214
215 if (pheader->caplen < PPPOE_HDR_LEN) {
216 verbosef("ppp: packet too short (%u bytes)", pheader->caplen);
217 return;
218 }
219
220 if (pdata[2] == 0x00 && pdata[3] == 0x21) {
221 decode_ip(pdata + PPP_HDR_LEN, pheader->caplen - PPP_HDR_LEN, &sm);
222 sm.time = pheader->ts.tv_sec;
223 acct_for(&sm);
224 } else
225 verbosef("non-IP PPP packet; ignoring.");
226 }
227
228 static void
229 decode_pppoe(u_char *user _unused_,
230 const struct pcap_pkthdr *pheader,
231 const u_char *pdata)
232 {
233 pktsummary sm;
234 memset(&sm, 0, sizeof(sm));
235 sm.time = pheader->ts.tv_sec;
236 decode_pppoe_real(pdata, pheader->caplen, &sm);
237 }
238
239 static void
240 decode_pppoe_real(const u_char *pdata, const uint32_t len,
241 pktsummary *sm)
242 {
243 if (len < PPPOE_HDR_LEN) {
244 verbosef("pppoe: packet too short (%u bytes)", len);
245 return;
246 }
247
248 if (pdata[1] != 0x00) {
249 verbosef("pppoe: code = 0x%02x, expecting 0; ignoring.", pdata[1]);
250 return;
251 }
252
253 if ((pdata[6] == 0xc0) && (pdata[7] == 0x21)) return; /* LCP */
254 if ((pdata[6] == 0xc0) && (pdata[7] == 0x25)) return; /* LQR */
255
256 if ((pdata[6] == 0x00) && (pdata[7] == 0x21)) {
257 decode_ip(pdata + PPPOE_HDR_LEN, len - PPPOE_HDR_LEN, sm);
258 acct_for(sm);
259 } else
260 verbosef("pppoe: non-IP PPPoE packet (0x%02x%02x); ignoring.",
261 pdata[6], pdata[7]);
262 }
263
264 /* very similar to decode_ether ... */
265 static void
266 decode_linux_sll(u_char *user _unused_,
267 const struct pcap_pkthdr *pheader,
268 const u_char *pdata)
269 {
270 const struct sll_header {
271 uint16_t packet_type;
272 uint16_t device_type;
273 uint16_t addr_length;
274 #define SLL_MAX_ADDRLEN 8
275 uint8_t addr[SLL_MAX_ADDRLEN];
276 uint16_t ether_type;
277 } *hdr = (const struct sll_header *)pdata;
278 u_short type;
279 pktsummary sm;
280 memset(&sm, 0, sizeof(sm));
281
282 if (pheader->caplen < SLL_HDR_LEN) {
283 verbosef("linux_sll: packet too short (%u bytes)", pheader->caplen);
284 return;
285 }
286
287 type = ntohs( hdr->ether_type );
288 switch (type) {
289 case ETHERTYPE_IP:
290 decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm);
291 sm.time = pheader->ts.tv_sec;
292 acct_for(&sm);
293 break;
294 case ETHERTYPE_ARP:
295 /* known protocol, don't complain about it. */
296 break;
297 default:
298 verbosef("linux_sll: unknown protocol (%04x)", type);
299 }
300 }
301
302 static void
303 decode_ip(const u_char *pdata, const uint32_t len, pktsummary *sm)
304 {
305 const struct ip *hdr = (const struct ip *)pdata;
306
307 if (len < IP_HDR_LEN) {
308 verbosef("ip: packet too short (%u bytes)", len);
309 return;
310 }
311 if (hdr->ip_v != 4) {
312 verbosef("ip: version %d (expecting 4)", hdr->ip_v);
313 return;
314 }
315
316 sm->len = ntohs(hdr->ip_len);
317 sm->proto = hdr->ip_p;
318 sm->src_ip = hdr->ip_src.s_addr;
319 sm->dest_ip = hdr->ip_dst.s_addr;
320
321 switch (sm->proto) {
322 case IPPROTO_TCP: {
323 const struct tcphdr *thdr =
324 (const struct tcphdr *)(pdata + IP_HDR_LEN);
325 if (len < IP_HDR_LEN + TCP_HDR_LEN) {
326 verbosef("tcp: packet too short (%u bytes)", len);
327 return;
328 }
329 sm->src_port = ntohs(thdr->th_sport);
330 sm->dest_port = ntohs(thdr->th_dport);
331 sm->tcp_flags = thdr->th_flags &
332 (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG);
333 break;
334 }
335
336 case IPPROTO_UDP: {
337 const struct udphdr *uhdr =
338 (const struct udphdr *)(pdata + IP_HDR_LEN);
339 if (len < IP_HDR_LEN + UDP_HDR_LEN) {
340 verbosef("udp: packet too short (%u bytes)", len);
341 return;
342 }
343 sm->src_port = ntohs(uhdr->uh_sport);
344 sm->dest_port = ntohs(uhdr->uh_dport);
345 break;
346 }
347
348 case IPPROTO_ICMP:
349 /* known protocol, don't complain about it */
350 break;
351
352 default:
353 verbosef("ip: unknown protocol %d", sm->proto);
354 }
355 }
356
357 /* vim:set ts=3 sw=3 tw=78 expandtab: */