1 /* 2 * This code is derived from code formerly in pcap-dlpi.c, originally 3 * contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk), University College 4 * London, and subsequently modified by Guy Harris (guy@alum.mit.edu), 5 * Mark Pizzolato <List-tcpdump-workers@subscriptions.pizzolato.net>, 6 * Mark C. Brown (mbrown@hp.com), and Sagun Shakya <Sagun.Shakya@Sun.COM>. 7 */ 8 9 /* 10 * This file contains dlpi/libdlpi related common functions used 11 * by pcap-[dlpi,libdlpi].c. 12 */ 13 #ifndef lint 14 static const char rcsid[] _U_ = 15 "@(#) $Header: /tcpdump/master/libpcap/dlpisubs.c,v 1.3 2008-12-02 16:40:19 guy Exp $ (LBL)"; 16 #endif 17 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #ifndef DL_IPATM 23 #define DL_IPATM 0x12 /* ATM Classical IP interface */ 24 #endif 25 26 #ifdef HAVE_SYS_BUFMOD_H 27 /* 28 * Size of a bufmod chunk to pass upstream; that appears to be the 29 * biggest value to which you can set it, and setting it to that value 30 * (which is bigger than what appears to be the Solaris default of 8192) 31 * reduces the number of packet drops. 32 */ 33 #define CHUNKSIZE 65536 34 35 /* 36 * Size of the buffer to allocate for packet data we read; it must be 37 * large enough to hold a chunk. 38 */ 39 #define PKTBUFSIZE CHUNKSIZE 40 41 #else /* HAVE_SYS_BUFMOD_H */ 42 43 /* 44 * Size of the buffer to allocate for packet data we read; this is 45 * what the value used to be - there's no particular reason why it 46 * should be tied to MAXDLBUF, but we'll leave it as this for now. 47 */ 48 #define MAXDLBUF 8192 49 #define PKTBUFSIZE (MAXDLBUF * sizeof(bpf_u_int32)) 50 51 #endif 52 53 #include <sys/types.h> 54 #include <sys/time.h> 55 #ifdef HAVE_SYS_BUFMOD_H 56 #include <sys/bufmod.h> 57 #endif 58 #include <sys/dlpi.h> 59 #include <sys/stream.h> 60 61 #include <errno.h> 62 #include <memory.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <stropts.h> 67 #include <unistd.h> 68 69 #include "pcap-int.h" 70 #include "dlpisubs.h" 71 72 #ifdef HAVE_SYS_BUFMOD_H 73 static void pcap_stream_err(const char *, int, char *); 74 #endif 75 76 /* 77 * Get the packet statistics. 78 */ 79 int 80 pcap_stats_dlpi(pcap_t *p, struct pcap_stat *ps) 81 { 82 83 /* 84 * "ps_recv" counts packets handed to the filter, not packets 85 * that passed the filter. As filtering is done in userland, 86 * this would not include packets dropped because we ran out 87 * of buffer space; in order to make this more like other 88 * platforms (Linux 2.4 and later, BSDs with BPF), where the 89 * "packets received" count includes packets received but dropped 90 * due to running out of buffer space, and to keep from confusing 91 * applications that, for example, compute packet drop percentages, 92 * we also make it count packets dropped by "bufmod" (otherwise we 93 * might run the risk of the packet drop count being bigger than 94 * the received-packet count). 95 * 96 * "ps_drop" counts packets dropped by "bufmod" because of 97 * flow control requirements or resource exhaustion; it doesn't 98 * count packets dropped by the interface driver, or packets 99 * dropped upstream. As filtering is done in userland, it counts 100 * packets regardless of whether they would've passed the filter. 101 * 102 * These statistics don't include packets not yet read from 103 * the kernel by libpcap, but they may include packets not 104 * yet read from libpcap by the application. 105 */ 106 *ps = p->md.stat; 107 108 /* 109 * Add in the drop count, as per the above comment. 110 */ 111 ps->ps_recv += ps->ps_drop; 112 return (0); 113 } 114 115 /* 116 * Loop through the packets and call the callback for each packet. 117 * Return the number of packets read. 118 */ 119 int 120 pcap_process_pkts(pcap_t *p, pcap_handler callback, u_char *user, 121 int count, u_char *bufp, int len) 122 { 123 int n, caplen, origlen; 124 u_char *ep, *pk; 125 struct pcap_pkthdr pkthdr; 126 #ifdef HAVE_SYS_BUFMOD_H 127 struct sb_hdr *sbp; 128 #ifdef LBL_ALIGN 129 struct sb_hdr sbhdr; 130 #endif 131 #endif 132 133 /* Loop through packets */ 134 ep = bufp + len; 135 n = 0; 136 137 #ifdef HAVE_SYS_BUFMOD_H 138 while (bufp < ep) { 139 /* 140 * Has "pcap_breakloop()" been called? 141 * If so, return immediately - if we haven't read any 142 * packets, clear the flag and return -2 to indicate 143 * that we were told to break out of the loop, otherwise 144 * leave the flag set, so that the *next* call will break 145 * out of the loop without having read any packets, and 146 * return the number of packets we've processed so far. 147 */ 148 if (p->break_loop) { 149 if (n == 0) { 150 p->break_loop = 0; 151 return (-2); 152 } else { 153 p->bp = bufp; 154 p->cc = ep - bufp; 155 return (n); 156 } 157 } 158 #ifdef LBL_ALIGN 159 if ((long)bufp & 3) { 160 sbp = &sbhdr; 161 memcpy(sbp, bufp, sizeof(*sbp)); 162 } else 163 #endif 164 sbp = (struct sb_hdr *)bufp; 165 p->md.stat.ps_drop = sbp->sbh_drops; 166 pk = bufp + sizeof(*sbp); 167 bufp += sbp->sbh_totlen; 168 origlen = sbp->sbh_origlen; 169 caplen = sbp->sbh_msglen; 170 #else 171 origlen = len; 172 caplen = min(p->snapshot, len); 173 pk = bufp; 174 bufp += caplen; 175 #endif 176 ++p->md.stat.ps_recv; 177 if (bpf_filter(p->fcode.bf_insns, pk, origlen, caplen)) { 178 #ifdef HAVE_SYS_BUFMOD_H 179 pkthdr.ts.tv_sec = sbp->sbh_timestamp.tv_sec; 180 pkthdr.ts.tv_usec = sbp->sbh_timestamp.tv_usec; 181 #else 182 (void) gettimeofday(&pkthdr.ts, NULL); 183 #endif 184 pkthdr.len = origlen; 185 pkthdr.caplen = caplen; 186 /* Insure caplen does not exceed snapshot */ 187 if (pkthdr.caplen > p->snapshot) 188 pkthdr.caplen = p->snapshot; 189 (*callback)(user, &pkthdr, pk); 190 if (++n >= count && count >= 0) { 191 p->cc = ep - bufp; 192 p->bp = bufp; 193 return (n); 194 } 195 } 196 #ifdef HAVE_SYS_BUFMOD_H 197 } 198 #endif 199 p->cc = 0; 200 return (n); 201 } 202 203 /* 204 * Process the mac type. Returns -1 if no matching mac type found, otherwise 0. 205 */ 206 int 207 pcap_process_mactype(pcap_t *p, u_int mactype) 208 { 209 int retv = 0; 210 211 switch (mactype) { 212 213 case DL_CSMACD: 214 case DL_ETHER: 215 p->linktype = DLT_EN10MB; 216 p->offset = 2; 217 /* 218 * This is (presumably) a real Ethernet capture; give it a 219 * link-layer-type list with DLT_EN10MB and DLT_DOCSIS, so 220 * that an application can let you choose it, in case you're 221 * capturing DOCSIS traffic that a Cisco Cable Modem 222 * Termination System is putting out onto an Ethernet (it 223 * doesn't put an Ethernet header onto the wire, it puts raw 224 * DOCSIS frames out on the wire inside the low-level 225 * Ethernet framing). 226 */ 227 p->dlt_list = (u_int *)malloc(sizeof(u_int) * 2); 228 /* 229 * If that fails, just leave the list empty. 230 */ 231 if (p->dlt_list != NULL) { 232 p->dlt_list[0] = DLT_EN10MB; 233 p->dlt_list[1] = DLT_DOCSIS; 234 p->dlt_count = 2; 235 } 236 break; 237 238 case DL_FDDI: 239 p->linktype = DLT_FDDI; 240 p->offset = 3; 241 break; 242 243 case DL_TPR: 244 /* XXX - what about DL_TPB? Is that Token Bus? */ 245 p->linktype = DLT_IEEE802; 246 p->offset = 2; 247 break; 248 249 #ifdef HAVE_SOLARIS 250 case DL_IPATM: 251 p->linktype = DLT_SUNATM; 252 p->offset = 0; /* works for LANE and LLC encapsulation */ 253 break; 254 #endif 255 256 default: 257 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown mactype %u", 258 mactype); 259 retv = -1; 260 } 261 262 return (retv); 263 } 264 265 #ifdef HAVE_SYS_BUFMOD_H 266 /* 267 * Push and configure the buffer module. Returns -1 for error, otherwise 0. 268 */ 269 int 270 pcap_conf_bufmod(pcap_t *p, int snaplen, int timeout) 271 { 272 int retv = 0; 273 274 bpf_u_int32 ss, chunksize; 275 276 /* Non-standard call to get the data nicely buffered. */ 277 if (ioctl(p->fd, I_PUSH, "bufmod") != 0) { 278 pcap_stream_err("I_PUSH bufmod", errno, p->errbuf); 279 retv = -1; 280 } 281 282 ss = snaplen; 283 if (ss > 0 && 284 strioctl(p->fd, SBIOCSSNAP, sizeof(ss), (char *)&ss) != 0) { 285 pcap_stream_err("SBIOCSSNAP", errno, p->errbuf); 286 retv = -1; 287 } 288 289 /* Set up the bufmod timeout. */ 290 if (timeout != 0) { 291 struct timeval to; 292 293 to.tv_sec = timeout / 1000; 294 to.tv_usec = (timeout * 1000) % 1000000; 295 if (strioctl(p->fd, SBIOCSTIME, sizeof(to), (char *)&to) != 0) { 296 pcap_stream_err("SBIOCSTIME", errno, p->errbuf); 297 retv = -1; 298 } 299 } 300 301 /* Set the chunk length. */ 302 chunksize = CHUNKSIZE; 303 if (strioctl(p->fd, SBIOCSCHUNK, sizeof(chunksize), (char *)&chunksize) 304 != 0) { 305 pcap_stream_err("SBIOCSCHUNKP", errno, p->errbuf); 306 retv = -1; 307 } 308 309 return (retv); 310 } 311 #endif /* HAVE_SYS_BUFMOD_H */ 312 313 /* 314 * Allocate data buffer. Returns -1 if memory allocation fails, else 0. 315 */ 316 int 317 pcap_alloc_databuf(pcap_t *p) 318 { 319 p->bufsize = PKTBUFSIZE; 320 p->buffer = (u_char *)malloc(p->bufsize + p->offset); 321 if (p->buffer == NULL) { 322 strlcpy(p->errbuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); 323 return (-1); 324 } 325 326 return (0); 327 } 328 329 /* 330 * Issue a STREAMS I_STR ioctl. Returns -1 on error, otherwise 331 * length of returned data on success. 332 */ 333 int 334 strioctl(int fd, int cmd, int len, char *dp) 335 { 336 struct strioctl str; 337 int retv; 338 339 str.ic_cmd = cmd; 340 str.ic_timout = -1; 341 str.ic_len = len; 342 str.ic_dp = dp; 343 if ((retv = ioctl(fd, I_STR, &str)) < 0) 344 return (retv); 345 346 return (str.ic_len); 347 } 348 349 #ifdef HAVE_SYS_BUFMOD_H 350 /* 351 * Write stream error message to errbuf. 352 */ 353 static void 354 pcap_stream_err(const char *func, int err, char *errbuf) 355 { 356 snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", func, pcap_strerror(err)); 357 } 358 #endif 359