/** Add a VP to the end of the list. * * Locates the end of 'first', and links an additional VP 'add' at the end. * * @param[in] first VP in linked list. Will add new VP to the end of this list. * @param[in] add VP to add to list. */ void pairadd(VALUE_PAIR **first, VALUE_PAIR *add) { VALUE_PAIR *i; if (!add) return; VERIFY_VP(add); if (*first == NULL) { *first = add; return; } for (i = *first; i->next; i = i->next) { #ifdef WITH_VERIFY_PTR VERIFY_VP(i); /* * The same VP should never by added multiple times * to the same list. */ fr_assert(i != add); #endif } i->next = add; }
/** Generate a backtrace for an object * * If this is the first entry being inserted */ int fr_backtrace_do(fr_bt_marker_t *marker) { fr_bt_info_t *bt; if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) return -1; bt = talloc_zero(NULL, fr_bt_info_t); if (!bt) return -1; bt->obj = marker->obj; bt->count = backtrace(bt->frames, MAX_BT_FRAMES); fr_cbuff_rp_insert(marker->cbuff, bt); return 0; }
/** Remove the current pair * * @todo this is really inefficient and should be fixed... * * @addtogroup module_safe * * @param cursor to remove the current pair from. * @return * - #VALUE_PAIR we just replaced. * - NULL on error. */ VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor) { VALUE_PAIR *vp, **last; if (!fr_assert(cursor->first)) return NULL; /* cursor must have been initialised */ vp = cursor->current; if (!vp) return NULL; last = cursor->first; while (*last != vp) last = &(*last)->next; fr_cursor_next(cursor); /* Advance the cursor past the one were about to delete */ *last = vp->next; vp->next = NULL; /* * Fixup cursor->found if we removed the VP it was referring to */ if (vp == cursor->found) cursor->found = *last; /* * Fixup cursor->last if we removed the VP it was referring to */ if (vp == cursor->last) cursor->last = *last; return vp; }
/** Generate a backtrace for an object during destruction * * If this is the first entry being inserted */ static int _fr_do_bt(fr_bt_marker_t *marker) { fr_bt_info_t *bt; if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) { return -1; } bt = talloc_zero(marker->cbuff, fr_bt_info_t); if (!bt) { return -1; } bt->count = backtrace(bt->frames, MAX_BT_FRAMES); fr_cbuff_rp_insert(marker->cbuff, bt); return 0; }
/** Find the pair with the matching DAs * */ VALUE_PAIR *pair_find_by_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag) { vp_cursor_t cursor; if(!fr_assert(da)) { return NULL; } (void) fr_cursor_init(&cursor, &vp); return fr_cursor_next_by_da(&cursor, da, tag); }
/** Merges multiple VALUE_PAIR into the cursor * * Add multiple VALUE_PAIR from add to cursor. * * @addtogroup module_safe * * @param cursor to insert VALUE_PAIRs with * @param add one or more VALUE_PAIRs (may be NULL, which results in noop). */ void fr_cursor_merge(vp_cursor_t *cursor, VALUE_PAIR *add) { vp_cursor_t from; VALUE_PAIR *vp; if (!add) return; if (!fr_assert(cursor->first)) return; /* cursor must have been initialised */ for (vp = fr_cursor_init(&from, &add); vp; vp = fr_cursor_next(&from)) { fr_cursor_insert(cursor, vp); } }
/** Escape string that may contain binary data, and write it to a new buffer * * This is useful in situations where we expect printable strings as input, * but under some conditions may get binary data. A good example is libldap * and the arrays of struct berval ldap_get_values_len returns. * * @param[in] ctx To allocate new buffer in. * @param[in] in String to escape. * @param[in] inlen Length of string. Should be >= 0 if the data may contain * embedded \0s. Must be >= 0 if data may not be \0 terminated. * If < 0 inlen will be calculated using strlen. * @param[in] quote the quotation character. * @return new buffer holding the escaped string. */ char *fr_aprints(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote) { size_t len, ret; char *out; len = fr_prints_len(in, inlen, quote); out = talloc_array(ctx, char, len); ret = fr_prints(out, len, in, inlen, quote); /* * This is a fatal error, but fr_assert is the strongest * assert we're allowed to use in library functions. */ if (!fr_assert(ret == (len - 1))) { talloc_free(out); return NULL; } return out; }
/** Write an error to the library errorbuff detailing the mismatch * * Retrieve output with fr_strerror(); * * @todo add thread specific talloc contexts. * * @param ctx a hack until we have thread specific talloc contexts. * @param failed pair of attributes which didn't match. */ void pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2]) { VALUE_PAIR const *filter = failed[0]; VALUE_PAIR const *list = failed[1]; char *value, *str; (void) fr_strerror(); /* Clear any existing messages */ if (!fr_assert(!(!filter && !list))) return; if (!list) { if (!filter) return; fr_strerror_printf("Attribute \"%s\" not found in list", filter->da->name); return; } if (!filter || (filter->da != list->da)) { fr_strerror_printf("Attribute \"%s\" not found in filter", list->da->name); return; } if (!TAG_EQ(filter->tag, list->tag)) { fr_strerror_printf("Attribute \"%s\" tag \"%i\" didn't match filter tag \"%i\"", list->da->name, list->tag, filter->tag); return; } value = vp_aprints_value(ctx, list, '"'); str = vp_aprints(ctx, filter, '"'); fr_strerror_printf("Attribute value \"%s\" didn't match filter: %s", value, str); talloc_free(str); talloc_free(value); return; }
int main(int argc, char *argv[]) { rs_t *conf; fr_pcap_t *in = NULL, *in_p; fr_pcap_t **in_head = ∈ fr_pcap_t *out = NULL; int ret = 1; /* Exit status */ int limit = -1; /* How many packets to sniff */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error buffer */ int port = 1812; char buffer[1024]; int opt; FR_TOKEN parsecode; char const *radius_dir = RADIUS_DIR; rs_stats_t stats; fr_debug_flag = 2; log_dst = stdout; talloc_set_log_stderr(); conf = talloc_zero(NULL, rs_t); if (!fr_assert(conf)) { exit (1); } /* * We don't really want probes taking down machines */ #ifdef HAVE_TALLOC_SET_MEMLIMIT talloc_set_memlimit(conf, 52428800); /* 50 MB */ #endif /* * Get options */ while ((opt = getopt(argc, argv, "c:d:DFf:hi:I:p:qr:s:Svw:xXW:P:O:")) != EOF) { switch (opt) { case 'c': limit = atoi(optarg); if (limit <= 0) { fprintf(stderr, "radsniff: Invalid number of packets \"%s\"", optarg); exit(1); } break; case 'd': radius_dir = optarg; break; case 'D': { pcap_if_t *all_devices = NULL; pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } int i = 1; for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { INFO("%i.%s", i++, dev_p->name); } ret = 0; goto finish; } case 'F': conf->from_stdin = true; conf->to_stdout = true; break; case 'f': conf->pcap_filter = optarg; break; case 'h': usage(0); break; case 'i': *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_dev = true; break; case 'I': *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; conf->from_file = true; break; case 'p': port = atoi(optarg); break; case 'q': if (fr_debug_flag > 0) { fr_debug_flag--; } break; case 'r': conf->radius_filter = optarg; break; case 's': conf->radius_secret = optarg; break; case 'S': conf->do_sort = true; break; case 'v': #ifdef HAVE_COLLECTDC_H INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(), lcc_version_string()); #else INFO("%s %s", radsniff_version, pcap_lib_version()); #endif exit(0); break; case 'w': out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT); conf->to_file = true; break; case 'x': case 'X': fr_debug_flag++; break; case 'W': conf->stats.interval = atoi(optarg); if (conf->stats.interval <= 0) { ERROR("Stats interval must be > 0"); usage(64); } break; case 'T': conf->stats.timeout = atoi(optarg); if (conf->stats.timeout <= 0) { ERROR("Timeout value must be > 0"); usage(64); } break; #ifdef HAVE_COLLECTDC_H case 'P': conf->stats.prefix = optarg; break; case 'O': conf->stats.collectd = optarg; conf->stats.out = RS_STATS_OUT_COLLECTD; break; #endif default: usage(64); } } /* What's the point in specifying -F ?! */ if (conf->from_stdin && conf->from_file && conf->to_file) { usage(64); } /* Can't read from both... */ if (conf->from_file && conf->from_dev) { usage(64); } /* Reading from file overrides stdin */ if (conf->from_stdin && (conf->from_file || conf->from_dev)) { conf->from_stdin = false; } /* Writing to file overrides stdout */ if (conf->to_file && conf->to_stdout) { conf->to_stdout = false; } if (conf->to_stdout) { out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT); if (!out) { goto finish; } } if (conf->from_stdin) { *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN); if (!*in_head) { goto finish; } in_head = &(*in_head)->next; } if (!conf->radius_secret) { conf->radius_secret = RS_DEFAULT_SECRET; } if (conf->stats.interval && !conf->stats.out) { conf->stats.out = RS_STATS_OUT_STDIO; } if (conf->stats.timeout == 0) { conf->stats.timeout = RS_DEFAULT_TIMEOUT; } /* * If were writing pcap data stdout we *really* don't want to send * logging there as well. */ log_dst = conf->to_stdout ? stderr : stdout; #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN) if (conf->from_stdin || conf->to_stdout) { ERROR("PCAP streams not supported"); goto finish; } #endif if (!conf->pcap_filter) { snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d", port, port + 1, 3799); conf->pcap_filter = buffer; } if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radsniff"); ret = 64; goto finish; } fr_strerror(); /* Clear out any non-fatal errors */ if (conf->radius_filter) { parsecode = userparse(NULL, conf->radius_filter, &filter_vps); if (parsecode == T_OP_INVALID) { ERROR("Invalid RADIUS filter \"%s\" (%s)", conf->radius_filter, fr_strerror()); ret = 64; goto finish; } if (!filter_vps) { ERROR("Empty RADIUS filter \"%s\"", conf->radius_filter); ret = 64; goto finish; } filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!filter_tree) { ERROR("Failed creating filter tree"); ret = 64; goto finish; } } /* * Setup the request tree */ request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0); if (!request_tree) { ERROR("Failed creating request tree"); goto finish; } /* * Allocate a null packet for decrypting attributes in CoA requests */ nullpacket = rad_alloc(conf, 0); if (!nullpacket) { ERROR("Out of memory"); goto finish; } /* * Get the default capture device */ if (!conf->from_stdin && !conf->from_file && !conf->from_dev) { pcap_if_t *all_devices; /* List of all devices libpcap can listen on */ pcap_if_t *dev_p; if (pcap_findalldevs(&all_devices, errbuf) < 0) { ERROR("Error getting available capture devices: %s", errbuf); goto finish; } if (!all_devices) { ERROR("No capture files specified and no live interfaces available"); ret = 64; goto finish; } for (dev_p = all_devices; dev_p; dev_p = dev_p->next) { /* Don't use the any devices, it's horribly broken */ if (!strcmp(dev_p->name, "any")) continue; *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN); in_head = &(*in_head)->next; } conf->from_auto = true; conf->from_dev = true; INFO("Defaulting to capture on all interfaces"); } /* * Print captures values which will be used */ if (fr_debug_flag > 2) { DEBUG1("Sniffing with options:"); if (conf->from_dev) { char *buff = fr_pcap_device_names(conf, in, ' '); DEBUG1(" Device(s) : [%s]", buff); talloc_free(buff); } if (conf->to_file || conf->to_stdout) { DEBUG1(" Writing to : [%s]", out->name); } if (limit > 0) { DEBUG1(" Capture limit (packets) : [%d]", limit); } DEBUG1(" PCAP filter : [%s]", conf->pcap_filter); DEBUG1(" RADIUS secret : [%s]", conf->radius_secret); if (filter_vps){ DEBUG1(" RADIUS filter :"); vp_printlist(log_dst, filter_vps); } } /* * Open our interface to collectd */ #ifdef HAVE_COLLECTDC_H if (conf->stats.out == RS_STATS_OUT_COLLECTD) { size_t i; rs_stats_tmpl_t *tmpl, **next; if (rs_stats_collectd_open(conf) < 0) { exit(1); } next = &conf->stats.tmpl; for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) { tmpl = rs_stats_collectd_init_latency(conf, next, conf, "radius_pkt_ex", &stats.exchange[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { goto tmpl_error; } next = &(tmpl->next); tmpl = rs_stats_collectd_init_counter(conf, next, conf, "radius_pkt", &stats.gauge.type[rs_useful_codes[i]], rs_useful_codes[i]); if (!tmpl) { tmpl_error: ERROR("Error allocating memory for stats template"); goto finish; } next = &(tmpl->next); } } #endif /* * This actually opens the capture interfaces/files (we just allocated the memory earlier) */ { fr_pcap_t *prev = NULL; for (in_p = in; in_p; in_p = in_p->next) { if (fr_pcap_open(in_p) < 0) { if (!conf->from_auto) { ERROR("Failed opening pcap handle for %s", in_p->name); goto finish; } DEBUG("Failed opening pcap handle: %s", fr_strerror()); /* Unlink it from the list */ if (prev) { prev->next = in_p->next; talloc_free(in_p); in_p = prev; } else { in = in_p->next; talloc_free(in_p); in_p = in; } goto next; } if (conf->pcap_filter) { if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) { ERROR("Failed applying filter"); goto finish; } } next: prev = in_p; } } /* * Open our output interface (if we have one); */ if (out) { if (fr_pcap_open(out) < 0) { ERROR("Failed opening pcap output"); goto finish; } } /* * Setup and enter the main event loop. Who needs libev when you can roll your own... */ { struct timeval now; fr_event_list_t *events; rs_update_t update; char *buff; memset(&stats, 0, sizeof(stats)); memset(&update, 0, sizeof(update)); events = fr_event_list_create(conf, _rs_event_status); if (!events) { ERROR(); goto finish; } for (in_p = in; in_p; in_p = in_p->next) { rs_event_t *event; event = talloc_zero(events, rs_event_t); event->conf = conf; event->in = in_p; event->out = out; event->stats = &stats; if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) { ERROR("Failed inserting file descriptor"); goto finish; } } buff = fr_pcap_device_names(conf, in, ' '); INFO("Sniffing on (%s)", buff); talloc_free(buff); gettimeofday(&now, NULL); start_pcap = now; /* * Insert our stats processor */ if (conf->stats.interval) { update.list = events; update.conf = conf; update.stats = &stats; update.in = in; now.tv_sec += conf->stats.interval; now.tv_usec = 0; fr_event_insert(events, rs_stats_process, (void *) &update, &now, NULL); } ret = fr_event_loop(events); /* Enter the main event loop */ } INFO("Done sniffing"); finish: if (filter_tree) { rbtree_free(filter_tree); } INFO("Exiting..."); /* * Free all the things! This also closes all the sockets and file descriptors */ talloc_free(conf); return ret; }
/** Open a PCAP handle abstraction * * This opens interfaces for capture or injection, or files/streams for reading/writing. * @param pcap created with fr_pcap_init. * @return 0 on success, -1 on error. */ int fr_pcap_open(fr_pcap_t *pcap) { switch (pcap->type) { case PCAP_INTERFACE_OUT: case PCAP_INTERFACE_IN: { #if defined(HAVE_PCAP_CREATE) && defined(HAVE_PCAP_ACTIVATE) pcap->handle = pcap_create(pcap->name, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } if (pcap_set_snaplen(pcap->handle, SNAPLEN) != 0) { create_error: fr_strerror_printf("%s", pcap_geterr(pcap->handle)); pcap_close(pcap->handle); pcap->handle = NULL; return -1; } if (pcap_set_timeout(pcap->handle, PCAP_NONBLOCK_TIMEOUT) != 0) { goto create_error; } if (pcap_set_promisc(pcap->handle, pcap->promiscuous) != 0) { goto create_error; } if (pcap_set_buffer_size(pcap->handle, SNAPLEN * (pcap->buffer_pkts ? pcap->buffer_pkts : PCAP_BUFFER_DEFAULT)) != 0) { goto create_error; } if (pcap_activate(pcap->handle) != 0) { goto create_error; } #else /* * Alternative functions for libpcap < 1.0 */ pcap->handle = pcap_open_live(pcap->name, SNAPLEN, pcap->promiscuous, PCAP_NONBLOCK_TIMEOUT, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } #endif /* * Despite accepting an errbuff, pcap_setnonblock doesn't seem to write * error message there in newer versions. */ if (pcap_setnonblock(pcap->handle, true, pcap->errbuf) != 0) { fr_strerror_printf("%s", *pcap->errbuf != '\0' ? pcap->errbuf : pcap_geterr(pcap->handle)); pcap_close(pcap->handle); pcap->handle = NULL; return -1; } pcap->fd = pcap_get_selectable_fd(pcap->handle); pcap->link_layer = pcap_datalink(pcap->handle); #ifndef __linux__ { int value = 1; if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) { fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno)); } } #endif } break; case PCAP_FILE_IN: pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } pcap->fd = pcap_get_selectable_fd(pcap->handle); pcap->link_layer = pcap_datalink(pcap->handle); break; case PCAP_FILE_OUT: if (pcap->link_layer < 0) { pcap->link_layer = DLT_EN10MB; } pcap->handle = pcap_open_dead(pcap->link_layer, SNAPLEN); if (!pcap->handle) { fr_strerror_printf("Unknown error occurred opening dead PCAP handle"); return -1; } pcap->dumper = pcap_dump_open(pcap->handle, pcap->name); if (!pcap->dumper) { fr_strerror_printf("%s", pcap_geterr(pcap->handle)); return -1; } break; #ifdef HAVE_PCAP_FOPEN_OFFLINE case PCAP_STDIO_IN: pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } pcap->fd = pcap_get_selectable_fd(pcap->handle); pcap->link_layer = pcap_datalink(pcap->handle); break; #else case PCAP_STDIO_IN: fr_strerror_printf("This version of libpcap does not support reading pcap data from streams"); return -1; #endif #ifdef HAVE_PCAP_DUMP_FOPEN case PCAP_STDIO_OUT: pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN); pcap->dumper = pcap_dump_fopen(pcap->handle, stdout); if (!pcap->dumper) { fr_strerror_printf("%s", pcap_geterr(pcap->handle)); return -1; } break; #else case PCAP_STDIO_OUT: fr_strerror_printf("This version of libpcap does not support writing pcap data to streams"); return -1; #endif case PCAP_INVALID: default: fr_assert(0); fr_strerror_printf("Bad handle type (%i)", pcap->type); return -1; } return 0; }
/** Insert a single VALUE_PAIR at the end of the list * * @note Will not advance cursor position to new attribute, but will set cursor * to this attribute, if it's the first one in the list. * * Insert a VALUE_PAIR at the end of the list. * * @addtogroup module_safe * * @param cursor to operate on. * @param vp to insert. */ void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp) { VALUE_PAIR *i; if (!fr_assert(cursor->first)) return; /* cursor must have been initialised */ if (!vp) return; VERIFY_VP(vp); /* * Only allow one VP to by inserted at a time */ vp->next = NULL; /* * Cursor was initialised with a pointer to a NULL value_pair */ if (!*cursor->first) { *cursor->first = vp; cursor->current = vp; return; } /* * We don't yet know where the last VALUE_PAIR is * * Assume current is closer to the end of the list and * use that if available. */ if (!cursor->last) cursor->last = cursor->current ? cursor->current : *cursor->first; VERIFY_VP(cursor->last); /* * Wind last to the end of the list. */ if (cursor->last->next) { for (i = cursor->last; i; i = i->next) { VERIFY_VP(i); cursor->last = i; } } /* * Either current was never set, or something iterated to the * end of the attribute list. In both cases the newly inserted * VALUE_PAIR should be set as the current VALUE_PAIR. */ if (!cursor->current) cursor->current = vp; /* * Add the VALUE_PAIR to the end of the list */ cursor->last->next = vp; cursor->last = vp; /* Wind it forward a little more */ /* * If the next pointer was NULL, and the VALUE_PAIR * just added has a next pointer value, set the cursor's next * pointer to the VALUE_PAIR's next pointer. */ if (!cursor->next) cursor->next = cursor->current->next; }
/** Open a PCAP handle abstraction * * This opens interfaces for capture or injection, or files/streams for reading/writing. * @param pcap created with fr_pcap_init. * @return 0 on success, -1 on error. */ int fr_pcap_open(fr_pcap_t *pcap) { switch (pcap->type) { case PCAP_INTERFACE_OUT: case PCAP_INTERFACE_IN: pcap->handle = pcap_open_live(pcap->name, SNAPLEN, true, PCAP_NONBLOCK_TIMEOUT, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } pcap->fd = pcap_get_selectable_fd(pcap->handle); #ifndef __linux__ { int value = 1; if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) { fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno)); } } #endif break; case PCAP_FILE_IN: pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } break; case PCAP_FILE_OUT: pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN); pcap->dumper = pcap_dump_open(pcap->handle, pcap->name); if (!pcap->dumper) { fr_strerror_printf("%s", pcap->errbuf); return -1; } break; #ifdef HAVE_PCAP_FOPEN_OFFLINE case PCAP_STDIO_IN: pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf); if (!pcap->handle) { fr_strerror_printf("%s", pcap->errbuf); return -1; } break; #else case PCAP_STDIO_IN: fr_strerror_printf("This version of libpcap does not support reading pcap data from streams"); return -1; #endif #ifdef HAVE_PCAP_DUMP_FOPEN case PCAP_STDIO_OUT: pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN); pcap->dumper = pcap_dump_fopen(pcap->handle, stdout); if (!pcap->dumper) { fr_strerror_printf("%s", pcap_geterr(pcap->handle)); return -1; } break; #else case PCAP_STDIO_OUT: fr_strerror_printf("This version of libpcap does not support writing pcap data to streams"); return -1; #endif case PCAP_INVALID: default: fr_assert(0); return -1; } return 0; }