From 2d2890dd3774c9b1f90828f41d06e4998191d2f7 Mon Sep 17 00:00:00 2001 From: guy Date: Tue, 23 Mar 2004 19:18:04 +0000 Subject: [PATCH] Add support for sending packets; includes contributions from Mark Pizzolato . --- CREDITS | 1 + pcap-bpf.c | 44 +++++++++++++++++++++- pcap-dag.c | 11 +++++- pcap-dlpi.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++- pcap-int.h | 6 ++- pcap-linux.c | 87 +++++++++++++++++++++++++++++++++++++------- pcap-nit.c | 32 +++++++++++++++- pcap-pf.c | 35 ++++++++++++++++-- pcap-snit.c | 44 +++++++++++++++++++++- pcap-snoop.c | 19 +++++++++- pcap-win32.c | 48 ++++++++++++------------ pcap.3 | 37 ++++++++++++++++++- pcap.c | 26 ++++++++++++- pcap.h | 5 ++- savefile.c | 11 +++++- 15 files changed, 453 insertions(+), 54 deletions(-) diff --git a/CREDITS b/CREDITS index 97a90c7..96fb485 100644 --- a/CREDITS +++ b/CREDITS @@ -55,6 +55,7 @@ Additional people who have contributed patches: Love Hörnquist-Åstrand Maciej W. Rozycki Marcus Felipe Pereira + Mark Pizzolato Martin Husemann Mike Wiacek Monroe Williams diff --git a/pcap-bpf.c b/pcap-bpf.c index 8585db1..5da81b1 100644 --- a/pcap-bpf.c +++ b/pcap-bpf.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.73 2003-12-24 08:26:24 itojun Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-bpf.c,v 1.74 2004-03-23 19:18:04 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -292,6 +292,29 @@ pcap_read_bpf(pcap_t *p, int cnt, pcap_handler callback, u_char *user) return (n); } +static int +pcap_inject_bpf(pcap_t *p, const void *buf, size_t size) +{ + int ret; + + /* + * Do a BIOCSHDRCMPLT, if defined, to turn that flag on, so + * the link-layer source address isn't forcibly overwritten? + * (Ignore errors? Return errors if not "sorry, that ioctl + * isn't supported?) + * + * XXX - I seem to remember some packet-sending bug in some + * BSDs - check CVS log for "bpf.c"? + */ + ret = write(p->fd, buf, size); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + #ifdef _AIX static int bpf_odminit(char *errbuf) @@ -467,7 +490,23 @@ bpf_open(pcap_t *p, char *errbuf) */ do { (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++); - fd = open(device, O_RDONLY); + /* + * Initially try a read/write open (to allow the inject + * method to work). If that fails due to permission + * issues, fall back to read-only. This allows a + * non-root user to be granted specific access to pcap + * capabilities via file permissions. + * + * XXX - we should have an API that has a flag that + * controls whether to open read-only or read-write, + * so that denial of permission to send (or inability + * to send, if sending packets isn't supported on + * the device in question) can be indicated at open + * time. + */ + fd = open(device, O_RDWR); + if (fd == -1 && errno == EACCES) + fd = open(device, O_RDONLY); } while (fd < 0 && errno == EBUSY); /* @@ -898,6 +937,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } p->read_op = pcap_read_bpf; + p->inject_op = pcap_inject_bpf; p->setfilter_op = pcap_setfilter_bpf; p->set_datalink_op = pcap_set_datalink_bpf; p->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-dag.c b/pcap-dag.c index b466fc7..c918b3e 100644 --- a/pcap-dag.c +++ b/pcap-dag.c @@ -15,7 +15,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.17 2004-01-30 02:23:53 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dag.c,v 1.18 2004-03-23 19:18:04 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -377,6 +377,14 @@ dag_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) return processed; } +static int +dag_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +{ + strlcpy(p->errbuf, "Sending packets isn't supported on DAG cards", + PCAP_ERRBUF_SIZE); + return (-1); +} + /* * Get a handle for a live capture from the given DAG device. Passing a NULL * device will result in a failure. The promisc flag is ignored because DAG @@ -503,6 +511,7 @@ dag_open_live(const char *device, int snaplen, int promisc, int to_ms, char *ebu #endif handle->read_op = dag_read; + handle->inject_op = dag_inject; handle->setfilter_op = dag_setfilter; handle->set_datalink_op = dag_set_datalink; handle->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-dlpi.c b/pcap-dlpi.c index 20c1fbb..722b360 100644 --- a/pcap-dlpi.c +++ b/pcap-dlpi.c @@ -38,7 +38,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.95 2003-12-18 23:32:32 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-dlpi.c,v 1.96 2004-03-23 19:18:05 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -220,6 +220,11 @@ pcap_read_dlpi(pcap_t *p, int cnt, pcap_handler callback, u_char *user) p->break_loop = 0; return (-2); } + /* + * XXX - check for the DLPI primitive, which + * would be DL_HP_RAWDATA_IND on HP-UX + * if we're in raw mode? + */ if (getmsg(p->fd, &ctl, &data, &flags) < 0) { /* Don't choke when we get ptraced */ if (errno == EINTR) { @@ -306,6 +311,23 @@ pcap_read_dlpi(pcap_t *p, int cnt, pcap_handler callback, u_char *user) return (n); } +static int +pcap_inject_dlpi(pcap_t *p, const void *buf, size_t size) +{ + int ret; + + /* + * XXX - use "dlunitdatareq()" on HP-UX. + */ + ret = write(p->fd, buf, size); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + #ifndef DL_IPATM #define DL_IPATM 0x12 /* ATM Classical IP interface */ #endif @@ -573,6 +595,8 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, /* ** Determine link type + ** XXX - get SAP length and address length as well, for use + ** when sending packets. */ if (dlinforeq(p->fd, ebuf) < 0 || dlinfoack(p->fd, (char *)buf, ebuf) < 0) @@ -612,6 +636,9 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, break; case DL_TPR: + /* + * XXX - what about DL_TPB? Is that Token Bus? + */ p->linktype = DLT_IEEE802; p->offset = 2; break; @@ -730,6 +757,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, 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->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; @@ -1194,6 +1222,77 @@ dlinfoack(int fd, char *bufp, char *ebuf) return (recv_ack(fd, DL_INFO_ACK_SIZE, "info", bufp, ebuf)); } +#if 0 +/* + * No ack? + * + * On HP-UX, use DL_HP_RAWDATA_REQ instead, *if* we're in RAW mode. + * There is an ack *IF* there's an error. + * + * XXX - this is not needed on Solaris; we're running in raw mode so + * we can just do a "write()". + * What about, say, AIX? + */ +static int +dlunitdatareq(int fd, u_char *addrp, int addrlen, u_char *datap, int datalen) +{ + struct strbuf ctl, data; + long buf[MAXDLBUF]; /* XXX - char? */ + union DL_primitives *dlp; + + dlp = (union DL_primitives*) buf; + + dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ; + dlp->unitdata_req.dl_dest_addr_length = addrlen; + dlp->unitdata_req.dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE; + dlp->unitdata_req.dl_priority.dl_min = 0; + dlp->unitdata_req.dl_priority.dl_max = 0 + + /* + * XXX - the "address", on Ethernet, is the destination address, + * followed by the link-layer type. What is it for other + * link layers? + * + * The address length "dl_addr_length" from the "info_ack" + * structure is the total length of the link-layer address. + * + * The SAP length "dl_sap_length" is the length of the SAP part + * of the address. + * If positive, the SAP comes before the destination address; + * if negative, the SAP comes after the destination address. + * + * XXX - what about Ethernet vs. 802.3? Is the SAP the Ethertype + * or the DSAP? How can we send both? *Can* we send both on + * the same device? + * + * Note that in raw mode, we send a raw link-layer packet. + * In that mode, can we avoid worrying about this crap? + * + * For ATM (ha ha), we might not be able to send packets, + * even in raw mode, without binding to a VC. (Presumably + * going into SAP promiscuous mode lets us *see* all packets.) + * + * XXX - extract it from the packet to be sent. + */ + memcpy((char *)dlp + DL_UNITDATA_REQ_SIZE, addrp, addrlen); + + ctl.maxlen = 0; + ctl.len = DL_UNITDATA_REQ_SIZE + addrlen; + ctl.buf = buf; + + data.maxlen = 0; + data.len = datalen; + data.buf = datap; + + if (putmsg(fd, &ctl, &data, 0) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "dlunitdatareq: putmsg: %s", pcap_strerror(errno)); + return (-1); + } + return (0); +} +#endif + #ifdef HAVE_SYS_BUFMOD_H static int strioctl(int fd, int cmd, int len, char *dp) diff --git a/pcap-int.h b/pcap-int.h index 9e2dc0b..636d102 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.61 2003-12-21 22:00:10 guy Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap-int.h,v 1.62 2004-03-23 19:18:05 guy Exp $ (LBL) */ #ifndef pcap_int_h @@ -74,13 +74,14 @@ struct pcap_md { u_long TotDrops; /* count of dropped packets */ long TotMissed; /* missed by i/f during this run */ long OrigMissed; /* missed by i/f before this run */ + char *device; /* device name */ #ifdef linux int sock_packet; /* using Linux 2.0 compatible interface */ int timeout; /* timeout specified to pcap_open_live */ int clear_promisc; /* must clear promiscuous mode when we close */ int cooked; /* using SOCK_DGRAM rather than SOCK_RAW */ + int ifindex; /* interface index of device we're bound to */ int lo_ifindex; /* interface index of the loopback device */ - char *device; /* device name */ struct pcap *next; /* list of open promiscuous sock_packet pcaps */ #endif @@ -130,6 +131,7 @@ struct pcap { * Methods. */ int (*read_op)(pcap_t *, int cnt, pcap_handler, u_char *); + int (*inject_op)(pcap_t *, const void *, size_t); int (*setfilter_op)(pcap_t *, struct bpf_program *); int (*set_datalink_op)(pcap_t *, int); int (*getnonblock_op)(pcap_t *, char *); diff --git a/pcap-linux.c b/pcap-linux.c index 12c37f5..9710c36 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -27,7 +27,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.105 2004-01-14 01:56:10 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.106 2004-03-23 19:18:05 guy Exp $ (LBL)"; #endif /* @@ -188,6 +188,7 @@ static int live_open_old(pcap_t *, const char *, int, int, char *); static int live_open_new(pcap_t *, const char *, int, int, char *); static int pcap_read_linux(pcap_t *, int, pcap_handler, u_char *); static int pcap_read_packet(pcap_t *, pcap_handler, u_char *); +static int pcap_inject_linux(pcap_t *, const void *, size_t); static int pcap_stats_linux(pcap_t *, struct pcap_stat *); static int pcap_setfilter_linux(pcap_t *, struct bpf_program *); static void pcap_close_linux(pcap_t *); @@ -404,6 +405,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, handle->selectable_fd = handle->fd; handle->read_op = pcap_read_linux; + handle->inject_op = pcap_inject_linux; handle->setfilter_op = pcap_setfilter_linux; handle->set_datalink_op = NULL; /* can't change data link type */ handle->getnonblock_op = pcap_getnonblock_fd; @@ -672,6 +674,63 @@ pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata) return 1; } +static int +pcap_inject_linux(pcap_t *handle, const void *buf, size_t size) +{ +#ifdef HAVE_PF_PACKET_SOCKETS + struct sockaddr_ll sa_ll; +#endif + struct sockaddr_pkt sa_pkt; + int ret; + +#ifdef HAVE_PF_PACKET_SOCKETS + if (!handle->md.sock_packet)) { + /* PF_PACKET socket */ + if (handle->md.ifindex == -1) { + /* + * Cooked mode - can't send. + * XXX - how do you send on a bound cooked-mode + * socket? + */ + strlcpy(handle->errbuf, + "Sending packets isn't supported in cooked mode", + PCAP_ERRBUF_SIZE); + return (-1); + } + + memset(&sa_ll, 0, sizeof(sa_ll)); + sa_ll.sll_family = AF_PACKET; + sa_ll.sll_ifindex = handle->md.ifindex; + /* + * Do we have to set the hardware address? + */ + sa_ll.sll_protocol = htons(ETH_P_ALL); + + ret = sendto(handle->fd, buf, size, 0, &sa_ll, sizeof(sa_ll)); + if (ret == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); + } +#endif + memset(&sa_pkt, 0, sizeof(sa_pkt)); + sa_pkt.spkt_family = PF_INET; + strcpy(sa_pkt.spkt_device, handle->md.device); + /* + * Do we have to set "spkt_protocol" to the Ethernet protocol? + */ + + ret = sendto(handle->fd, buf, size, 0, &sa, sizeof(sa))); + if (ret == -1) { + snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + /* * Get the statistics for the given packet capture handle. * Reports the number of dropped packets iff the kernel supports @@ -1221,7 +1280,7 @@ live_open_new(pcap_t *handle, const char *device, int promisc, int to_ms, char *ebuf) { #ifdef HAVE_PF_PACKET_SOCKETS - int sock_fd = -1, device_id, arptype; + int sock_fd = -1, arptype; int err; int fatal_err = 0; struct packet_mreq mr; @@ -1341,11 +1400,12 @@ live_open_new(pcap_t *handle, const char *device, int promisc, handle->linktype = DLT_LINUX_SLL; } - device_id = iface_get_id(sock_fd, device, ebuf); - if (device_id == -1) + handle->md.ifindex = iface_get_id(sock_fd, device, ebuf); + if (handle->md.ifindex == -1) break; - if ((err = iface_bind(sock_fd, device_id, ebuf)) < 0) { + if ((err = iface_bind(sock_fd, handle->md.ifindex, + ebuf)) < 0) { if (err == -2) fatal_err = 1; break; @@ -1358,14 +1418,15 @@ live_open_new(pcap_t *handle, const char *device, int promisc, handle->linktype = DLT_LINUX_SLL; /* - * XXX - squelch GCC complaints about - * uninitialized variables; if we can't - * select promiscuous mode on all interfaces, - * we should move the code below into the - * "if (device)" branch of the "if" and - * get rid of the next statement. + * We're not bound to a device. + * XXX - true? Or true only if we're using + * the "any" device? + * For now, we're using this as an indication + * that we can't transmit; stop doing that only + * if we figure out how to transmit in cooked + * mode. */ - device_id = -1; + handle->md.ifindex = -1; } /* @@ -1389,7 +1450,7 @@ live_open_new(pcap_t *handle, const char *device, int promisc, if (device && promisc) { memset(&mr, 0, sizeof(mr)); - mr.mr_ifindex = device_id; + mr.mr_ifindex = handle->md.ifindex; mr.mr_type = PACKET_MR_PROMISC; if (setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) == -1) diff --git a/pcap-nit.c b/pcap-nit.c index ae4c40c..7517f9e 100644 --- a/pcap-nit.c +++ b/pcap-nit.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.55 2004-03-21 08:32:05 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-nit.c,v 1.56 2004-03-23 19:18:06 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -192,6 +192,23 @@ pcap_read_nit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) return (n); } +static int +pcap_inject_nit(pcap_t *p, const void *buf, size_t size) +{ + struct sockaddr sa; + int ret; + + memset(&sa, 0, sizeof(sa)); + strncpy(sa.sa_data, device, sizeof(sa.sa_data)); + ret = sendto(p->fd, buf, size, 0, &sa, sizeof(sa)); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + static int nit_setflags(int fd, int promisc, int to_ms, char *ebuf) { @@ -226,6 +243,8 @@ pcap_close_nit(pcap_t *p) { if (p->buffer != NULL) free(p->buffer); + if (p->device != NULL) + free(p->device); if (p->fd >= 0) close(p->fd); } @@ -280,6 +299,16 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, goto bad; } + /* + * We need the device name in order to send packets. + */ + p->device = strdup(device); + if (p->device == NULL) { + strlcpy(ebuf, pcap_strerror(errno), PCAP_ERRBUF_SIZE); + free(p->buffer); + goto bad; + } + /* * "p->fd" is a socket, so "select()" should work on it. */ @@ -306,6 +335,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } p->read_op = pcap_read_nit; + p->inject_op = pcap_inject_nit; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-pf.c b/pcap-pf.c index 3fa1c84..3a59083 100644 --- a/pcap-pf.c +++ b/pcap-pf.c @@ -24,7 +24,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.86 2004-02-09 06:24:42 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-pf.c,v 1.87 2004-03-23 19:18:06 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -219,6 +219,20 @@ pcap_read_pf(pcap_t *pc, int cnt, pcap_handler callback, u_char *user) return (n); } +static int +pcap_inject_pf(pcap_t *p, const void *buf, size_t size) +{ + int ret; + + ret = write(p->fd, buf, size); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + static int pcap_stats_pf(pcap_t *p, struct pcap_stat *ps) { @@ -299,14 +313,28 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, return (0); } memset(p, 0, sizeof(*p)); - /* + * Initially try a read/write open (to allow the inject + * method to work). If that fails due to permission + * issues, fall back to read-only. This allows a + * non-root user to be granted specific access to pcap + * capabilities via file permissions. + * + * XXX - we should have an API that has a flag that + * controls whether to open read-only or read-write, + * so that denial of permission to send (or inability + * to send, if sending packets isn't supported on + * the device in question) can be indicated at open + * time. + * * XXX - we assume here that "pfopen()" does not, in fact, modify * its argument, even though it takes a "char *" rather than a * "const char *" as its first argument. That appears to be * the case, at least on Digital UNIX 4.0. */ - p->fd = pfopen(device, O_RDONLY); + p->fd = pfopen(device, O_RDWR); + if (p->fd == -1 && errno == EACCES) + p->fd = pfopen(device, O_RDONLY); if (p->fd < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "pf open: %s: %s\n\ your system may not be properly configured; see the packetfilter(4) man page\n", @@ -467,6 +495,7 @@ your system may not be properly configured; see the packetfilter(4) man page\n", p->selectable_fd = p->fd; p->read_op = pcap_read_pf; + p->inject_op = pcap_inject_pf; p->setfilter_op = pcap_setfilter_pf; p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-snit.c b/pcap-snit.c index 209878b..3b0801e 100644 --- a/pcap-snit.c +++ b/pcap-snit.c @@ -25,7 +25,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.70 2003-12-18 23:32:33 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snit.c,v 1.71 2004-03-23 19:18:06 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -204,6 +204,29 @@ pcap_read_snit(pcap_t *p, int cnt, pcap_handler callback, u_char *user) return (n); } +static int +pcap_inject_snit(pcap_t *p, const void *buf, size_t size) +{ + struct strbuf ctl, data; + + /* + * XXX - can we just do + * + ret = write(pd->f, buf, size); + */ + ctl.len = sizeof(*sa); /* XXX - what was this? */ + ctl.buf = (char *)sa; + data.buf = buf; + data.len = size; + ret = putmsg(p->fd, &ctl, &data); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + static int nit_setflags(int fd, int promisc, int to_ms, char *ebuf) { @@ -271,7 +294,23 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, snaplen = 96; memset(p, 0, sizeof(*p)); - p->fd = fd = open(dev, O_RDONLY); + /* + * Initially try a read/write open (to allow the inject + * method to work). If that fails due to permission + * issues, fall back to read-only. This allows a + * non-root user to be granted specific access to pcap + * capabilities via file permissions. + * + * XXX - we should have an API that has a flag that + * controls whether to open read-only or read-write, + * so that denial of permission to send (or inability + * to send, if sending packets isn't supported on + * the device in question) can be indicated at open + * time. + */ + p->fd = fd = open(dev, O_RDWR); + if (fd < 0 && errno == EACCES) + p->fd = fd = open(dev, O_RDONLY); if (fd < 0) { snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s", dev, pcap_strerror(errno)); @@ -365,6 +404,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, } p->read_op = pcap_read_snit; + p->inject_op = pcap_inject_snit; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-snoop.c b/pcap-snoop.c index d847444..288ae70 100644 --- a/pcap-snoop.c +++ b/pcap-snoop.c @@ -20,7 +20,7 @@ */ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.51 2004-03-21 08:32:06 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-snoop.c,v 1.52 2004-03-23 19:18:06 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -125,6 +125,22 @@ again: return (0); } +static int +pcap_inject_snoop(pcap_t *p, const void *buf, size_t size) +{ + /* + * XXX - libnet overwrites the source address with what I + * presume is the interface's address; is that required? + */ + ret = write(p->fd, buf, size); + if (ret == -1) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send: %s", + pcap_strerror(errno)); + return (-1); + } + return (ret); +} + static int pcap_stats_snoop(pcap_t *p, struct pcap_stat *ps) { @@ -358,6 +374,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, p->selectable_fd = p->fd; p->read_op = pcap_read_snoop; + p->inject_op = pcap_inject_snoop; p->setfilter_op = install_bpf_program; /* no kernel filtering */ p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_fd; diff --git a/pcap-win32.c b/pcap-win32.c index da01f28..dcd4dc1 100644 --- a/pcap-win32.c +++ b/pcap-win32.c @@ -32,7 +32,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap-win32.c,v 1.20 2004-01-28 14:06:20 risso Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap-win32.c,v 1.21 2004-03-23 19:18:07 guy Exp $ (LBL)"; #endif #include @@ -344,6 +344,29 @@ pcap_read_win32_dag(pcap_t *p, int cnt, pcap_handler callback, u_char *user) } #endif /* HAVE_DAG_API */ +/* Send a packet to the network */ +static int +pcap_inject_win32(pcap_t *p, const void *buf, size_t size){ + LPPACKET PacketToSend; + + PacketToSend=PacketAllocatePacket(); + PacketInitPacket(PacketToSend,buf,size); + if(PacketSendPacket(p->adapter,PacketToSend,TRUE) == FALSE){ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "send error: PacketSendPacket failed"); + PacketFreePacket(PacketToSend); + return -1; + } + + PacketFreePacket(PacketToSend); + + /* + * We assume it all got sent if "PacketSendPacket()" succeeded. + * "pcap_inject()" is expected to return the number of bytes + * sent. + */ + return size; +} + static void pcap_close_win32(pcap_t *p) { @@ -569,6 +592,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, #ifdef HAVE_DAG_API } #endif /* HAVE_DAG_API */ + p->inject_op = pcap_inject_win32; p->set_datalink_op = NULL; /* can't change data link type */ p->getnonblock_op = pcap_getnonblock_win32; p->setnonblock_op = pcap_setnonblock_win32; @@ -686,28 +710,6 @@ pcap_setmode(pcap_t *p, int mode){ return 0; } -/* Send a packet to the network */ -int -pcap_sendpacket(pcap_t *p, u_char *buf, int size){ - LPPACKET PacketToSend; - - if (p->adapter==NULL) - { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Writing a packet is allowed only on a physical adapter"); - return -1; - } - - PacketToSend=PacketAllocatePacket(); - PacketInitPacket(PacketToSend,buf,size); - if(PacketSendPacket(p->adapter,PacketToSend,TRUE) == FALSE){ - PacketFreePacket(PacketToSend); - return -1; - } - - PacketFreePacket(PacketToSend); - return 0; -} - /* Set the dimension of the kernel-level capture buffer */ int pcap_setbuff(pcap_t *p, int dim) diff --git a/pcap.3 b/pcap.3 index 9f19df3..d5725dd 100644 --- a/pcap.3 +++ b/pcap.3 @@ -1,4 +1,4 @@ -.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.58 2004-02-28 02:51:26 guy Exp $ +.\" @(#) $Header: /tcpdump/master/libpcap/Attic/pcap.3,v 1.59 2004-03-23 19:18:07 guy Exp $ .\" .\" Copyright (c) 1994, 1996, 1997 .\" The Regents of the University of California. All rights reserved. @@ -88,6 +88,11 @@ void pcap_breakloop(pcap_t *) .ft .LP .ft B +int pcap_inject(pcap_t *p, const void *buf, size_t size) +int pcap_sendpacket(pcap_t *p, const u_char *buf, int size) +.ft +.LP +.ft B int pcap_datalink(pcap_t *p) int pcap_list_datalinks(pcap_t *p, int **dlt_buf); int pcap_set_datalink(pcap_t *p, int dlt); @@ -628,6 +633,36 @@ the flag is cleared, so a subsequent call will resume reading packets. If a positive number is returned, the flag is not cleared, so a subsequent call will return \-2 and clear the flag. .PP +.B pcap_inject() +sends a raw packet through the network interface; +.I buf +points to the data of the packet, including the link-layer header, and +.I size +is the number of bytes in the packet. +It returns the number of bytes written on success. A return of \-1 +indicates an error in which case +.B pcap_perror() +or +.B pcap_geterr() +may be used to display the error text. +Note that, even if you successfully open the network interface, you +might not have permission to send packets on it, or it might not support +sending packets; as +.I pcap_open_live() +doesn't have a flag to indicate whether to open for capturing, sending, +or capturing and sending, you cannot request an open that supports +sending and be notified at open time whether sending will be possible. +Note also that some devices might not support sending packets. +.PP +.B pcap_sendpacket() +is like +.BR pcap_inject() , +but it returns 0 on success and \-1 on failure. +.RB ( pcap_inject() +comes from OpenBSD; +.B pcap_sendpacket() +comes from WinPcap. Both are provided for compatibility.) +.PP .B pcap_dump() outputs a packet to the ``savefile'' opened with .BR pcap_dump_open() . diff --git a/pcap.c b/pcap.c index a32dc3f..01a3140 100644 --- a/pcap.c +++ b/pcap.c @@ -33,7 +33,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.72 2004-03-17 19:03:29 guy Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/pcap.c,v 1.73 2004-03-23 19:18:07 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -700,6 +700,30 @@ pcap_open_dead(int linktype, int snaplen) return p; } +/* + * API compatible with WinPcap's "send a packet" routine - returns -1 + * on error, 0 otherwise. + * + * XXX - what if we get a short write? + */ +int +pcap_sendpacket(pcap_t *p, const u_char *buf, int size) +{ + if (p->inject_op(p, buf, size) == -1) + return (-1); + return (0); +} + +/* + * API compatible with OpenBSD's "send a packet" routine - returns -1 on + * error, number of bytes written otherwise. + */ +int +pcap_inject(pcap_t *p, const void *buf, size_t size) +{ + return (p->inject_op(p, buf, size)); +} + void pcap_close(pcap_t *p) { diff --git a/pcap.h b/pcap.h index b2cc62f..55bca95 100644 --- a/pcap.h +++ b/pcap.h @@ -31,7 +31,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.49 2004-01-27 09:44:14 risso Exp $ (LBL) + * @(#) $Header: /tcpdump/master/libpcap/pcap.h,v 1.50 2004-03-23 19:18:07 guy Exp $ (LBL) */ #ifndef lib_pcap_h @@ -184,6 +184,8 @@ int pcap_setfilter(pcap_t *, struct bpf_program *); int pcap_getnonblock(pcap_t *, char *); int pcap_setnonblock(pcap_t *, int, char *); void pcap_perror(pcap_t *, char *); +int pcap_inject(pcap_t *, const void *, size_t); +int pcap_sendpacket(pcap_t *, const u_char *, int); char *pcap_strerror(int); char *pcap_geterr(pcap_t *); int pcap_compile(pcap_t *, struct bpf_program *, char *, int, @@ -230,7 +232,6 @@ void bpf_dump(struct bpf_program *, int); int pcap_setbuff(pcap_t *p, int dim); int pcap_setmode(pcap_t *p, int mode); -int pcap_sendpacket(pcap_t *p, u_char *buf, int size); int pcap_setmintocopy(pcap_t *p, int size); #ifdef WPCAP diff --git a/savefile.c b/savefile.c index dc4f341..d98178c 100644 --- a/savefile.c +++ b/savefile.c @@ -30,7 +30,7 @@ #ifndef lint static const char rcsid[] _U_ = - "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.105 2004-03-16 19:27:55 risso Exp $ (LBL)"; + "@(#) $Header: /tcpdump/master/libpcap/savefile.c,v 1.106 2004-03-23 19:18:08 guy Exp $ (LBL)"; #endif #ifdef HAVE_CONFIG_H @@ -574,6 +574,14 @@ sf_stats(pcap_t *p, struct pcap_stat *ps) return (-1); } +static int +sf_inject(pcap_t *p, const void *buf _U_, size_t size _U_) +{ + strlcpy(p->errbuf, "Sending packets isn't supported on savefiles", + PCAP_ERRBUF_SIZE); + return (-1); +} + static void sf_close(pcap_t *p) { @@ -730,6 +738,7 @@ pcap_open_offline(const char *fname, char *errbuf) #endif p->read_op = pcap_offline_read; + p->inject_op = sf_inject; p->setfilter_op = install_bpf_program; p->set_datalink_op = NULL; /* we don't support munging link-layer headers */ p->getnonblock_op = sf_getnonblock;