1335640Shselasky/* 2335640Shselasky * Copyright (c) 1993, 1994, 1995, 1996, 1997 3335640Shselasky * The Regents of the University of California. All rights reserved. 4335640Shselasky * 5335640Shselasky * Redistribution and use in source and binary forms, with or without 6335640Shselasky * modification, are permitted provided that: (1) source code distributions 7335640Shselasky * retain the above copyright notice and this paragraph in its entirety, (2) 8335640Shselasky * distributions including binary code include the above copyright notice and 9335640Shselasky * this paragraph in its entirety in the documentation or other materials 10335640Shselasky * provided with the distribution, and (3) all advertising materials mentioning 11335640Shselasky * features or use of this software display the following acknowledgement: 12335640Shselasky * ``This product includes software developed by the University of California, 13335640Shselasky * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 14335640Shselasky * the University nor the names of its contributors may be used to endorse 15335640Shselasky * or promote products derived from this software without specific prior 16335640Shselasky * written permission. 17335640Shselasky * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 18335640Shselasky * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 19335640Shselasky * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20335640Shselasky */ 21335640Shselasky 22335640Shselasky#ifdef HAVE_CONFIG_H 23335640Shselasky#include <config.h> 24335640Shselasky#endif 25335640Shselasky 26335640Shselasky#include <sys/param.h> 27335640Shselasky#include <sys/file.h> 28335640Shselasky#include <sys/ioctl.h> 29335640Shselasky#include <sys/socket.h> 30335640Shselasky#include <sys/time.h> 31335640Shselasky 32335640Shselasky#include <net/raw.h> 33335640Shselasky#include <net/if.h> 34335640Shselasky 35335640Shselasky#include <netinet/in.h> 36335640Shselasky#include <netinet/in_systm.h> 37335640Shselasky#include <netinet/ip.h> 38335640Shselasky#include <netinet/if_ether.h> 39335640Shselasky#include <netinet/ip_var.h> 40335640Shselasky#include <netinet/udp.h> 41335640Shselasky#include <netinet/udp_var.h> 42335640Shselasky#include <netinet/tcp.h> 43335640Shselasky#include <netinet/tcpip.h> 44335640Shselasky 45335640Shselasky#include <errno.h> 46335640Shselasky#include <stdio.h> 47335640Shselasky#include <stdlib.h> 48335640Shselasky#include <string.h> 49335640Shselasky#include <unistd.h> 50335640Shselasky 51335640Shselasky#include "pcap-int.h" 52335640Shselasky 53335640Shselasky#ifdef HAVE_OS_PROTO_H 54335640Shselasky#include "os-proto.h" 55335640Shselasky#endif 56335640Shselasky 57335640Shselasky/* 58335640Shselasky * Private data for capturing on snoop devices. 59335640Shselasky */ 60335640Shselaskystruct pcap_snoop { 61335640Shselasky struct pcap_stat stat; 62335640Shselasky}; 63335640Shselasky 64335640Shselaskystatic int 65335640Shselaskypcap_read_snoop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 66335640Shselasky{ 67335640Shselasky struct pcap_snoop *psn = p->priv; 68335640Shselasky int cc; 69335640Shselasky register struct snoopheader *sh; 70335640Shselasky register u_int datalen; 71335640Shselasky register u_int caplen; 72335640Shselasky register u_char *cp; 73335640Shselasky 74335640Shselaskyagain: 75335640Shselasky /* 76335640Shselasky * Has "pcap_breakloop()" been called? 77335640Shselasky */ 78335640Shselasky if (p->break_loop) { 79335640Shselasky /* 80335640Shselasky * Yes - clear the flag that indicates that it 81335640Shselasky * has, and return -2 to indicate that we were 82335640Shselasky * told to break out of the loop. 83335640Shselasky */ 84335640Shselasky p->break_loop = 0; 85335640Shselasky return (-2); 86335640Shselasky } 87335640Shselasky cc = read(p->fd, (char *)p->buffer, p->bufsize); 88335640Shselasky if (cc < 0) { 89335640Shselasky /* Don't choke when we get ptraced */ 90335640Shselasky switch (errno) { 91335640Shselasky 92335640Shselasky case EINTR: 93335640Shselasky goto again; 94335640Shselasky 95335640Shselasky case EWOULDBLOCK: 96335640Shselasky return (0); /* XXX */ 97335640Shselasky } 98335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), 99335640Shselasky errno, "read"); 100335640Shselasky return (-1); 101335640Shselasky } 102335640Shselasky sh = (struct snoopheader *)p->buffer; 103335640Shselasky datalen = sh->snoop_packetlen; 104335640Shselasky 105335640Shselasky /* 106335640Shselasky * XXX - Sigh, snoop_packetlen is a 16 bit quantity. If we 107335640Shselasky * got a short length, but read a full sized snoop pakcet, 108335640Shselasky * assume we overflowed and add back the 64K... 109335640Shselasky */ 110335640Shselasky if (cc == (p->snapshot + sizeof(struct snoopheader)) && 111335640Shselasky (datalen < p->snapshot)) 112335640Shselasky datalen += (64 * 1024); 113335640Shselasky 114335640Shselasky caplen = (datalen < p->snapshot) ? datalen : p->snapshot; 115335640Shselasky cp = (u_char *)(sh + 1) + p->offset; /* XXX */ 116335640Shselasky 117335640Shselasky /* 118335640Shselasky * XXX unfortunately snoop loopback isn't exactly like 119335640Shselasky * BSD's. The address family is encoded in the first 2 120335640Shselasky * bytes rather than the first 4 bytes! Luckily the last 121335640Shselasky * two snoop loopback bytes are zeroed. 122335640Shselasky */ 123335640Shselasky if (p->linktype == DLT_NULL && *((short *)(cp + 2)) == 0) { 124335640Shselasky u_int *uip = (u_int *)cp; 125335640Shselasky *uip >>= 16; 126335640Shselasky } 127335640Shselasky 128335640Shselasky if (p->fcode.bf_insns == NULL || 129335640Shselasky bpf_filter(p->fcode.bf_insns, cp, datalen, caplen)) { 130335640Shselasky struct pcap_pkthdr h; 131335640Shselasky ++psn->stat.ps_recv; 132335640Shselasky h.ts.tv_sec = sh->snoop_timestamp.tv_sec; 133335640Shselasky h.ts.tv_usec = sh->snoop_timestamp.tv_usec; 134335640Shselasky h.len = datalen; 135335640Shselasky h.caplen = caplen; 136335640Shselasky (*callback)(user, &h, cp); 137335640Shselasky return (1); 138335640Shselasky } 139335640Shselasky return (0); 140335640Shselasky} 141335640Shselasky 142335640Shselaskystatic int 143335640Shselaskypcap_inject_snoop(pcap_t *p, const void *buf, size_t size) 144335640Shselasky{ 145335640Shselasky int ret; 146335640Shselasky 147335640Shselasky /* 148335640Shselasky * XXX - libnet overwrites the source address with what I 149335640Shselasky * presume is the interface's address; is that required? 150335640Shselasky */ 151335640Shselasky ret = write(p->fd, buf, size); 152335640Shselasky if (ret == -1) { 153335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 154335640Shselasky errno, "send"); 155335640Shselasky return (-1); 156335640Shselasky } 157335640Shselasky return (ret); 158335640Shselasky} 159335640Shselasky 160335640Shselaskystatic int 161335640Shselaskypcap_stats_snoop(pcap_t *p, struct pcap_stat *ps) 162335640Shselasky{ 163335640Shselasky struct pcap_snoop *psn = p->priv; 164335640Shselasky register struct rawstats *rs; 165335640Shselasky struct rawstats rawstats; 166335640Shselasky 167335640Shselasky rs = &rawstats; 168335640Shselasky memset(rs, 0, sizeof(*rs)); 169335640Shselasky if (ioctl(p->fd, SIOCRAWSTATS, (char *)rs) < 0) { 170335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), 171335640Shselasky errno, "SIOCRAWSTATS"); 172335640Shselasky return (-1); 173335640Shselasky } 174335640Shselasky 175335640Shselasky /* 176335640Shselasky * "ifdrops" are those dropped by the network interface 177335640Shselasky * due to resource shortages or hardware errors. 178335640Shselasky * 179335640Shselasky * "sbdrops" are those dropped due to socket buffer limits. 180335640Shselasky * 181335640Shselasky * As filter is done in userland, "sbdrops" counts packets 182335640Shselasky * regardless of whether they would've passed the filter. 183335640Shselasky * 184335640Shselasky * XXX - does this count *all* Snoop or Drain sockets, 185335640Shselasky * rather than just this socket? If not, why does it have 186335640Shselasky * both Snoop and Drain statistics? 187335640Shselasky */ 188335640Shselasky psn->stat.ps_drop = 189335640Shselasky rs->rs_snoop.ss_ifdrops + rs->rs_snoop.ss_sbdrops + 190335640Shselasky rs->rs_drain.ds_ifdrops + rs->rs_drain.ds_sbdrops; 191335640Shselasky 192335640Shselasky /* 193335640Shselasky * "ps_recv" counts only packets that passed the filter. 194335640Shselasky * As filtering is done in userland, this does not include 195335640Shselasky * packets dropped because we ran out of buffer space. 196335640Shselasky */ 197335640Shselasky *ps = psn->stat; 198335640Shselasky return (0); 199335640Shselasky} 200335640Shselasky 201335640Shselasky/* XXX can't disable promiscuous */ 202335640Shselaskystatic int 203335640Shselaskypcap_activate_snoop(pcap_t *p) 204335640Shselasky{ 205335640Shselasky int fd; 206335640Shselasky struct sockaddr_raw sr; 207335640Shselasky struct snoopfilter sf; 208335640Shselasky u_int v; 209335640Shselasky int ll_hdrlen; 210335640Shselasky int snooplen; 211335640Shselasky struct ifreq ifr; 212335640Shselasky 213335640Shselasky fd = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP); 214335640Shselasky if (fd < 0) { 215335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 216335640Shselasky errno, "snoop socket"); 217335640Shselasky goto bad; 218335640Shselasky } 219335640Shselasky p->fd = fd; 220335640Shselasky memset(&sr, 0, sizeof(sr)); 221335640Shselasky sr.sr_family = AF_RAW; 222335640Shselasky (void)strncpy(sr.sr_ifname, p->opt.device, sizeof(sr.sr_ifname)); 223335640Shselasky if (bind(fd, (struct sockaddr *)&sr, sizeof(sr))) { 224335640Shselasky /* 225335640Shselasky * XXX - there's probably a particular bind error that 226335640Shselasky * means "there's no such device" and a particular bind 227335640Shselasky * error that means "that device doesn't support snoop"; 228335640Shselasky * they might be the same error, if they both end up 229335640Shselasky * meaning "snoop doesn't know about that device". 230335640Shselasky */ 231335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 232335640Shselasky errno, "snoop bind"); 233335640Shselasky goto bad; 234335640Shselasky } 235335640Shselasky memset(&sf, 0, sizeof(sf)); 236335640Shselasky if (ioctl(fd, SIOCADDSNOOP, &sf) < 0) { 237335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 238335640Shselasky errno, "SIOCADDSNOOP"); 239335640Shselasky goto bad; 240335640Shselasky } 241335640Shselasky if (p->opt.buffer_size != 0) 242335640Shselasky v = p->opt.buffer_size; 243335640Shselasky else 244335640Shselasky v = 64 * 1024; /* default to 64K buffer size */ 245335640Shselasky (void)setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&v, sizeof(v)); 246335640Shselasky /* 247335640Shselasky * XXX hack - map device name to link layer type 248335640Shselasky */ 249335640Shselasky if (strncmp("et", p->opt.device, 2) == 0 || /* Challenge 10 Mbit */ 250335640Shselasky strncmp("ec", p->opt.device, 2) == 0 || /* Indigo/Indy 10 Mbit, 251335640Shselasky O2 10/100 */ 252335640Shselasky strncmp("ef", p->opt.device, 2) == 0 || /* O200/2000 10/100 Mbit */ 253335640Shselasky strncmp("eg", p->opt.device, 2) == 0 || /* Octane/O2xxx/O3xxx Gigabit */ 254335640Shselasky strncmp("gfe", p->opt.device, 3) == 0 || /* GIO 100 Mbit */ 255335640Shselasky strncmp("fxp", p->opt.device, 3) == 0 || /* Challenge VME Enet */ 256335640Shselasky strncmp("ep", p->opt.device, 2) == 0 || /* Challenge 8x10 Mbit EPLEX */ 257335640Shselasky strncmp("vfe", p->opt.device, 3) == 0 || /* Challenge VME 100Mbit */ 258335640Shselasky strncmp("fa", p->opt.device, 2) == 0 || 259335640Shselasky strncmp("qaa", p->opt.device, 3) == 0 || 260335640Shselasky strncmp("cip", p->opt.device, 3) == 0 || 261335640Shselasky strncmp("el", p->opt.device, 2) == 0) { 262335640Shselasky p->linktype = DLT_EN10MB; 263335640Shselasky p->offset = RAW_HDRPAD(sizeof(struct ether_header)); 264335640Shselasky ll_hdrlen = sizeof(struct ether_header); 265335640Shselasky /* 266335640Shselasky * This is (presumably) a real Ethernet capture; give it a 267335640Shselasky * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so 268335640Shselasky * that an application can let you choose it, in case you're 269335640Shselasky * capturing DOCSIS traffic that a Cisco Cable Modem 270335640Shselasky * Termination System is putting out onto an Ethernet (it 271335640Shselasky * doesn't put an Ethernet header onto the wire, it puts raw 272335640Shselasky * DOCSIS frames out on the wire inside the low-level 273335640Shselasky * Ethernet framing). 274335640Shselasky * 275335640Shselasky * XXX - are there any sorts of "fake Ethernet" that have 276335640Shselasky * Ethernet link-layer headers but that *shouldn't offer 277335640Shselasky * DLT_DOCSIS as a Cisco CMTS won't put traffic onto it 278335640Shselasky * or get traffic bridged onto it? "el" is for ATM LANE 279335640Shselasky * Ethernet devices, so that might be the case for them; 280335640Shselasky * the same applies for "qaa" classical IP devices. If 281335640Shselasky * "fa" devices are for FORE SPANS, that'd apply to them 282335640Shselasky * as well; what are "cip" devices - some other ATM 283335640Shselasky * Classical IP devices? 284335640Shselasky */ 285335640Shselasky p->dlt_list = (u_int *) malloc(sizeof(u_int) * 2); 286335640Shselasky /* 287335640Shselasky * If that fails, just leave the list empty. 288335640Shselasky */ 289335640Shselasky if (p->dlt_list != NULL) { 290335640Shselasky p->dlt_list[0] = DLT_EN10MB; 291335640Shselasky p->dlt_list[1] = DLT_DOCSIS; 292335640Shselasky p->dlt_count = 2; 293335640Shselasky } 294335640Shselasky } else if (strncmp("ipg", p->opt.device, 3) == 0 || 295335640Shselasky strncmp("rns", p->opt.device, 3) == 0 || /* O2/200/2000 FDDI */ 296335640Shselasky strncmp("xpi", p->opt.device, 3) == 0) { 297335640Shselasky p->linktype = DLT_FDDI; 298335640Shselasky p->offset = 3; /* XXX yeah? */ 299335640Shselasky ll_hdrlen = 13; 300335640Shselasky } else if (strncmp("ppp", p->opt.device, 3) == 0) { 301335640Shselasky p->linktype = DLT_RAW; 302335640Shselasky ll_hdrlen = 0; /* DLT_RAW meaning "no PPP header, just the IP packet"? */ 303335640Shselasky } else if (strncmp("qfa", p->opt.device, 3) == 0) { 304335640Shselasky p->linktype = DLT_IP_OVER_FC; 305335640Shselasky ll_hdrlen = 24; 306335640Shselasky } else if (strncmp("pl", p->opt.device, 2) == 0) { 307335640Shselasky p->linktype = DLT_RAW; 308335640Shselasky ll_hdrlen = 0; /* Cray UNICOS/mp pseudo link */ 309335640Shselasky } else if (strncmp("lo", p->opt.device, 2) == 0) { 310335640Shselasky p->linktype = DLT_NULL; 311335640Shselasky ll_hdrlen = 4; 312335640Shselasky } else { 313335640Shselasky pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 314335640Shselasky "snoop: unknown physical layer type"); 315335640Shselasky goto bad; 316335640Shselasky } 317335640Shselasky 318335640Shselasky if (p->opt.rfmon) { 319335640Shselasky /* 320335640Shselasky * No monitor mode on Irix (no Wi-Fi devices on 321335640Shselasky * hardware supported by Irix). 322335640Shselasky */ 323335640Shselasky return (PCAP_ERROR_RFMON_NOTSUP); 324335640Shselasky } 325335640Shselasky 326335640Shselasky /* 327335640Shselasky * Turn a negative snapshot value (invalid), a snapshot value of 328335640Shselasky * 0 (unspecified), or a value bigger than the normal maximum 329335640Shselasky * value, into the maximum allowed value. 330335640Shselasky * 331335640Shselasky * If some application really *needs* a bigger snapshot 332335640Shselasky * length, we should just increase MAXIMUM_SNAPLEN. 333335640Shselasky */ 334335640Shselasky if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN) 335335640Shselasky p->snapshot = MAXIMUM_SNAPLEN; 336335640Shselasky 337335640Shselasky#ifdef SIOCGIFMTU 338335640Shselasky /* 339335640Shselasky * XXX - IRIX appears to give you an error if you try to set the 340335640Shselasky * capture length to be greater than the MTU, so let's try to get 341335640Shselasky * the MTU first and, if that succeeds, trim the snap length 342335640Shselasky * to be no greater than the MTU. 343335640Shselasky */ 344335640Shselasky (void)strncpy(ifr.ifr_name, p->opt.device, sizeof(ifr.ifr_name)); 345335640Shselasky if (ioctl(fd, SIOCGIFMTU, (char *)&ifr) < 0) { 346335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 347335640Shselasky errno, "SIOCGIFMTU"); 348335640Shselasky goto bad; 349335640Shselasky } 350335640Shselasky /* 351335640Shselasky * OK, we got it. 352335640Shselasky * 353335640Shselasky * XXX - some versions of IRIX 6.5 define "ifr_mtu" and have an 354335640Shselasky * "ifru_metric" member of the "ifr_ifru" union in an "ifreq" 355335640Shselasky * structure, others don't. 356335640Shselasky * 357335640Shselasky * I've no idea what's going on, so, if "ifr_mtu" isn't defined, 358335640Shselasky * we define it as "ifr_metric", as using that field appears to 359335640Shselasky * work on the versions that lack "ifr_mtu" (and, on those that 360335640Shselasky * don't lack it, "ifru_metric" and "ifru_mtu" are both "int" 361335640Shselasky * members of the "ifr_ifru" union, which suggests that they 362335640Shselasky * may be interchangeable in this case). 363335640Shselasky */ 364335640Shselasky#ifndef ifr_mtu 365335640Shselasky#define ifr_mtu ifr_metric 366335640Shselasky#endif 367335640Shselasky if (p->snapshot > ifr.ifr_mtu + ll_hdrlen) 368335640Shselasky p->snapshot = ifr.ifr_mtu + ll_hdrlen; 369335640Shselasky#endif 370335640Shselasky 371335640Shselasky /* 372335640Shselasky * The argument to SIOCSNOOPLEN is the number of link-layer 373335640Shselasky * payload bytes to capture - it doesn't count link-layer 374335640Shselasky * header bytes. 375335640Shselasky */ 376335640Shselasky snooplen = p->snapshot - ll_hdrlen; 377335640Shselasky if (snooplen < 0) 378335640Shselasky snooplen = 0; 379335640Shselasky if (ioctl(fd, SIOCSNOOPLEN, &snooplen) < 0) { 380335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 381335640Shselasky errno, "SIOCSNOOPLEN"); 382335640Shselasky goto bad; 383335640Shselasky } 384335640Shselasky v = 1; 385335640Shselasky if (ioctl(fd, SIOCSNOOPING, &v) < 0) { 386335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 387335640Shselasky errno, "SIOCSNOOPING"); 388335640Shselasky goto bad; 389335640Shselasky } 390335640Shselasky 391335640Shselasky p->bufsize = 4096; /* XXX */ 392335640Shselasky p->buffer = malloc(p->bufsize); 393335640Shselasky if (p->buffer == NULL) { 394335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 395335640Shselasky errno, "malloc"); 396335640Shselasky goto bad; 397335640Shselasky } 398335640Shselasky 399335640Shselasky /* 400335640Shselasky * "p->fd" is a socket, so "select()" should work on it. 401335640Shselasky */ 402335640Shselasky p->selectable_fd = p->fd; 403335640Shselasky 404335640Shselasky p->read_op = pcap_read_snoop; 405335640Shselasky p->inject_op = pcap_inject_snoop; 406335640Shselasky p->setfilter_op = install_bpf_program; /* no kernel filtering */ 407335640Shselasky p->setdirection_op = NULL; /* Not implemented. */ 408335640Shselasky p->set_datalink_op = NULL; /* can't change data link type */ 409335640Shselasky p->getnonblock_op = pcap_getnonblock_fd; 410335640Shselasky p->setnonblock_op = pcap_setnonblock_fd; 411335640Shselasky p->stats_op = pcap_stats_snoop; 412335640Shselasky 413335640Shselasky return (0); 414335640Shselasky bad: 415335640Shselasky pcap_cleanup_live_common(p); 416335640Shselasky return (PCAP_ERROR); 417335640Shselasky} 418335640Shselasky 419335640Shselaskypcap_t * 420335640Shselaskypcap_create_interface(const char *device _U_, char *ebuf) 421335640Shselasky{ 422335640Shselasky pcap_t *p; 423335640Shselasky 424335640Shselasky p = pcap_create_common(ebuf, sizeof (struct pcap_snoop)); 425335640Shselasky if (p == NULL) 426335640Shselasky return (NULL); 427335640Shselasky 428335640Shselasky p->activate_op = pcap_activate_snoop; 429335640Shselasky return (p); 430335640Shselasky} 431335640Shselasky 432335640Shselasky/* 433335640Shselasky * XXX - there's probably a particular bind error that means "that device 434335640Shselasky * doesn't support snoop"; if so, we should try a bind and use that. 435335640Shselasky */ 436335640Shselaskystatic int 437335640Shselaskycan_be_bound(const char *name _U_) 438335640Shselasky{ 439335640Shselasky return (1); 440335640Shselasky} 441335640Shselasky 442335640Shselaskystatic int 443335640Shselaskyget_if_flags(const char *name _U_, bpf_u_int32 *flags _U_, char *errbuf _U_) 444335640Shselasky{ 445335640Shselasky /* 446335640Shselasky * Nothing we can do. 447335640Shselasky * XXX - is there a way to find out whether an adapter has 448335640Shselasky * something plugged into it? 449335640Shselasky */ 450335640Shselasky return (0); 451335640Shselasky} 452335640Shselasky 453335640Shselaskyint 454335640Shselaskypcap_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf) 455335640Shselasky{ 456335640Shselasky return (pcap_findalldevs_interfaces(devlistp, errbuf, can_be_bound, 457335640Shselasky get_if_flags)); 458335640Shselasky} 459335640Shselasky 460335640Shselasky/* 461335640Shselasky * Libpcap version string. 462335640Shselasky */ 463335640Shselaskyconst char * 464335640Shselaskypcap_lib_version(void) 465335640Shselasky{ 466335640Shselasky return (PCAP_VERSION_STRING); 467335640Shselasky} 468