1335640Shselasky/* 2335640Shselasky * This code is derived from code formerly in pcap-dlpi.c, originally 3335640Shselasky * contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), University College 4335640Shselasky * London, and subsequently modified by Guy Harris (guy@alum.mit.edu), 5335640Shselasky * Mark Pizzolato <List-tcpdump-workers@subscriptions.pizzolato.net>, 6335640Shselasky * Mark C. Brown (mbrown@hp.com), and Sagun Shakya <Sagun.Shakya@Sun.COM>. 7335640Shselasky */ 8335640Shselasky 9335640Shselasky/* 10335640Shselasky * This file contains dlpi/libdlpi related common functions used 11335640Shselasky * by pcap-[dlpi,libdlpi].c. 12335640Shselasky */ 13335640Shselasky 14335640Shselasky#ifdef HAVE_CONFIG_H 15335640Shselasky#include <config.h> 16335640Shselasky#endif 17335640Shselasky 18335640Shselasky#ifndef DL_IPATM 19335640Shselasky#define DL_IPATM 0x12 /* ATM Classical IP interface */ 20335640Shselasky#endif 21335640Shselasky 22335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 23335640Shselasky /* 24335640Shselasky * Size of a bufmod chunk to pass upstream; that appears to be the 25335640Shselasky * biggest value to which you can set it, and setting it to that value 26335640Shselasky * (which is bigger than what appears to be the Solaris default of 8192) 27335640Shselasky * reduces the number of packet drops. 28335640Shselasky */ 29335640Shselasky#define CHUNKSIZE 65536 30335640Shselasky 31335640Shselasky /* 32335640Shselasky * Size of the buffer to allocate for packet data we read; it must be 33335640Shselasky * large enough to hold a chunk. 34335640Shselasky */ 35335640Shselasky#define PKTBUFSIZE CHUNKSIZE 36335640Shselasky 37335640Shselasky#else /* HAVE_SYS_BUFMOD_H */ 38335640Shselasky 39335640Shselasky /* 40335640Shselasky * Size of the buffer to allocate for packet data we read; this is 41335640Shselasky * what the value used to be - there's no particular reason why it 42335640Shselasky * should be tied to MAXDLBUF, but we'll leave it as this for now. 43335640Shselasky */ 44335640Shselasky#define MAXDLBUF 8192 45335640Shselasky#define PKTBUFSIZE (MAXDLBUF * sizeof(bpf_u_int32)) 46335640Shselasky 47335640Shselasky#endif 48335640Shselasky 49335640Shselasky#include <sys/types.h> 50335640Shselasky#include <sys/time.h> 51335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 52335640Shselasky#include <sys/bufmod.h> 53335640Shselasky#endif 54335640Shselasky#include <sys/dlpi.h> 55335640Shselasky#include <sys/stream.h> 56335640Shselasky 57335640Shselasky#include <errno.h> 58335640Shselasky#include <memory.h> 59335640Shselasky#include <stdio.h> 60335640Shselasky#include <stdlib.h> 61335640Shselasky#include <string.h> 62335640Shselasky#include <stropts.h> 63335640Shselasky#include <unistd.h> 64335640Shselasky 65335640Shselasky#ifdef HAVE_LIBDLPI 66335640Shselasky#include <libdlpi.h> 67335640Shselasky#endif 68335640Shselasky 69335640Shselasky#include "pcap-int.h" 70335640Shselasky#include "dlpisubs.h" 71335640Shselasky 72335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 73335640Shselaskystatic void pcap_stream_err(const char *, int, char *); 74335640Shselasky#endif 75335640Shselasky 76335640Shselasky/* 77335640Shselasky * Get the packet statistics. 78335640Shselasky */ 79335640Shselaskyint 80335640Shselaskypcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps) 81335640Shselasky{ 82335640Shselasky struct pcap_dlpi *pd = p->priv; 83335640Shselasky 84335640Shselasky /* 85335640Shselasky * "ps_recv" counts packets handed to the filter, not packets 86335640Shselasky * that passed the filter. As filtering is done in userland, 87335640Shselasky * this would not include packets dropped because we ran out 88335640Shselasky * of buffer space; in order to make this more like other 89335640Shselasky * platforms (Linux 2.4 and later, BSDs with BPF), where the 90335640Shselasky * "packets received" count includes packets received but dropped 91335640Shselasky * due to running out of buffer space, and to keep from confusing 92335640Shselasky * applications that, for example, compute packet drop percentages, 93335640Shselasky * we also make it count packets dropped by "bufmod" (otherwise we 94335640Shselasky * might run the risk of the packet drop count being bigger than 95335640Shselasky * the received-packet count). 96335640Shselasky * 97335640Shselasky * "ps_drop" counts packets dropped by "bufmod" because of 98335640Shselasky * flow control requirements or resource exhaustion; it doesn't 99335640Shselasky * count packets dropped by the interface driver, or packets 100335640Shselasky * dropped upstream. As filtering is done in userland, it counts 101335640Shselasky * packets regardless of whether they would've passed the filter. 102335640Shselasky * 103335640Shselasky * These statistics don't include packets not yet read from 104335640Shselasky * the kernel by libpcap, but they may include packets not 105335640Shselasky * yet read from libpcap by the application. 106335640Shselasky */ 107335640Shselasky *ps = pd->stat; 108335640Shselasky 109335640Shselasky /* 110335640Shselasky * Add in the drop count, as per the above comment. 111335640Shselasky */ 112335640Shselasky ps->ps_recv += ps->ps_drop; 113335640Shselasky return (0); 114335640Shselasky} 115335640Shselasky 116335640Shselasky/* 117335640Shselasky * Loop through the packets and call the callback for each packet. 118335640Shselasky * Return the number of packets read. 119335640Shselasky */ 120335640Shselaskyint 121335640Shselaskypcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, 122335640Shselasky int count, u_char *bufp, int len) 123335640Shselasky{ 124335640Shselasky struct pcap_dlpi *pd = p->priv; 125335640Shselasky int n, caplen, origlen; 126335640Shselasky u_char *ep, *pk; 127335640Shselasky struct pcap_pkthdr pkthdr; 128335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 129335640Shselasky struct sb_hdr *sbp; 130335640Shselasky#ifdef LBL_ALIGN 131335640Shselasky struct sb_hdr sbhdr; 132335640Shselasky#endif 133335640Shselasky#endif 134335640Shselasky 135335640Shselasky /* Loop through packets */ 136335640Shselasky ep = bufp + len; 137335640Shselasky n = 0; 138335640Shselasky 139335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 140335640Shselasky while (bufp < ep) { 141335640Shselasky /* 142335640Shselasky * Has "pcap_breakloop()" been called? 143335640Shselasky * If so, return immediately - if we haven't read any 144335640Shselasky * packets, clear the flag and return -2 to indicate 145335640Shselasky * that we were told to break out of the loop, otherwise 146335640Shselasky * leave the flag set, so that the *next* call will break 147335640Shselasky * out of the loop without having read any packets, and 148335640Shselasky * return the number of packets we've processed so far. 149335640Shselasky */ 150335640Shselasky if (p->break_loop) { 151335640Shselasky if (n == 0) { 152335640Shselasky p->break_loop = 0; 153335640Shselasky return (-2); 154335640Shselasky } else { 155335640Shselasky p->bp = bufp; 156335640Shselasky p->cc = ep - bufp; 157335640Shselasky return (n); 158335640Shselasky } 159335640Shselasky } 160335640Shselasky#ifdef LBL_ALIGN 161335640Shselasky if ((long)bufp & 3) { 162335640Shselasky sbp = &sbhdr; 163335640Shselasky memcpy(sbp, bufp, sizeof(*sbp)); 164335640Shselasky } else 165335640Shselasky#endif 166335640Shselasky sbp = (struct sb_hdr *)bufp; 167335640Shselasky pd->stat.ps_drop = sbp->sbh_drops; 168335640Shselasky pk = bufp + sizeof(*sbp); 169335640Shselasky bufp += sbp->sbh_totlen; 170335640Shselasky origlen = sbp->sbh_origlen; 171335640Shselasky caplen = sbp->sbh_msglen; 172335640Shselasky#else 173335640Shselasky origlen = len; 174335640Shselasky caplen = min(p->snapshot, len); 175335640Shselasky pk = bufp; 176335640Shselasky bufp += caplen; 177335640Shselasky#endif 178335640Shselasky ++pd->stat.ps_recv; 179335640Shselasky if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) { 180335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 181335640Shselasky pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec; 182335640Shselasky pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec; 183335640Shselasky#else 184335640Shselasky (void) gettimeofday(&pkthdr.ts, NULL); 185335640Shselasky#endif 186335640Shselasky pkthdr.len = origlen; 187335640Shselasky pkthdr.caplen = caplen; 188335640Shselasky /* Insure caplen does not exceed snapshot */ 189335640Shselasky if (pkthdr.caplen > (bpf_u_int32)p->snapshot) 190335640Shselasky pkthdr.caplen = (bpf_u_int32)p->snapshot; 191335640Shselasky (*callback)(user, &pkthdr, pk); 192335640Shselasky if (++n >= count && !PACKET_COUNT_IS_UNLIMITED(count)) { 193335640Shselasky p->cc = ep - bufp; 194335640Shselasky p->bp = bufp; 195335640Shselasky return (n); 196335640Shselasky } 197335640Shselasky } 198335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 199335640Shselasky } 200335640Shselasky#endif 201335640Shselasky p->cc = 0; 202335640Shselasky return (n); 203335640Shselasky} 204335640Shselasky 205335640Shselasky/* 206335640Shselasky * Process the mac type. Returns -1 if no matching mac type found, otherwise 0. 207335640Shselasky */ 208335640Shselaskyint 209335640Shselaskypcap_process_mactype(pcap_t *p, u_int mactype) 210335640Shselasky{ 211335640Shselasky int retv = 0; 212335640Shselasky 213335640Shselasky switch (mactype) { 214335640Shselasky 215335640Shselasky case DL_CSMACD: 216335640Shselasky case DL_ETHER: 217335640Shselasky p->linktype = DLT_EN10MB; 218335640Shselasky p->offset = 2; 219335640Shselasky /* 220335640Shselasky * This is (presumably) a real Ethernet capture; give it a 221335640Shselasky * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so 222335640Shselasky * that an application can let you choose it, in case you're 223335640Shselasky * capturing DOCSIS traffic that a Cisco Cable Modem 224335640Shselasky * Termination System is putting out onto an Ethernet (it 225335640Shselasky * doesn't put an Ethernet header onto the wire, it puts raw 226335640Shselasky * DOCSIS frames out on the wire inside the low-level 227335640Shselasky * Ethernet framing). 228335640Shselasky */ 229335640Shselasky p->dlt_list = (u_int *)malloc(sizeof(u_int) * 2); 230335640Shselasky /* 231335640Shselasky * If that fails, just leave the list empty. 232335640Shselasky */ 233335640Shselasky if (p->dlt_list != NULL) { 234335640Shselasky p->dlt_list[0] = DLT_EN10MB; 235335640Shselasky p->dlt_list[1] = DLT_DOCSIS; 236335640Shselasky p->dlt_count = 2; 237335640Shselasky } 238335640Shselasky break; 239335640Shselasky 240335640Shselasky case DL_FDDI: 241335640Shselasky p->linktype = DLT_FDDI; 242335640Shselasky p->offset = 3; 243335640Shselasky break; 244335640Shselasky 245335640Shselasky case DL_TPR: 246335640Shselasky /* XXX - what about DL_TPB? Is that Token Bus? */ 247335640Shselasky p->linktype = DLT_IEEE802; 248335640Shselasky p->offset = 2; 249335640Shselasky break; 250335640Shselasky 251335640Shselasky#ifdef HAVE_SOLARIS 252335640Shselasky case DL_IPATM: 253335640Shselasky p->linktype = DLT_SUNATM; 254335640Shselasky p->offset = 0; /* works for LANE and LLC encapsulation */ 255335640Shselasky break; 256335640Shselasky#endif 257335640Shselasky 258335640Shselasky#ifdef DL_IPV4 259335640Shselasky case DL_IPV4: 260335640Shselasky p->linktype = DLT_IPV4; 261335640Shselasky p->offset = 0; 262335640Shselasky break; 263335640Shselasky#endif 264335640Shselasky 265335640Shselasky#ifdef DL_IPV6 266335640Shselasky case DL_IPV6: 267335640Shselasky p->linktype = DLT_IPV6; 268335640Shselasky p->offset = 0; 269335640Shselasky break; 270335640Shselasky#endif 271335640Shselasky 272335640Shselasky#ifdef DL_IPNET 273335640Shselasky case DL_IPNET: 274335640Shselasky /* 275335640Shselasky * XXX - DL_IPNET devices default to "raw IP" rather than 276335640Shselasky * "IPNET header"; see 277335640Shselasky * 278335640Shselasky * http://seclists.org/tcpdump/2009/q1/202 279335640Shselasky * 280335640Shselasky * We'd have to do DL_IOC_IPNET_INFO to enable getting 281335640Shselasky * the IPNET header. 282335640Shselasky */ 283335640Shselasky p->linktype = DLT_RAW; 284335640Shselasky p->offset = 0; 285335640Shselasky break; 286335640Shselasky#endif 287335640Shselasky 288335640Shselasky default: 289335640Shselasky pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype 0x%x", 290335640Shselasky mactype); 291335640Shselasky retv = -1; 292335640Shselasky } 293335640Shselasky 294335640Shselasky return (retv); 295335640Shselasky} 296335640Shselasky 297335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 298335640Shselasky/* 299335640Shselasky * Push and configure the buffer module. Returns -1 for error, otherwise 0. 300335640Shselasky */ 301335640Shselaskyint 302335640Shselaskypcap_conf_bufmod(pcap_t *p, int snaplen) 303335640Shselasky{ 304335640Shselasky struct timeval to; 305335640Shselasky bpf_u_int32 ss, chunksize; 306335640Shselasky 307335640Shselasky /* Non-standard call to get the data nicely buffered. */ 308335640Shselasky if (ioctl(p->fd, I_PUSH, "bufmod") != 0) { 309335640Shselasky pcap_stream_err("I_PUSH bufmod", errno, p->errbuf); 310335640Shselasky return (-1); 311335640Shselasky } 312335640Shselasky 313335640Shselasky ss = snaplen; 314335640Shselasky if (ss > 0 && 315335640Shselasky strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) { 316335640Shselasky pcap_stream_err("SBIOCSSNAP", errno, p->errbuf); 317335640Shselasky return (-1); 318335640Shselasky } 319335640Shselasky 320335640Shselasky if (p->opt.immediate) { 321335640Shselasky /* Set the timeout to zero, for immediate delivery. */ 322335640Shselasky to.tv_sec = 0; 323335640Shselasky to.tv_usec = 0; 324335640Shselasky if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) { 325335640Shselasky pcap_stream_err("SBIOCSTIME", errno, p->errbuf); 326335640Shselasky return (-1); 327335640Shselasky } 328335640Shselasky } else { 329335640Shselasky /* Set up the bufmod timeout. */ 330335640Shselasky if (p->opt.timeout != 0) { 331335640Shselasky to.tv_sec = p->opt.timeout / 1000; 332335640Shselasky to.tv_usec = (p->opt.timeout * 1000) % 1000000; 333335640Shselasky if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) { 334335640Shselasky pcap_stream_err("SBIOCSTIME", errno, p->errbuf); 335335640Shselasky return (-1); 336335640Shselasky } 337335640Shselasky } 338335640Shselasky 339335640Shselasky /* Set the chunk length. */ 340335640Shselasky chunksize = CHUNKSIZE; 341335640Shselasky if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize) 342335640Shselasky != 0) { 343335640Shselasky pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf); 344335640Shselasky return (-1); 345335640Shselasky } 346335640Shselasky } 347335640Shselasky 348335640Shselasky return (0); 349335640Shselasky} 350335640Shselasky#endif /* HAVE_SYS_BUFMOD_H */ 351335640Shselasky 352335640Shselasky/* 353335640Shselasky * Allocate data buffer. Returns -1 if memory allocation fails, else 0. 354335640Shselasky */ 355335640Shselaskyint 356335640Shselaskypcap_alloc_databuf(pcap_t *p) 357335640Shselasky{ 358335640Shselasky p->bufsize = PKTBUFSIZE; 359335640Shselasky p->buffer = malloc(p->bufsize + p->offset); 360335640Shselasky if (p->buffer == NULL) { 361335640Shselasky pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, 362335640Shselasky errno, "malloc"); 363335640Shselasky return (-1); 364335640Shselasky } 365335640Shselasky 366335640Shselasky return (0); 367335640Shselasky} 368335640Shselasky 369335640Shselasky/* 370335640Shselasky * Issue a STREAMS I_STR ioctl. Returns -1 on error, otherwise 371335640Shselasky * length of returned data on success. 372335640Shselasky */ 373335640Shselaskyint 374335640Shselaskystrioctl(int fd, int cmd, int len, char *dp) 375335640Shselasky{ 376335640Shselasky struct strioctl str; 377335640Shselasky int retv; 378335640Shselasky 379335640Shselasky str.ic_cmd = cmd; 380335640Shselasky str.ic_timout = -1; 381335640Shselasky str.ic_len = len; 382335640Shselasky str.ic_dp = dp; 383335640Shselasky if ((retv = ioctl(fd, I_STR, &str)) < 0) 384335640Shselasky return (retv); 385335640Shselasky 386335640Shselasky return (str.ic_len); 387335640Shselasky} 388335640Shselasky 389335640Shselasky#ifdef HAVE_SYS_BUFMOD_H 390335640Shselasky/* 391335640Shselasky * Write stream error message to errbuf. 392335640Shselasky */ 393335640Shselaskystatic void 394335640Shselaskypcap_stream_err(const char *func, int err, char *errbuf) 395335640Shselasky{ 396335640Shselasky pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, err, "%s", func); 397335640Shselasky} 398335640Shselasky#endif 399