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