| /* pcap.c - does roughly the job of bpf.c from the NetBSD version sources |
| * |
| * This code was written in about 1996 by |
| * Peter Maydell <pmaydell@chiark.greenend.org.uk>, and revised |
| * in 1998 to clean it up a little... |
| * |
| * Revised to use PF_PACKET instead of SOCK_PACKET (thanks to patch from |
| * Kars de Jong <jongk@linux-m68k.org>) |
| * |
| * On the positive side, this code is rather less nasty than bpf.c, |
| * primarily because a lot of lower-level stuff has vanished into libpcap. |
| * |
| * Provides : |
| * PcapGetIntfName(char* errmsg) get first ethernet interface name, |
| * or put an error message into errmsg buffer |
| * |
| * PcapOpen() open the packet filter, etc |
| * PcapDispatch(handlerfcn) dispatch a packet if one present |
| * PcapHandler() internal function - passes packets to |
| * user's handler. |
| * PcapClose() close filter, deinitialise, etc |
| * |
| * The legal bumph from the NetBSD sources is huge. Consider it said. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> /* socket(), sendto() */ |
| #include <netinet/in.h> /* htons(), ntohs() */ |
| #include <sys/ioctl.h> /* ioctl() */ |
| #include <unistd.h> /* close() */ |
| #include <syslog.h> /* syslog() */ |
| #include <net/if.h> /* struct ifreq */ |
| #include <net/ethernet.h> /* ETH_P_802_3 */ |
| #include <netpacket/packet.h> /* PF_PACKET stuff */ |
| #include <string.h> /* strncpy(), bcopy() */ |
| #include "defs.h" |
| |
| static pcap_t *pdesc = NULL; /* packet capture descriptor */ |
| static char errbuf[PCAP_ERRBUF_SIZE]; /* buffer we use to get error msgs */ |
| |
| /* PcapOpen() - open & init packet filter. No params/return code. |
| * If we encounter an error, we stop here (since that's what BpfOpen does) |
| */ |
| void PcapOpen() |
| { |
| int n; |
| /* First we open the device. We use promiscuous mode as I have no |
| * idea whether or not pcap supports multicast. Besides, there's not |
| * going to be much net traffic for *my* application :-> |
| * We set capture length to twice the size of the RMP packet |
| * as a cheap way to detect oversize packets. This is a nasty hack... |
| */ |
| pdesc = pcap_open_live(IntfName, 3 * sizeof(struct rmp_packet), |
| 1, 3, errbuf); |
| if (pdesc == NULL) |
| { |
| syslog(LOG_ERR, "pcap: couldn't open filter: %s",errbuf); |
| Exit(0); |
| } |
| |
| /* check we're being used on an Ethernet device */ |
| n = pcap_datalink(pdesc); |
| if (n != DLT_EN10MB) |
| { |
| syslog(LOG_ERR, "pcap: %s: datalink type %d unsupported",IntfName, n); |
| Exit(0); |
| } |
| |
| /* Now set the filter program; this is straight from bpf.c */ |
| { |
| #define RMP ((struct rmp_packet *)0) |
| static struct bpf_insn bpf_insn[] = { |
| { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, |
| { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, |
| { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, |
| { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, |
| { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, |
| { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, |
| { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, |
| { BPF_RET|BPF_K, 0, 0, 0x0 } |
| }; |
| #undef RMP |
| static struct bpf_program bpf_pgm = { |
| sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn |
| }; |
| |
| if (pcap_setfilter(pdesc, &bpf_pgm) != 0) |
| { |
| syslog(LOG_ERR, "pcap: failed to set filter (bug?)"); |
| Exit(0); |
| } |
| } |
| } |
| |
| /* PcapGetIntfName - get name of the first ethernet device in the system. |
| * We ignore loopback. We return 0 if none found, and then *errmsg |
| * explains why. Why do we pass the errmsg buffer as char**? We only |
| * dereference it here, and we use address-of to pass it in rbootd.c... |
| * Ah well, presumably there was a reason :-> |
| */ |
| char* PcapGetIntfName(char **errmsg) |
| { |
| static char netdev[31]; |
| pcap_if_t *alldevsp; |
| int ret; |
| |
| /* We'll assume this function is suitable... */ |
| ret = pcap_findalldevs(&alldevsp, *errmsg); /* find a device */ |
| if (ret != 0) |
| { |
| return NULL; /* flag error to caller */ |
| } |
| strcpy(netdev, alldevsp->name); |
| pcap_freealldevs(alldevsp); |
| return netdev; /* device found - return ptr to its name */ |
| } |
| |
| |
| /* var used to pass the user's handler from PcapDispatch to PcapHandler */ |
| static void (*uhandler)(RMPCONN rconn); |
| |
| void PcapHandler(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) |
| { |
| /* Called when we get a packet. Sort it out into the right format for |
| * the user's handler. Then call that. |
| * This is probably the only really tricky bit in this file. |
| * user is just the ptr passed to pcap_dispatch() - we don't use it. |
| * h is ptr to the header. sp is the pointer to the actual packet. |
| * You can get h->caplen bytes of data from sp onwards. |
| * Header length is just sizeof(struct pcap_pkthdr). |
| */ |
| |
| RMPCONN rconn; /* fill this in with packet details */ |
| register int datlen, caplen; /* declared register because bpf.c does... */ |
| datlen = h->len; /* length of this packet (untruncated) */ |
| caplen = h->caplen; /* max. captured bytes */ |
| |
| /* First we ensure we got a complete packet of the right size! */ |
| if (caplen < datlen) |
| syslog(LOG_ERR, "bpf: truncated packet dropped (%d of %d bytes)", |
| caplen, datlen); |
| else if (datlen > sizeof(struct rmp_packet)) |
| syslog(LOG_ERR, "bpf: oversized packet dropped (%d bytes)", caplen); |
| else |
| { |
| /* fill in fields in rconn and pass packet to user handler */ |
| rconn.rmplen = datlen; |
| bcopy((char *)&h->ts, (char *)&rconn.tstamp, sizeof(struct timeval)); |
| bcopy(sp, (char *)&rconn.rmp, datlen); /* and copy packet over */ |
| uhandler(rconn); |
| } |
| } |
| |
| /* PcapDispatch(handlerfcn) : just an interface to the real |
| * dispatch / handler code... |
| * Takes one parameter - the user's handler, which has prototype: |
| * void DealWithPacket(RMPCONN rconn); |
| */ |
| int PcapDispatch(void (*usrhandler)(RMPCONN rconn)) |
| { |
| int retcode; |
| /* First we save ptr to the user's handler code */ |
| uhandler = usrhandler; |
| /* Then we use pcap_dispatch() to start things off, |
| using a ptr to the real PcapHandler() */ |
| retcode = pcap_dispatch(pdesc, 1, PcapHandler, NULL); |
| /* We return the errorcode ( < 0 for errors) |
| * but we syslog it here, since only we know the pdesc |
| */ |
| if (retcode < 0) |
| syslog(LOG_ERR, "pcap: dispatch failed: %s", pcap_geterr(pdesc)); |
| return retcode; |
| } |
| |
| /* PcapClose() - close filter, deinitialise anything we did, etc. */ |
| void PcapClose() |
| { |
| /* call pcap_close() to shut down interface... |
| ...but only if we've already opened it! */ |
| if (pdesc != NULL) |
| pcap_close(pdesc); |
| /* now do our own deinit, if any */ |
| } |
| |
| /* We also need to be able to write packets to the Ethernet... |
| This facility isn't provided by libpcap, but the subroutine's |
| in this source file anyway. */ |
| int PcapWrite(RMPCONN* rconn) |
| { |
| /* write rconn direct to ethernet */ |
| /* What we get in an RMPCONN: |
| * rconn->rmp is a struct rmp_packet |
| * rconn->rmplen is the length of said packet |
| * rconn->tstamp |
| * rconn->bootfd filedesc of open bootfile |
| * rconn->next ptr to next RMPCONN... |
| * |
| * We use a PF_PACKET socket in IEEE 802.3 mode. |
| * All we need to do is fill out the destination addres and send rconn->rmp, |
| * skipping the Ethernet header (it will be filled in by the kernel). |
| */ |
| int s,cs; |
| struct sockaddr_ll sa; |
| |
| memset (&sa, 0, sizeof(sa)); |
| s=socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_802_3)); |
| if (s == -1) |
| { |
| syslog(LOG_ERR, "attempt to open socket for write failed!"); |
| return 1; |
| } |
| |
| /* Get the interface index to be used. */ |
| { |
| struct ifreq req; |
| int status; |
| |
| strcpy (req.ifr_name, IntfName); |
| status = ioctl (s, SIOCGIFINDEX, &req); |
| if (status == -1) |
| { |
| syslog(LOG_ERR, "SIOCGIFINDEX: couldn't get interface index!"); |
| return 0; |
| } |
| sa.sll_ifindex = req.ifr_ifindex; |
| } |
| |
| /* Fill in the socket address */ |
| sa.sll_protocol = htons(ETH_P_802_3); |
| sa.sll_family = AF_PACKET; |
| memcpy (&sa.sll_addr, &(rconn->rmp.hp_hdr.daddr), RMP_ADDRLEN); |
| sa.sll_halen = RMP_ADDRLEN; |
| |
| /* perform the actual packet send */ |
| cs = sendto(s, &(rconn->rmp.hp_llc), rconn->rmplen - sizeof(struct hp_hdr), |
| 0, (struct sockaddr *)&sa, sizeof(sa)); |
| |
| close (s); |
| |
| if (cs == -1) |
| { |
| syslog (LOG_ERR, "sendto: failed to send packet!"); |
| return 0; |
| } |
| |
| return 1; /* success */ |
| } |