1 /*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 of the
4 * License as published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14 */
15
16 /**
17 * $Id: 987a8ea3f049a843136905b1e2b4c82b26d163ee $
18 * @file pcap.c
19 * @brief Wrappers around libpcap functions
20 *
21 * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 */
24 #ifdef HAVE_LIBPCAP
25
26 #include <sys/ioctl.h>
27 #include <freeradius-devel/pcap.h>
28
29 const FR_NAME_NUMBER pcap_types[] = {
30 { "interface", PCAP_INTERFACE_IN },
31 { "file", PCAP_FILE_IN },
32 { "stdio", PCAP_STDIO_IN },
33 { "interface", PCAP_INTERFACE_OUT },
34 { "file", PCAP_FILE_OUT },
35 { "stdio", PCAP_STDIO_OUT },
36
37 { NULL, 0}
38 };
39
40 /** Talloc destructor to free pcap resources associated with a handle.
41 *
42 * @param pcap to free.
43 * @return 0
44 */
_free_pcap(fr_pcap_t * pcap)45 static int _free_pcap(fr_pcap_t *pcap) {
46 switch (pcap->type) {
47 case PCAP_INTERFACE_IN:
48 case PCAP_INTERFACE_OUT:
49 case PCAP_FILE_IN:
50 case PCAP_STDIO_IN:
51 if (pcap->handle) {
52 pcap_close(pcap->handle);
53
54 if (pcap->fd > 0) {
55 close(pcap->fd);
56 }
57 }
58
59 break;
60
61 case PCAP_FILE_OUT:
62 case PCAP_STDIO_OUT:
63 if (pcap->dumper) {
64 pcap_dump_flush(pcap->dumper);
65 pcap_dump_close(pcap->dumper);
66 }
67
68 break;
69 case PCAP_INVALID:
70 break;
71 }
72
73 return 0;
74 }
75
76 /** Get data link from pcap_if_t
77 *
78 * libpcap requires an open pcap handle to get data_link type
79 * unfortunately when we're trying to find useful interfaces
80 * this is too late.
81 *
82 * @param errbuff Error message.
83 * @param dev to get link layer for.
84 * @return datalink layer or -1 on failure.
85 */
fr_pcap_if_link_layer(char * errbuff,pcap_if_t * dev)86 int fr_pcap_if_link_layer(char *errbuff, pcap_if_t *dev)
87 {
88 pcap_t *pcap;
89 int data_link;
90
91 pcap = pcap_open_live(dev->name, 0, 0, 0, errbuff);
92 if (!pcap) return -1;
93
94 data_link = pcap_datalink(pcap);
95 pcap_close(pcap);
96
97 return data_link;
98 }
99
100 /** Initialise a pcap handle abstraction
101 *
102 * @param ctx talloc TALLOC_CTX to allocate handle in.
103 * @param name of interface or file to open.
104 * @param type of handle to initialise.
105 * @return new handle or NULL on error.
106 */
fr_pcap_init(TALLOC_CTX * ctx,char const * name,fr_pcap_type_t type)107 fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type)
108 {
109 fr_pcap_t *this = talloc_zero(ctx, fr_pcap_t);
110 if (!this) {
111 return NULL;
112 }
113
114 talloc_set_destructor(this, _free_pcap);
115 this->name = talloc_typed_strdup(this, name);
116 this->type = type;
117 this->link_layer = -1;
118
119 return this;
120 }
121
122 /** Open a PCAP handle abstraction
123 *
124 * This opens interfaces for capture or injection, or files/streams for reading/writing.
125 * @param pcap created with fr_pcap_init.
126 * @return 0 on success, -1 on error.
127 */
fr_pcap_open(fr_pcap_t * pcap)128 int fr_pcap_open(fr_pcap_t *pcap)
129 {
130 switch (pcap->type) {
131 case PCAP_INTERFACE_OUT:
132 case PCAP_INTERFACE_IN:
133 {
134 #if defined(HAVE_PCAP_CREATE) && defined(HAVE_PCAP_ACTIVATE)
135 pcap->handle = pcap_create(pcap->name, pcap->errbuf);
136 if (!pcap->handle) {
137 fr_strerror_printf("%s", pcap->errbuf);
138 return -1;
139 }
140 if (pcap_set_snaplen(pcap->handle, SNAPLEN) != 0) {
141 create_error:
142 fr_strerror_printf("%s", pcap_geterr(pcap->handle));
143 pcap_close(pcap->handle);
144 pcap->handle = NULL;
145 return -1;
146 }
147 if (pcap_set_timeout(pcap->handle, PCAP_NONBLOCK_TIMEOUT) != 0) {
148 goto create_error;
149 }
150 if (pcap_set_promisc(pcap->handle, pcap->promiscuous) != 0) {
151 goto create_error;
152 }
153
154 if (pcap_set_buffer_size(pcap->handle, SNAPLEN *
155 (pcap->buffer_pkts ? pcap->buffer_pkts : PCAP_BUFFER_DEFAULT)) != 0) {
156 goto create_error;
157 }
158 if (pcap_activate(pcap->handle) != 0) {
159 goto create_error;
160 }
161 #else
162 /*
163 * Alternative functions for libpcap < 1.0
164 */
165 pcap->handle = pcap_open_live(pcap->name, SNAPLEN, pcap->promiscuous, PCAP_NONBLOCK_TIMEOUT,
166 pcap->errbuf);
167 if (!pcap->handle) {
168 fr_strerror_printf("%s", pcap->errbuf);
169 return -1;
170 }
171 #endif
172 /*
173 * Despite accepting an errbuff, pcap_setnonblock doesn't seem to write
174 * error message there in newer versions.
175 */
176 if (pcap_setnonblock(pcap->handle, true, pcap->errbuf) != 0) {
177 fr_strerror_printf("%s", *pcap->errbuf != '\0' ?
178 pcap->errbuf : pcap_geterr(pcap->handle));
179 pcap_close(pcap->handle);
180 pcap->handle = NULL;
181 return -1;
182 }
183
184 pcap->fd = pcap_get_selectable_fd(pcap->handle);
185 pcap->link_layer = pcap_datalink(pcap->handle);
186 #ifndef __linux__
187 {
188 int value = 1;
189 if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) {
190 fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno));
191 }
192 }
193 #endif
194 }
195 break;
196
197 case PCAP_FILE_IN:
198 pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf);
199 if (!pcap->handle) {
200 fr_strerror_printf("%s", pcap->errbuf);
201
202 return -1;
203 }
204 pcap->fd = pcap_get_selectable_fd(pcap->handle);
205 pcap->link_layer = pcap_datalink(pcap->handle);
206 break;
207
208 case PCAP_FILE_OUT:
209 if (pcap->link_layer < 0) {
210 pcap->link_layer = DLT_EN10MB;
211 }
212 pcap->handle = pcap_open_dead(pcap->link_layer, SNAPLEN);
213 if (!pcap->handle) {
214 fr_strerror_printf("Unknown error occurred opening dead PCAP handle");
215
216 return -1;
217 }
218 pcap->dumper = pcap_dump_open(pcap->handle, pcap->name);
219 if (!pcap->dumper) {
220 fr_strerror_printf("%s", pcap_geterr(pcap->handle));
221
222 return -1;
223 }
224 break;
225
226 #ifdef HAVE_PCAP_FOPEN_OFFLINE
227 case PCAP_STDIO_IN:
228 pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf);
229 if (!pcap->handle) {
230 fr_strerror_printf("%s", pcap->errbuf);
231
232 return -1;
233 }
234 pcap->fd = pcap_get_selectable_fd(pcap->handle);
235 pcap->link_layer = pcap_datalink(pcap->handle);
236 break;
237 #else
238 case PCAP_STDIO_IN:
239 fr_strerror_printf("This version of libpcap does not support reading pcap data from streams");
240
241 return -1;
242 #endif
243 #ifdef HAVE_PCAP_DUMP_FOPEN
244 case PCAP_STDIO_OUT:
245 pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN);
246 pcap->dumper = pcap_dump_fopen(pcap->handle, stdout);
247 if (!pcap->dumper) {
248 fr_strerror_printf("%s", pcap_geterr(pcap->handle));
249
250 return -1;
251 }
252 break;
253 #else
254 case PCAP_STDIO_OUT:
255 fr_strerror_printf("This version of libpcap does not support writing pcap data to streams");
256
257 return -1;
258 #endif
259 case PCAP_INVALID:
260 default:
261 fr_assert(0);
262 fr_strerror_printf("Bad handle type (%i)", pcap->type);
263 return -1;
264 }
265
266 return 0;
267 }
268
269 /** Apply capture filter to an interface
270 *
271 * @param pcap handle to apply filter to.
272 * @param expression PCAP expression to use as a filter.
273 * @return 0 on success, 1 can't apply to interface, -1 on error.
274 */
fr_pcap_apply_filter(fr_pcap_t * pcap,char const * expression)275 int fr_pcap_apply_filter(fr_pcap_t *pcap, char const *expression)
276 {
277 bpf_u_int32 mask = 0; /* Our netmask */
278 bpf_u_int32 net = 0; /* Our IP */
279 struct bpf_program fp;
280
281 /*
282 * nflog devices are in the set of devices selected by default.
283 * Unfortunately there's a bug in all released version of libpcap (as of 2/1/2014)
284 * which triggers an abort if pcap_setfilter is called on an nflog interface.
285 *
286 * See here:
287 * https://github.com/the-tcpdump-group/libpcap/commit/676cf8a61ed240d0a86d471ef419f45ba35dba80
288 */
289 #ifdef DLT_NFLOG
290 if (pcap->link_layer == DLT_NFLOG) {
291 fr_strerror_printf("NFLOG link-layer type filtering not implemented");
292
293 return 1;
294 }
295 #endif
296
297 if (pcap->type == PCAP_INTERFACE_IN) {
298 if (pcap_lookupnet(pcap->name, &net, &mask, pcap->errbuf) < 0) {
299 fr_strerror_printf("Failed getting IP for interface \"%s\", using defaults: %s",
300 pcap->name, pcap->errbuf);
301 }
302 }
303
304 if (pcap_compile(pcap->handle, &fp, expression, 0, net) < 0) {
305 fr_strerror_printf("%s", pcap_geterr(pcap->handle));
306
307 return -1;
308 }
309
310 if (pcap_setfilter(pcap->handle, &fp) < 0) {
311 fr_strerror_printf("%s", pcap_geterr(pcap->handle));
312
313 return -1;
314 }
315
316 return 0;
317 }
318
fr_pcap_device_names(TALLOC_CTX * ctx,fr_pcap_t * pcap,char c)319 char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *pcap, char c)
320 {
321 fr_pcap_t *pcap_p;
322 char *buff, *p;
323 size_t len = 0, left = 0, wrote;
324
325 if (!pcap) {
326 goto null;
327 }
328
329 for (pcap_p = pcap;
330 pcap_p;
331 pcap_p = pcap_p->next) {
332 len += talloc_array_length(pcap_p->name); // Talloc array length includes the \0
333 }
334
335 if (!len) {
336 null:
337 return talloc_zero_array(ctx, char, 1);
338 }
339
340 left = len + 1;
341 buff = p = talloc_zero_array(ctx, char, left);
342 for (pcap_p = pcap;
343 pcap_p;
344 pcap_p = pcap_p->next) {
345 wrote = snprintf(p, left, "%s%c", pcap_p->name, c);
346 left -= wrote;
347 p += wrote;
348 }
349 buff[len - 1] = '\0';
350
351 return buff;
352 }
353
354 /** Check whether fr_pcap_link_layer_offset can process a link_layer
355 *
356 * @param link_layer to check.
357 * @return true if supported, else false.
358 */
fr_pcap_link_layer_supported(int link_layer)359 bool fr_pcap_link_layer_supported(int link_layer)
360 {
361 switch (link_layer) {
362 case DLT_EN10MB:
363 case DLT_RAW:
364 case DLT_NULL:
365 case DLT_LOOP:
366 #ifdef DLT_LINUX_SLL
367 case DLT_LINUX_SLL:
368 #endif
369 case DLT_PFLOG:
370 return true;
371
372 default:
373 return false;
374 }
375 }
376
377 /** Returns the length of the link layer header
378 *
379 * Libpcap does not include a decoding function to skip the L2 header, but it does
380 * at least inform us of the type.
381 *
382 * Unfortunately some headers are of variable length (like ethernet), so additional
383 * decoding logic is required.
384 *
385 * @note No header data is returned, this is only meant to be used to determine how
386 * data to consume before attempting to parse the IP header.
387 *
388 * @param data start of packet data.
389 * @param len caplen.
390 * @param link_layer value returned from pcap_linktype.
391 * @return the length of the header, or -1 on error.
392 */
fr_pcap_link_layer_offset(uint8_t const * data,size_t len,int link_layer)393 ssize_t fr_pcap_link_layer_offset(uint8_t const *data, size_t len, int link_layer)
394 {
395 uint8_t const *p = data;
396
397 switch (link_layer) {
398 case DLT_RAW:
399 break;
400
401 case DLT_NULL:
402 case DLT_LOOP:
403 p += 4;
404 if (((size_t)(p - data)) > len) {
405 ood:
406 fr_strerror_printf("Out of data, needed %zu bytes, have %zu bytes",
407 (size_t)(p - data), len);
408 return -1;
409 }
410 break;
411
412 case DLT_EN10MB:
413 {
414 uint16_t ether_type; /* Ethernet type */
415 int i;
416
417 p += 12; /* SRC/DST Mac-Addresses */
418 if (((size_t)(p - data)) > len) {
419 goto ood;
420 }
421
422 for (i = 0; i < 3; i++) {
423 ether_type = ntohs(*((uint16_t const *) p));
424 switch (ether_type) {
425 /*
426 * There are a number of devices out there which
427 * double tag with 0x8100 *sigh*
428 */
429 case 0x8100: /* CVLAN */
430 case 0x9100: /* SVLAN */
431 case 0x9200: /* SVLAN */
432 case 0x9300: /* SVLAN */
433 p += 4;
434 if (((size_t)(p - data)) > len) {
435 goto ood;
436 }
437 break;
438
439 default:
440 p += 2;
441 if (((size_t)(p - data)) > len) {
442 goto ood;
443 }
444 goto done;
445 }
446 }
447 fr_strerror_printf("Exceeded maximum level of VLAN tag nesting (2)");
448 return -1;
449 }
450
451 #ifdef DLT_LINUX_SLL
452 case DLT_LINUX_SLL:
453 p += 16;
454 if (((size_t)(p - data)) > len) {
455 goto ood;
456 }
457 break;
458 #endif
459
460 case DLT_PFLOG:
461 p += 28;
462 if (((size_t)(p - data)) > len) {
463 goto ood;
464 }
465 break;
466
467 default:
468 fr_strerror_printf("Unsupported link layer type %i", link_layer);
469 }
470
471 done:
472 return p - data;
473 }
474 #endif /* HAVE_LIBPCAP */
475