All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcap.c
Go to the documentation of this file.
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: ac462585c87135764146956082308d5b285f350c $
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 <sys/types.h>
28 
29 #ifndef SIOCGIFHWADDR
30  #include <net/if_dl.h>
31  #include <ifaddrs.h>
32 #else
33  #include <net/if.h>
34 #endif
35 
36 #include <freeradius-devel/pcap.h>
37 #include <freeradius-devel/net.h>
38 #include <freeradius-devel/rad_assert.h>
39 
40 /** Talloc destructor to free pcap resources associated with a handle.
41  *
42  * @param pcap to free.
43  * @return 0
44  */
45 static int _free_pcap(fr_pcap_t *pcap)
46 {
47  switch (pcap->type) {
48  case PCAP_INTERFACE_IN:
49  case PCAP_INTERFACE_OUT:
50  case PCAP_INTERFACE_IN_OUT:
51  case PCAP_FILE_IN:
52  case PCAP_STDIO_IN:
53  if (pcap->handle) {
54  pcap_close(pcap->handle);
55 
56  if (pcap->fd > 0) {
57  close(pcap->fd);
58  }
59  }
60  break;
61 
62  case PCAP_FILE_OUT:
63  case PCAP_STDIO_OUT:
64  if (pcap->dumper) {
65  pcap_dump_flush(pcap->dumper);
66  pcap_dump_close(pcap->dumper);
67  }
68  break;
69 
70  case PCAP_INVALID:
71  break;
72  }
73 
74  return 0;
75 }
76 
77 /** Get data link from pcap_if_t
78  *
79  * libpcap requires an open pcap handle to get data_link type
80  * unfortunately when we're trying to find useful interfaces
81  * this is too late.
82  *
83  * @param errbuff Error message.
84  * @param dev to get link layer for.
85  * @return
86  * - Datalink layer.
87  * - -1 on failure.
88  */
89 int fr_pcap_if_link_layer(char *errbuff, pcap_if_t *dev)
90 {
91  pcap_t *pcap;
92  int data_link;
93 
94  pcap = pcap_open_live(dev->name, 0, 0, 0, errbuff);
95  if (!pcap) return -1;
96 
97  data_link = pcap_datalink(pcap);
98  pcap_close(pcap);
99 
100  return data_link;
101 }
102 
103 /** Initialise a pcap handle abstraction
104  *
105  * @param ctx talloc TALLOC_CTX to allocate handle in.
106  * @param name of interface or file to open.
107  * @param type of handle to initialise.
108  * @return new handle or NULL on error.
109  */
110 fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type)
111 {
112  fr_pcap_t *this;
113 
114  if (!fr_assert(type >= PCAP_INTERFACE_IN && type <= PCAP_INTERFACE_IN_OUT)) {
115  fr_strerror_printf("Invalid PCAP type: %d", type);
116  return NULL;
117  }
118 
119  this = talloc_zero(ctx, fr_pcap_t);
120  if (!this) return NULL;
121 
122  talloc_set_destructor(this, _free_pcap);
123  this->name = talloc_typed_strdup(this, name);
124  this->type = type;
125  this->link_layer = -1;
126 
127  return this;
128 }
129 
130 /** Get MAC address for given interface
131  *
132  * @param[out] macaddr to write MAC address to.
133  * @param[in] ifname to get MAC for.
134  * @return
135  * - 0 on success.
136  * - -1 on error.
137  */
138 int fr_pcap_mac_addr(uint8_t *macaddr, char *ifname)
139 {
140 #ifndef SIOCGIFHWADDR
141  struct ifaddrs *ifap, *ifaptr;
142  unsigned char *ptr;
143 
144  if (getifaddrs(&ifap) == 0) {
145  for (ifaptr = ifap; ifaptr != NULL; ifaptr = (ifaptr)->ifa_next) {
146  if (!strcmp((ifaptr)->ifa_name, ifname) && (((ifaptr)->ifa_addr)->sa_family == AF_LINK)) {
147  ptr = (uint8_t *)LLADDR((struct sockaddr_dl *)(ifaptr)->ifa_addr);
148  memcpy(macaddr, ptr, ETHER_ADDR_LEN);
149  break;
150  }
151  }
152  freeifaddrs(ifap);
153  return (ifaptr != NULL ? 0 : -1);
154  }
155  return -1;
156 #else
157  int fd, ret;
158  struct ifreq ifr;
159 
160  fd = socket(AF_INET, SOCK_DGRAM, 0);
161 
162  if (fd < 0) {
163  return -1;
164  }
165 
166  ifr.ifr_addr.sa_family = AF_INET;
167  strncpy(ifr.ifr_name, ifname , IFNAMSIZ-1);
168 
169  ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
170 
171  close(fd);
172 
173  if (ret == 0) {
174  memcpy(macaddr, (uint8_t *)ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
175  return 0;
176  }
177  return -1;
178 #endif
179 }
180 
181 /** Open a PCAP handle abstraction
182  *
183  * This opens interfaces for capture or injection, or files/streams for reading/writing.
184  * @param pcap created with fr_pcap_init.
185  * @return
186  * - 0 on success.
187  * - -1 on failure.
188  */
189 int fr_pcap_open(fr_pcap_t *pcap)
190 {
191  switch (pcap->type) {
192  case PCAP_INTERFACE_OUT:
193  case PCAP_INTERFACE_IN:
194  case PCAP_INTERFACE_IN_OUT:
195  {
196 #if defined(HAVE_PCAP_CREATE) && defined(HAVE_PCAP_ACTIVATE)
197  pcap->handle = pcap_create(pcap->name, pcap->errbuf);
198  if (!pcap->handle) {
199  fr_strerror_printf("%s", pcap->errbuf);
200  return -1;
201  }
202  if (pcap_set_snaplen(pcap->handle, SNAPLEN) != 0) {
203  create_error:
204  fr_strerror_printf("%s", pcap_geterr(pcap->handle));
205  pcap_close(pcap->handle);
206  pcap->handle = NULL;
207  return -1;
208  }
209  if (pcap_set_timeout(pcap->handle, PCAP_NONBLOCK_TIMEOUT) != 0) {
210  goto create_error;
211  }
212  if (pcap_set_promisc(pcap->handle, pcap->promiscuous) != 0) {
213  goto create_error;
214  }
215 
216  if (pcap_set_buffer_size(pcap->handle, SNAPLEN *
217  (pcap->buffer_pkts ? pcap->buffer_pkts : PCAP_BUFFER_DEFAULT)) != 0) {
218  goto create_error;
219  }
220  if (pcap_activate(pcap->handle) != 0) {
221  goto create_error;
222  }
223 #else
224  /*
225  * Alternative functions for libpcap < 1.0
226  */
227  pcap->handle = pcap_open_live(pcap->name, SNAPLEN, pcap->promiscuous, PCAP_NONBLOCK_TIMEOUT,
228  pcap->errbuf);
229  if (!pcap->handle) {
230  fr_strerror_printf("%s", pcap->errbuf);
231  return -1;
232  }
233 #endif
234 
235  /*
236  * Do this later so we get real errors.
237  */
238  if (fr_pcap_mac_addr((uint8_t *)&pcap->ether_addr, pcap->name) != 0) {
239  fr_strerror_printf("Couldn't get MAC address for interface %s", pcap->name);
240  pcap_close(pcap->handle);
241  return -1;
242  }
243 
244  /*
245  * Despite accepting an errbuff, pcap_setnonblock doesn't seem to write
246  * error message there in newer versions.
247  */
248  if (pcap_setnonblock(pcap->handle, true, pcap->errbuf) != 0) {
249  fr_strerror_printf("%s", *pcap->errbuf != '\0' ?
250  pcap->errbuf : pcap_geterr(pcap->handle));
251  pcap_close(pcap->handle);
252  pcap->handle = NULL;
253  return -1;
254  }
255 
256  pcap->fd = pcap_get_selectable_fd(pcap->handle);
257  pcap->link_layer = pcap_datalink(pcap->handle);
258 #ifndef __linux__
259  {
260  int value = 1;
261  if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) {
262  fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno));
263  }
264  }
265 #endif
266  }
267  break;
268 
269  case PCAP_FILE_IN:
270  pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf);
271  if (!pcap->handle) {
272  fr_strerror_printf("%s", pcap->errbuf);
273 
274  return -1;
275  }
276  pcap->fd = pcap_get_selectable_fd(pcap->handle);
277  pcap->link_layer = pcap_datalink(pcap->handle);
278  break;
279 
280  case PCAP_FILE_OUT:
281  if (pcap->link_layer < 0) {
282  pcap->link_layer = DLT_EN10MB;
283  }
284  pcap->handle = pcap_open_dead(pcap->link_layer, SNAPLEN);
285  if (!pcap->handle) {
286  fr_strerror_printf("Unknown error occurred opening dead PCAP handle");
287 
288  return -1;
289  }
290  pcap->dumper = pcap_dump_open(pcap->handle, pcap->name);
291  if (!pcap->dumper) {
292  fr_strerror_printf("%s", pcap_geterr(pcap->handle));
293 
294  return -1;
295  }
296  break;
297 
298 #ifdef HAVE_PCAP_FOPEN_OFFLINE
299  case PCAP_STDIO_IN:
300  pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf);
301  if (!pcap->handle) {
302  fr_strerror_printf("%s", pcap->errbuf);
303 
304  return -1;
305  }
306  pcap->fd = pcap_get_selectable_fd(pcap->handle);
307  pcap->link_layer = pcap_datalink(pcap->handle);
308  break;
309 #else
310  case PCAP_STDIO_IN:
311  fr_strerror_printf("This version of libpcap does not support reading pcap data from streams");
312 
313  return -1;
314 #endif
315 #ifdef HAVE_PCAP_DUMP_FOPEN
316  case PCAP_STDIO_OUT:
317  pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN);
318  pcap->dumper = pcap_dump_fopen(pcap->handle, stdout);
319  if (!pcap->dumper) {
320  fr_strerror_printf("%s", pcap_geterr(pcap->handle));
321 
322  return -1;
323  }
324  break;
325 #else
326  case PCAP_STDIO_OUT:
327  fr_strerror_printf("This version of libpcap does not support writing pcap data to streams");
328 
329  return -1;
330 #endif
331  case PCAP_INVALID:
332  default:
333  fr_assert(0);
334  fr_strerror_printf("Bad handle type (%i)", pcap->type);
335  return -1;
336  }
337 
338  return 0;
339 }
340 
341 /** Apply capture filter to an interface
342  *
343  * @param pcap handle to apply filter to.
344  * @param expression PCAP expression to use as a filter.
345  * @return
346  * - 0 on success.
347  * - 1 can't apply to interface.
348  * - -1 on failure.
349  */
350 int fr_pcap_apply_filter(fr_pcap_t *pcap, char const *expression)
351 {
352  bpf_u_int32 mask = 0; /* Our netmask */
353  bpf_u_int32 net = 0; /* Our IP */
354  struct bpf_program fp;
355 
356  /*
357  * nflog devices are in the set of devices selected by default.
358  * Unfortunately there's a bug in all released version of libpcap (as of 2/1/2014)
359  * which triggers an abort if pcap_setfilter is called on an nflog interface.
360  *
361  * See here:
362  * https://github.com/the-tcpdump-group/libpcap/commit/676cf8a61ed240d0a86d471ef419f45ba35dba80
363  */
364 #ifdef DLT_NFLOG
365  if (pcap->link_layer == DLT_NFLOG) {
366  fr_strerror_printf("NFLOG link-layer type filtering not implemented");
367 
368  return 1;
369  }
370 #endif
371 
372  if (pcap->type == PCAP_INTERFACE_IN || pcap->type == PCAP_INTERFACE_IN_OUT) {
373  if (pcap_lookupnet(pcap->name, &net, &mask, pcap->errbuf) < 0) {
374  fr_strerror_printf("Failed getting IP for interface \"%s\", using defaults: %s",
375  pcap->name, pcap->errbuf);
376  }
377  }
378 
379  if (pcap_compile(pcap->handle, &fp, expression, 0, net) < 0) {
380  fr_strerror_printf("%s", pcap_geterr(pcap->handle));
381 
382  return -1;
383  }
384 
385  if (pcap_setfilter(pcap->handle, &fp) < 0) {
386  fr_strerror_printf("%s", pcap_geterr(pcap->handle));
387 
388  return -1;
389  }
390 
391  return 0;
392 }
393 
394 /** Retrieve list of interface names that will be used for capture.
395  * Only used for debugging.
396  *
397  * @param ctx talloc context for allicating string.
398  * @param pcap handle list.
399  * @param c separator to use for list.
400  * @return
401  * - string buffer.
402  */
403 char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *pcap, char c)
404 {
405  fr_pcap_t *pcap_p;
406  char *buff, *p;
407  size_t len = 0, left = 0, wrote;
408 
409  if (!pcap) {
410  goto null;
411  }
412 
413  for (pcap_p = pcap;
414  pcap_p;
415  pcap_p = pcap_p->next) {
416  len += talloc_array_length(pcap_p->name); // Talloc array length includes the \0
417  }
418 
419  if (!len) {
420  null:
421  return talloc_zero_array(ctx, char, 1);
422  }
423 
424  left = len + 1;
425  buff = p = talloc_zero_array(ctx, char, left);
426  for (pcap_p = pcap;
427  pcap_p;
428  pcap_p = pcap_p->next) {
429  wrote = snprintf(p, left, "%s%c", pcap_p->name, c);
430  left -= wrote;
431  p += wrote;
432  }
433  buff[len - 1] = '\0';
434 
435  return buff;
436 }
437 #endif /* HAVE_LIBPCAP */
static char const * name
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
static uint32_t mask
Definition: rbmonkey.c:75
#define fr_assert(_x)
Definition: libradius.h:505
#define ETHER_ADDR_LEN
Definition: net.h:53
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
Definition: net.h:44
char * talloc_typed_strdup(void const *t, char const *p)
Call talloc strdup, setting the type on the new chunk correctly.
Definition: missing.c:588