/* * There's an ack *if* there's an error. */ static int dlrawdatareq(int fd, const u_char *datap, int datalen) { struct strbuf ctl, data; long buf[MAXDLBUF]; /* XXX - char? */ union DL_primitives *dlp; int dlen; dlp = MAKE_DL_PRIMITIVES(buf); dlp->dl_primitive = DL_HP_RAWDATA_REQ; dlen = DL_HP_RAWDATA_REQ_SIZE; /* * HP's documentation doesn't appear to show us supplying any * address pointed to by the control part of the message. * I think that's what raw mode means - you just send the raw * packet, you don't specify where to send it to, as that's * implied by the destination address. */ ctl.maxlen = 0; ctl.len = dlen; ctl.buf = (void *)buf; data.maxlen = 0; data.len = datalen; data.buf = (void *)datap; return (putmsg(fd, &ctl, &data, 0)); }
static int pcap_activate_dlpi(pcap_t *p) { #ifdef DL_HP_RAWDLS struct pcap_dlpi *pd = p->priv; #endif int status = 0; int retv; register char *cp; int ppa; #ifdef HAVE_SOLARIS int isatm = 0; #endif register dl_info_ack_t *infop; #ifdef HAVE_SYS_BUFMOD_H bpf_u_int32 ss; #ifdef HAVE_SOLARIS register char *release; bpf_u_int32 osmajor, osminor, osmicro; #endif #endif bpf_u_int32 buf[MAXDLBUF]; char dname[100]; #ifndef HAVE_DEV_DLPI char dname2[100]; #endif #ifdef HAVE_DEV_DLPI /* ** Remove any "/dev/" on the front of the device. */ cp = strrchr(p->opt.source, '/'); if (cp == NULL) strlcpy(dname, p->opt.source, sizeof(dname)); else strlcpy(dname, cp + 1, sizeof(dname)); /* * Split the device name into a device type name and a unit number; * chop off the unit number, so "dname" is just a device type name. */ cp = split_dname(dname, &ppa, p->errbuf); if (cp == NULL) { status = PCAP_ERROR_NO_SUCH_DEVICE; goto bad; } *cp = '\0'; /* * Use "/dev/dlpi" as the device. * * XXX - HP's DLPI Programmer's Guide for HP-UX 11.00 says that * the "dl_mjr_num" field is for the "major number of interface * driver"; that's the major of "/dev/dlpi" on the system on * which I tried this, but there may be DLPI devices that * use a different driver, in which case we may need to * search "/dev" for the appropriate device with that major * device number, rather than hardwiring "/dev/dlpi". */ cp = "/dev/dlpi"; if ((p->fd = open(cp, O_RDWR)) < 0) { if (errno == EPERM || errno == EACCES) status = PCAP_ERROR_PERM_DENIED; else status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", cp, pcap_strerror(errno)); goto bad; } #ifdef DL_HP_RAWDLS /* * XXX - HP-UX 10.20 and 11.xx don't appear to support sending and * receiving packets on the same descriptor - you need separate * descriptors for sending and receiving, bound to different SAPs. * * If the open fails, we just leave -1 in "pd->send_fd" and reject * attempts to send packets, just as if, in pcap-bpf.c, we fail * to open the BPF device for reading and writing, we just try * to open it for reading only and, if that succeeds, just let * the send attempts fail. */ pd->send_fd = open(cp, O_RDWR); #endif /* * Get a table of all PPAs for that device, and search that * table for the specified device type name and unit number. */ ppa = get_dlpi_ppa(p->fd, dname, ppa, p->errbuf); if (ppa < 0) { status = ppa; goto bad; } #else /* * If the device name begins with "/", assume it begins with * the pathname of the directory containing the device to open; * otherwise, concatenate the device directory name and the * device name. */ if (*p->opt.source == '/') strlcpy(dname, p->opt.source, sizeof(dname)); else snprintf(dname, sizeof(dname), "%s/%s", PCAP_DEV_PREFIX, p->opt.source); /* * Get the unit number, and a pointer to the end of the device * type name. */ cp = split_dname(dname, &ppa, p->errbuf); if (cp == NULL) { status = PCAP_ERROR_NO_SUCH_DEVICE; goto bad; } /* * Make a copy of the device pathname, and then remove the unit * number from the device pathname. */ strlcpy(dname2, dname, sizeof(dname)); *cp = '\0'; /* Try device without unit number */ if ((p->fd = open(dname, O_RDWR)) < 0) { if (errno != ENOENT) { if (errno == EPERM || errno == EACCES) status = PCAP_ERROR_PERM_DENIED; else status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", dname, pcap_strerror(errno)); goto bad; } /* Try again with unit number */ if ((p->fd = open(dname2, O_RDWR)) < 0) { if (errno == ENOENT) { status = PCAP_ERROR_NO_SUCH_DEVICE; /* * We provide an error message even * for this error, for diagnostic * purposes (so that, for example, * the app can show the message if the * user requests it). * * In it, we just report "No DLPI device * found" with the device name, so people * don't get confused and think, for example, * that if they can't capture on "lo0" * on Solaris the fix is to change libpcap * (or the application that uses it) to * look for something other than "/dev/lo0", * as the fix is to look for an operating * system other than Solaris - you just * *can't* capture on a loopback interface * on Solaris, the lack of a DLPI device * for the loopback interface is just a * symptom of that inability. */ snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: No DLPI device found", p->opt.source); } else { if (errno == EPERM || errno == EACCES) status = PCAP_ERROR_PERM_DENIED; else status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", dname2, pcap_strerror(errno)); } goto bad; } /* XXX Assume unit zero */ ppa = 0; } #endif /* ** Attach if "style 2" provider */ if (dlinforeq(p->fd, p->errbuf) < 0 || dlinfoack(p->fd, (char *)buf, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } infop = &(MAKE_DL_PRIMITIVES(buf))->info_ack; #ifdef HAVE_SOLARIS if (infop->dl_mac_type == DL_IPATM) isatm = 1; #endif if (infop->dl_provider_style == DL_STYLE2) { retv = dl_doattach(p->fd, ppa, p->errbuf); if (retv < 0) { status = retv; goto bad; } #ifdef DL_HP_RAWDLS if (pd->send_fd >= 0) { retv = dl_doattach(pd->send_fd, ppa, p->errbuf); if (retv < 0) { status = retv; goto bad; } } #endif } if (p->opt.rfmon) { /* * This device exists, but we don't support monitor mode * any platforms that support DLPI. */ status = PCAP_ERROR_RFMON_NOTSUP; goto bad; } #ifdef HAVE_DLPI_PASSIVE /* * Enable Passive mode to be able to capture on aggregated link. * Not supported in all Solaris versions. */ dlpassive(p->fd, p->errbuf); #endif /* ** Bind (defer if using HP-UX 9 or HP-UX 10.20 or later, totally ** skip if using SINIX) */ #if !defined(HAVE_HPUX9) && !defined(HAVE_HPUX10_20_OR_LATER) && !defined(sinix) #ifdef _AIX /* ** AIX. ** According to IBM's AIX Support Line, the dl_sap value ** should not be less than 0x600 (1536) for standard Ethernet. ** However, we seem to get DL_BADADDR - "DLSAP addr in improper ** format or invalid" - errors if we use 1537 on the "tr0" ** device, which, given that its name starts with "tr" and that ** it's IBM, probably means a Token Ring device. (Perhaps we ** need to use 1537 on "/dev/dlpi/en" because that device is for ** D/I/X Ethernet, the "SAP" is actually an Ethernet type, and ** it rejects invalid Ethernet types.) ** ** So if 1537 fails, we try 2, as Hyung Sik Yoon of IBM Korea ** says that works on Token Ring (he says that 0 does *not* ** work; perhaps that's considered an invalid LLC SAP value - I ** assume the SAP value in a DLPI bind is an LLC SAP for network ** types that use 802.2 LLC). */ if ((dlbindreq(p->fd, 1537, p->errbuf) < 0 && dlbindreq(p->fd, 2, p->errbuf) < 0) || dlbindack(p->fd, (char *)buf, p->errbuf, NULL) < 0) { status = PCAP_ERROR; goto bad; } #elif defined(DL_HP_RAWDLS) /* ** HP-UX 10.0x and 10.1x. */ if (dl_dohpuxbind(p->fd, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } if (pd->send_fd >= 0) { /* ** XXX - if this fails, just close send_fd and ** set it to -1, so that you can't send but can ** still receive? */ if (dl_dohpuxbind(pd->send_fd, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } } #else /* neither AIX nor HP-UX */ /* ** Not Sinix, and neither AIX nor HP-UX - Solaris, and any other ** OS using DLPI. **/ if (dlbindreq(p->fd, 0, p->errbuf) < 0 || dlbindack(p->fd, (char *)buf, p->errbuf, NULL) < 0) { status = PCAP_ERROR; goto bad; } #endif /* AIX vs. HP-UX vs. other */ #endif /* !HP-UX 9 and !HP-UX 10.20 or later and !SINIX */ #ifdef HAVE_SOLARIS if (isatm) { /* ** Have to turn on some special ATM promiscuous mode ** for SunATM. ** Do *NOT* turn regular promiscuous mode on; it doesn't ** help, and may break things. */ if (strioctl(p->fd, A_PROMISCON_REQ, 0, NULL) < 0) { status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A_PROMISCON_REQ: %s", pcap_strerror(errno)); goto bad; } } else #endif if (p->opt.promisc) { /* ** Enable promiscuous (not necessary on send FD) */ retv = dlpromiscon(p, DL_PROMISC_PHYS); if (retv < 0) { if (retv == PCAP_ERROR_PERM_DENIED) status = PCAP_ERROR_PROMISC_PERM_DENIED; else status = retv; goto bad; } /* ** Try to enable multicast (you would have thought ** promiscuous would be sufficient). (Skip if using ** HP-UX or SINIX) (Not necessary on send FD) */ #if !defined(__hpux) && !defined(sinix) retv = dlpromiscon(p, DL_PROMISC_MULTI); if (retv < 0) status = PCAP_WARNING; #endif } /* ** Try to enable SAP promiscuity (when not in promiscuous mode ** when using HP-UX, when not doing SunATM on Solaris, and never ** under SINIX) (Not necessary on send FD) */ #ifndef sinix #if defined(__hpux) /* HP-UX - only do this when not in promiscuous mode */ if (!p->opt.promisc) { #elif defined(HAVE_SOLARIS) /* Solaris - don't do this on SunATM devices */ if (!isatm) { #else /* Everything else (except for SINIX) - always do this */ { #endif retv = dlpromiscon(p, DL_PROMISC_SAP); if (retv < 0) { if (p->opt.promisc) { /* * Not fatal, since the DL_PROMISC_PHYS mode * worked. * * Report it as a warning, however. */ status = PCAP_WARNING; } else { /* * Fatal. */ status = retv; goto bad; } } } #endif /* sinix */ /* ** HP-UX 9, and HP-UX 10.20 or later, must bind after setting ** promiscuous options. */ #if defined(HAVE_HPUX9) || defined(HAVE_HPUX10_20_OR_LATER) if (dl_dohpuxbind(p->fd, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } /* ** We don't set promiscuous mode on the send FD, but we'll defer ** binding it anyway, just to keep the HP-UX 9/10.20 or later ** code together. */ if (pd->send_fd >= 0) { /* ** XXX - if this fails, just close send_fd and ** set it to -1, so that you can't send but can ** still receive? */ if (dl_dohpuxbind(pd->send_fd, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } } #endif /* ** Determine link type ** XXX - get SAP length and address length as well, for use ** when sending packets. */ if (dlinforeq(p->fd, p->errbuf) < 0 || dlinfoack(p->fd, (char *)buf, p->errbuf) < 0) { status = PCAP_ERROR; goto bad; } infop = &(MAKE_DL_PRIMITIVES(buf))->info_ack; if (pcap_process_mactype(p, infop->dl_mac_type) != 0) { status = PCAP_ERROR; goto bad; } #ifdef DLIOCRAW /* ** This is a non standard SunOS hack to get the full raw link-layer ** header. */ if (strioctl(p->fd, DLIOCRAW, 0, NULL) < 0) { status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "DLIOCRAW: %s", pcap_strerror(errno)); goto bad; } #endif #ifdef HAVE_SYS_BUFMOD_H ss = p->snapshot; /* ** There is a bug in bufmod(7). When dealing with messages of ** less than snaplen size it strips data from the beginning not ** the end. ** ** This bug is fixed in 5.3.2. Also, there is a patch available. ** Ask for bugid 1149065. */ #ifdef HAVE_SOLARIS release = get_release(&osmajor, &osminor, &osmicro); if (osmajor == 5 && (osminor <= 2 || (osminor == 3 && osmicro < 2)) && getenv("BUFMOD_FIXED") == NULL) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "WARNING: bufmod is broken in SunOS %s; ignoring snaplen.", release); ss = 0; status = PCAP_WARNING; } #endif /* Push and configure bufmod. */ if (pcap_conf_bufmod(p, ss) != 0) { status = PCAP_ERROR; goto bad; } #endif /* ** As the last operation flush the read side. */ if (ioctl(p->fd, I_FLUSH, FLUSHR) != 0) { status = PCAP_ERROR; snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "FLUSHR: %s", pcap_strerror(errno)); goto bad; } /* Allocate data buffer. */ if (pcap_alloc_databuf(p) != 0) { status = PCAP_ERROR; goto bad; } /* * Success. * * "p->fd" is an FD for a STREAMS device, so "select()" and * "poll()" should work on it. */ p->selectable_fd = p->fd; p->read_op = pcap_read_dlpi; p->inject_op = pcap_inject_dlpi; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->setdirection_op = NULL; /* Not implemented.*/ p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; p->setnonblock_op = pcap_setnonblock_fd; p->stats_op = pcap_stats_dlpi; p->cleanup_op = pcap_cleanup_dlpi; return (status); bad: pcap_cleanup_dlpi(p); return (status); } /* * Split a device name into a device type name and a unit number; * return the a pointer to the beginning of the unit number, which * is the end of the device type name, and set "*unitp" to the unit * number. * * Returns NULL on error, and fills "ebuf" with an error message. */ static char * split_dname(char *device, int *unitp, char *ebuf) { char *cp; char *eos; long unit; /* * Look for a number at the end of the device name string. */ cp = device + strlen(device) - 1; if (*cp < '0' || *cp > '9') { snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s missing unit number", device); return (NULL); } /* Digits at end of string are unit number */ while (cp-1 >= device && *(cp-1) >= '0' && *(cp-1) <= '9') cp--; errno = 0; unit = strtol(cp, &eos, 10); if (*eos != '\0') { snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s bad unit number", device); return (NULL); } if (errno == ERANGE || unit > INT_MAX) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number too large", device); return (NULL); } if (unit < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s unit number is negative", device); return (NULL); } *unitp = (int)unit; return (cp); } static int dl_doattach(int fd, int ppa, char *ebuf) { dl_attach_req_t req; bpf_u_int32 buf[MAXDLBUF]; int err; req.dl_primitive = DL_ATTACH_REQ; req.dl_ppa = ppa; if (send_request(fd, (char *)&req, sizeof(req), "attach", ebuf) < 0) return (PCAP_ERROR); err = dlokack(fd, "attach", (char *)buf, ebuf); if (err < 0) return (err); return (0); }
static int recv_ack(int fd, int size, const char *what, char *bufp, char *ebuf, int *uerror) { union DL_primitives *dlp; struct strbuf ctl; int flags; /* * Clear out "*uerror", so it's only set for DL_ERROR_ACK/DL_SYSERR, * making that the only place where EBUSY is treated specially. */ if (uerror != NULL) *uerror = 0; ctl.maxlen = MAXDLBUF; ctl.len = 0; ctl.buf = bufp; flags = 0; if (getmsg(fd, &ctl, (struct strbuf*)NULL, &flags) < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s getmsg: %s", what, pcap_strerror(errno)); return (PCAP_ERROR); } dlp = MAKE_DL_PRIMITIVES(ctl.buf); switch (dlp->dl_primitive) { case DL_INFO_ACK: case DL_BIND_ACK: case DL_OK_ACK: #ifdef DL_HP_PPA_ACK case DL_HP_PPA_ACK: #endif /* These are OK */ break; case DL_ERROR_ACK: switch (dlp->error_ack.dl_errno) { case DL_SYSERR: if (uerror != NULL) *uerror = dlp->error_ack.dl_unix_errno; snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: UNIX error - %s", what, pcap_strerror(dlp->error_ack.dl_unix_errno)); if (dlp->error_ack.dl_unix_errno == EPERM || dlp->error_ack.dl_unix_errno == EACCES) return (PCAP_ERROR_PERM_DENIED); break; default: snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: %s", what, dlstrerror(dlp->error_ack.dl_errno)); if (dlp->error_ack.dl_errno == DL_BADPPA) return (PCAP_ERROR_NO_SUCH_DEVICE); else if (dlp->error_ack.dl_errno == DL_ACCESS) return (PCAP_ERROR_PERM_DENIED); break; } return (PCAP_ERROR); default: snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: Unexpected primitive ack %s", what, dlprim(dlp->dl_primitive)); return (PCAP_ERROR); } if (ctl.len < size) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "recv_ack: %s: Ack too small (%d < %d)", what, ctl.len, size); return (PCAP_ERROR); } return (ctl.len); }