1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
/* 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 */
}
|