blob: 3c86a12bb6a6f64759f7279a706fcb592284aad2 [file] [log] [blame]
/* 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 */
}