1 /*-
2 * pcap_wire.cc
3 *
4 * Implementation of Wire for libpcap
5 */
6 #include "config.h"
7
8 #if HAVE_LIBPCAP
9 #include <cstring>
10 #include <stdexcept>
11 #include <cerrno>
12
13 #include <sys/types.h> /* Required for class EUID */
14
15 #if HAVE_GETIFADDRS
16 # include <sys/socket.h>
17 #endif
18
19 #include <sys/time.h>
20 #include <sys/select.h>
21
22 /* Ways of finding the hardware MAC on this machine... */
23 /* This is the Linux only fallback. */
24 #ifdef SIOCGIFHWADDR
25 # include <sys/ioctl.h>
26 # include <net/if.h>
27 #endif
28
29 #if HAVE_GETIFADDRS
30 # include <ifaddrs.h>
31 #endif
32
33 /* Now the struct sockaddr header files for the required protocol
34 * families. Expect either AF_LINK (BSDs) or AF_PACKET(Linux), or
35 * maybe both to be returned from getifaddrs.
36 */
37 #ifdef AF_LINK
38 # include <net/if_dl.h>
39 #endif
40 #ifdef AF_PACKET
41 # include <netpacket/packet.h>
42 #endif
43
44 #include <unistd.h>
45
46 #include <pcap.h>
47
48 #include "nslu2_upgrade.h"
49
50 namespace NSLU2Upgrade {
51 /* The basic class implemented to transmit and receive packets over the
52 * wire.
53 */
54 class PCapWire : public Wire {
55 public:
PCapWire(pcap_t * p,const char * device,const unsigned char * mac,const unsigned char address[6])56 PCapWire(pcap_t *p, const char *device, const unsigned char *mac,
57 const unsigned char address[6]) :
58 pcap(p), file(pcap_fileno(p)), broadcast(address == 0) {
59 /* The 255 gives the ethernet hardware broadcast address,
60 * set this if a host address is provided. The packet
61 * header is:
62 *
63 * target MAC [6 bytes] target or broadcast
64 * originating MAC [6 bytes] MAC of this device
65 * protocol [2 bytes] 0x8888 (big endian)
66 */
67 if (address)
68 std::memcpy(header, address, 6);
69 else
70 std::memset(header, 255, 6);
71 header[12] = NSLU2Protocol::UpgradeProtocol >> 8;
72 header[13] = NSLU2Protocol::UpgradeProtocol;
73
74 /* This is set just in case of a call to LastAddress before
75 * Receive has succeeded - the result will be all 0's
76 */
77 std::memset(source, 0, sizeof source);
78
79 /* This should always work. */
80 if (file == -1)
81 throw WireError(errno);
82
83 /* And copy the MAC address into the header. */
84 std::memcpy(header+6, mac, 6);
85 }
86
~PCapWire()87 virtual ~PCapWire() {
88 pcap_close(pcap);
89 }
90
91 /* Throws SendError on a fatal error. */
Send(const void * packet,size_t length)92 virtual void Send(const void *packet, size_t length) {
93 if (length > 1540-14)
94 throw std::logic_error("packet too large");
95
96 /* Set no flags (0) - we block on the transmit if
97 * required, the pcap is *not* set O_NONBLOCK.
98 */
99 char buffer[1540];
100 std::memcpy(buffer, header, 14);
101 std::memcpy(buffer+14, packet, length);
102
103 char *data = buffer;
104 int len(length+14);
105 do {
106 /* This seems to work on BSD as well as Linux, BSD supports
107 * pcap_inject which does the same thing, WinPcap supports
108 * pcap_sendpacket, which also does the same thing.
109 */
110 # if HAVE_PCAP_INJECT
111 const ssize_t written(pcap_inject(pcap, data, len));
112 # else
113 const ssize_t written(write(file, data, len));
114 # endif
115 if (written < 0) {
116 if (errno != EINTR)
117 throw SendError(errno);
118 } else {
119 /* I suspect this won't work - the write either
120 * consumes all the data or none of it I think.
121 */
122 data += written;
123 len -= written;
124 }
125 } while (len > 0);
126 }
127
128 /* This is a pcap_handler implementation, the static callback passed
129 * to pcap_dispatch derives the original 'this' pointer and calls the
130 * non-static (real) Handler.
131 */
Handler(const struct pcap_pkthdr * packet_header,const u_char * packet)132 void Handler(const struct pcap_pkthdr *packet_header, const u_char *packet) {
133 /* This should only be called once... */
134 if (captured)
135 throw std::logic_error("Handler called twice");
136
137 /* Verify the protocol and originating address of the packet, then
138 * return this packet.
139 */
140 if (packet_header->caplen > 14 && (broadcast ||
141 std::memcmp(packet+6, header, 6) == 0)) {
142 /* Record the address and copy the data */
143 std::memcpy(source, packet+6, 6);
144 const size_t len(packet_header->caplen - 14);
145 if (len > captureSize)
146 throw std::logic_error("packet too long");
147 std::memcpy(captureBuffer, packet+14, len);
148 captureSize = len;
149 captured = true;
150 }
151 }
152
PCapHandler(u_char * user,const struct pcap_pkthdr * packet_header,const u_char * packet)153 static void PCapHandler(u_char *user, const struct pcap_pkthdr *packet_header,
154 const u_char *packet) {
155 /* The following should never happen because this is an ethernet
156 * packet and the buffer should be big enough.
157 */
158 if (packet_header->caplen < packet_header->len)
159 throw std::logic_error("truncated packet");
160
161 /*IGNORE EVIL: known evil cast */
162 reinterpret_cast<PCapWire*>(user)->Handler(packet_header, packet);
163 }
164
165 /* Receive throws ReceiveError on a fatal error and must update
166 * size with the received packet size. 0 must be used to
167 * indicate failure to receive a packet (and this must not
168 * be fatal). If timeout is greater than 0 the implementation
169 * should wait that number of microseconds until a packet is
170 * received or the timeout has expired (in which case a size
171 * of 0 must be returned).
172 */
Receive(void * buffer,size_t & size,unsigned long timeout)173 virtual void Receive(void *buffer, size_t &size, unsigned long timeout) {
174 /* Now try to read packets until the timeout has been consumed.
175 */
176 struct timeval tvStart;
177 if (timeout > 0 && gettimeofday(&tvStart, 0) != 0)
178 throw OSError(errno, "gettimeofday(base)");
179
180 captureBuffer = buffer;
181 captureSize = size;
182 captured = false;
183 do {
184 /*IGNORE EVIL: known evil cast */
185 int count(pcap_dispatch(pcap, 1, PCapHandler,
186 reinterpret_cast<u_char*>(this)));
187
188 if (count > 0) {
189 /* Were any packets handled? */
190 if (captured) {
191 size = captureSize;
192 return;
193 }
194 /* else try again. */
195 } else if (count == 0) {
196 /* Nothing to handle - do the timeout, do this
197 * by waiting a bit then trying again, the trick
198 * to this is to work out how long to wait each
199 * time, for the moment a 10ms delay is used.
200 */
201 if (timeout == 0)
202 break;
203
204 struct timeval tvNow;
205 if (gettimeofday(&tvNow, 0) != 0)
206 throw OSError(errno, "gettimeofday(now)");
207
208 unsigned long t(tvNow.tv_sec - tvStart.tv_sec);
209 t *= 1000000;
210 t += tvNow.tv_usec;
211 t -= tvStart.tv_usec;
212 if (t > timeout)
213 break;
214
215 tvNow.tv_sec = 0;
216 tvNow.tv_usec = timeout-t;
217 if (tvNow.tv_usec > 10000)
218 tvNow.tv_usec = 10000;
219
220 /* Delay, may be interrupted - this should
221 * be portable to the BSDs (since the
222 * technique originates in BSD.)
223 */
224 (void)select(0, 0, 0, 0, &tvNow);
225 } else {
226 /* Error condition. */
227 if (count == -1) {
228 if (errno != EINTR)
229 throw ReceiveError(errno,
230 pcap_geterr(pcap));
231 /* else try again */
232 } else
233 throw std::logic_error("pcap unexpected result");
234 }
235 } while (timeout != 0);
236
237 /* Here on timeout. */
238 size = 0;
239 return;
240 }
241
242 /* Return the address of the last received packet. This is
243 * an NSLU2 so the address is a 6 byte Ethernet hardware
244 * address.
245 */
LastAddress(unsigned char address[6])246 virtual void LastAddress(unsigned char address[6]) {
247 std::memcpy(address, source, 6);
248 }
249
250 private:
251 void* captureBuffer; /* Buffer to be filled in by Handler */
252 size_t captureSize; /* Filled in by Handler - bytes in buffer */
253 pcap_t* pcap;
254 int file; /* pcap file descriptor */
255 char header[14]; /* Packet header. */
256 char source[6]; /* Source of last *received* packet. */
257 bool broadcast;
258 bool captured; /* Whether Handler was called */
259 };
260
261 /* Class to set and reset the user id to the effective uid. */
262 class EUID {
263 public:
EUID(int uid)264 EUID(int uid) : euid(::geteuid()) {
265 if (uid != -1 && ::seteuid(uid) != 0)
266 throw WireError(errno);
267 }
268
~EUID()269 ~EUID() {
270 ::seteuid(euid);
271 }
272
273 private:
274 ::uid_t euid;
275 };
276
277 };
278
279
280 /* Make a new wire, which may be deleted with delete. The
281 * address should be a value (null terminated this time) returned
282 * by LastAddress, if NULL the Wire will broadcast. 'device'
283 * is the hardware device name to use - the value of the
284 * --device parameter on the command line (if given). If not
285 * given (NULL) a potentially useless default will be used.
286 */
MakeWire(const char * device,const unsigned char * mac,const unsigned char * address,int uid)287 NSLU2Upgrade::Wire *NSLU2Upgrade::Wire::MakeWire(const char *device,
288 const unsigned char *mac, const unsigned char *address, int uid) {
289 /* This is used to store the error passed to throw. */
290 static char PCapErrbuf[PCAP_ERRBUF_SIZE];
291
292 /* Check the device name. If not given use 'eth0'. */
293 if (device == NULL)
294 device = "eth0";
295
296 pcap_t *pcap = NULL;
297 {
298 EUID euid(uid);
299 /* Do *NOT* set promiscuous here - all manner of strangeness
300 * will result because the interfaces will capture packets destined
301 * for other ethernet MACs. (Because the code above does not
302 * check that the destination matches the device in use).
303 */
304 pcap = pcap_open_live(device, 1540, false/*promiscuous*/, 1/*ms*/, PCapErrbuf);
305
306 if (pcap == NULL)
307 throw WireError(errno, PCapErrbuf);
308 }
309
310 /* Always do a non-blocking read, because the 'timeout' above
311 * doesn't work on Linux (return is immediate) and on OSX (and
312 * maybe other BSDs) the interface tends to hang waiting for
313 * the timeout to expire even after receiving a single packet.
314 */
315 if (pcap_setnonblock(pcap, true, PCapErrbuf))
316 throw WireError(errno, PCapErrbuf);
317
318 try {
319 /* The MAC of the transmitting device is needed - without
320 * this the return packet won't go to the right place!
321 */
322 unsigned char macBuffer[6];
323 std::memset(macBuffer, 0, sizeof macBuffer);
324
325 /* If the MAC is not given (the normal case) use getifaddrs to find
326 * the MAC of the named device. getifaddrs is the standard BSD
327 * interface, but it seems to exist on Linux too (anyway, this Wire
328 * implementation should probably not be used on Linux!)
329 */
330 # if HAVE_GETIFADDRS
331 if (mac == NULL) {
332 struct ifaddrs *ifap;
333
334 if (getifaddrs(&ifap) != 0)
335 throw WireError(errno, "getifaddrs failed");
336
337 try {
338 struct ifaddrs *ifa = ifap;
339 do {
340 if (ifa == NULL)
341 break;
342
343 # ifdef AF_LINK
344 if (ifa->ifa_addr->sa_family == AF_LINK &&
345 strcmp(ifa->ifa_name, device) == 0) {
346 const struct sockaddr_dl *sdl =
347 reinterpret_cast<struct sockaddr_dl*>(ifa->ifa_addr);
348 std::memcpy(macBuffer, LLADDR(sdl), 6);
349 mac = macBuffer;
350 break;
351 }
352 # endif
353 # ifdef AF_PACKET
354 if (ifa->ifa_addr->sa_family == AF_PACKET &&
355 strcmp(ifa->ifa_name, device) == 0) {
356 const struct sockaddr_ll *sll =
357 reinterpret_cast<struct sockaddr_ll*>(ifa->ifa_addr);
358 std::memcpy(macBuffer, sll->sll_addr, 6);
359 mac = macBuffer;
360 break;
361 }
362 # endif
363
364 ifa = ifa->ifa_next;
365 } while (1);
366 } catch (...) {
367 freeifaddrs(ifap);
368 throw;
369 }
370
371 freeifaddrs(ifap);
372 }
373 # endif
374 # ifdef SIOCGIFHWADDR
375 /* This is a fallback which currently is only know to work
376 * on Linux.
377 */
378 if (mac == NULL) {
379 struct ifreq device_interface;
380
381 strncpy(device_interface.ifr_name, device,
382 sizeof device_interface.ifr_name);
383 device_interface.ifr_name[(sizeof device_interface.ifr_name)-1] = 0;
384
385 /* Get the hardware information. */
386 if (ioctl(pcap_fileno(pcap), SIOCGIFHWADDR, &device_interface) == (-1))
387 throw WireError(errno);
388
389 std::memcpy(macBuffer, device_interface.ifr_hwaddr.sa_data, 6);
390 mac = macBuffer;
391 }
392 # endif
393
394 if (mac == NULL)
395 throw WireError(ENOENT, "no link-level interface to provide hardware MAC");
396
397 /* libpcap has the primary purpose of slurping all the packets then
398 * filtering out interesting ones. This is a somewhat dumb way of
399 * receiving packets from a known protocol, but this seems to be the
400 * only portable approach. Consequently it is necessary to 'compile'
401 * a 'program' for libpcap to get the correct packets.
402 */
403 {
404 struct bpf_program filter_program;
405
406 if (pcap_compile(pcap, &filter_program, "ether proto 0x8888",
407 true/*optimise*/, 0/*netmask - not used*/) == -1)
408 throw WireError(errno);
409
410 try {
411 if (pcap_setfilter(pcap, &filter_program) == -1)
412 throw WireError(errno);
413 } catch (...) {
414 pcap_freecode(&filter_program);
415 throw;
416 }
417
418 pcap_freecode(&filter_program);
419 }
420
421 /* This is enough to make a new wire. */
422 return new PCapWire(pcap, device, mac, address);
423 } catch (...) {
424 /* Error cleanup - the pcap needs to be deleted. */
425 pcap_close(pcap);
426 throw;
427 }
428 }
429 #endif
430