1 //============================================================================
5 // A utility program to perform low-level ethernet operations
7 //============================================================================
8 //####COPYRIGHTBEGIN####
10 // ----------------------------------------------------------------------------
11 // Copyright (C) 2002 Bart Veer
13 // This file is part of the eCos host tools.
15 // This program is free software; you can redistribute it and/or modify it
16 // under the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 of the License, or (at your option)
20 // This program is distributed in the hope that it will be useful, but WITHOUT
21 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
25 // You should have received a copy of the GNU General Public License along with
26 // this program; if not, write to the Free Software Foundation, Inc.,
27 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 // ----------------------------------------------------------------------------
30 //####COPYRIGHTEND####
31 //============================================================================
32 //#####DESCRIPTIONBEGIN####
35 // Contact(s): bartv, andrew.lunn@ascom.ch
40 // This program is fork'ed by the ethernet.tcl script running inside
41 // the synthetic target auxiliary. It is responsible for performing
42 // low-level ethernet I/O.
44 //####DESCRIPTIONEND####
45 //============================================================================
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <sys/socket.h>
59 #include <sys/ioctl.h>
61 #include <net/if_arp.h>
62 #include <netinet/in.h>
63 #include <netinet/if_ether.h>
64 #include <linux/if_packet.h>
65 #include <linux/if_ether.h>
66 #ifdef HAVE_LINUX_IF_TUN_H
67 # include <linux/if_tun.h>
70 // The protocol between host and target is defined by a private
71 // target-side header.
72 #include "../src/protocol.h"
74 // Allow debug builds. Set this flag to 0, 1 or 2
77 // ----------------------------------------------------------------------------
80 // Are we using a real ethernet device or ethertap?
81 static int real_ether = 0;
82 static int ethertap = 0;
84 // The six-byte MAC address, which must be returned to eCos
85 static unsigned char MAC[6];
87 // Does the driver support multicasting?
88 static int multicast_supported = 0;
90 // The file descriptor for incoming data ethernet packets.
91 // Used for select() together with fd 0 corresponding to ecosynth
92 static int ether_fd = -1;
94 // Is the interface up?
97 // Space for incoming and outgoing packets. In the case of rx_buffer
98 // there are an extra four bytes at the front for the protocol header.
100 static unsigned char tx_buffer[MTU];
101 static unsigned char rx_buffer[MTU+4];
103 // Indirect to get to the actual implementation functions.
104 static void (*tx_fn)(unsigned char*, int);
105 static void (*rx_fn)(void);
106 static void (*start_fn)(int);
107 static void (*stop_fn)(void);
108 static void (*multicast_fn)(int);
111 // ----------------------------------------------------------------------------
112 // A utility buffer for messages.
114 static unsigned char msg[MSG_SIZE];
116 // Report an error to ecosynth during initialization. This means a
117 // single byte 0, followed by a string.
119 report_error(char* msg)
122 write(1, msg, strlen(msg));
127 // Report success to ecosynth. This means a byte 1 followed by
134 msg[6] = multicast_supported;
139 // ----------------------------------------------------------------------------
140 // Real ethernet. This involves creating a SOCK_RAW socket and binding it
141 // to the appropriate interface. Relevant documentation can be found in
142 // the man pages (packet(7) and netdevice(7)).
144 // The device name. Needed for various ioctl()'s.
145 static char real_devname[IFNAMSIZ];
147 // The interface index.
148 static int real_ifindex = -1;
150 // Transmit a single ethernet frame. The socket should be set up so a
151 // simple send() operation should do the trick. Errors such as EAGAIN,
152 // indicating that the network device is still busy, are ignored.
153 // Ethernet is not a reliable communication medium.
155 real_handle_tx(unsigned char* buffer, int size)
159 result = send(ether_fd, buffer, size, MSG_DONTWAIT);
161 // It appears that one retry is worthwhile, to clear pending
162 // errors or something.
163 result = send(ether_fd, buffer, size, MSG_DONTWAIT);
166 fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
169 fprintf(stderr, " %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
170 buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
171 buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11],
172 buffer[12], buffer[13]);
176 // Receive a single ethernet frame, using the static rxbuffer. If the
177 // interface is not currently up discard it. Otherwise forward it on
186 size = recv(ether_fd, rx_buffer + 4, MTU, MSG_TRUNC);
189 fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, strerror(errno), errno);
193 return; // Ignore errors, just go around the main loop again.
195 if ((size < 14) || (size > MTU)) {
196 return; // Invalid packet size. Discard the packet.
200 fprintf(stderr, " %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
201 rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7], rx_buffer[8], rx_buffer[9],
202 rx_buffer[10], rx_buffer[11], rx_buffer[12], rx_buffer[13], rx_buffer[14], rx_buffer[15],
203 rx_buffer[16], rx_buffer[17]);
207 // eCos is not currently expecting packets, so discard them.
208 // This may not actually be necessary because the interface
209 // is only up when eCos wants it to be up.
213 // It looks this packet should get forwarded to eCos.
214 rx_buffer[0] = SYNTH_ETH_RX;
216 rx_buffer[2] = size & 0x00FF;
217 rx_buffer[3] = (size >> 8) & 0x00FF;
219 result = write(1, rx_buffer, 4 + size);
220 } while ((-1 == result) && (EINTR == errno));
222 if (result != (size + 4)) {
223 fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
228 // Utility to manipulate interface flags. This involves retrieving the
229 // current flags, or'ing in some bits, and'ing out others, and updating.
231 real_update_ifflags(int set_bits, int clear_bits)
233 struct ifreq request;
236 strncpy(request.ifr_name, real_devname, IFNAMSIZ);
237 if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) {
238 fprintf(stderr, "rawether (%s): failed to get interface flags, exiting\n", real_devname);
242 flags = request.ifr_flags;
245 flags &= ~clear_bits;
247 if (flags == request.ifr_flags) {
248 // Nothing is changing.
252 strncpy(request.ifr_name, real_devname, IFNAMSIZ);
253 request.ifr_flags = flags;
254 if (ioctl(ether_fd, SIOCSIFFLAGS, &request) < 0) {
255 fprintf(stderr, "rawether (%s): failed to update interface flags, exiting\n", real_devname);
261 // Starting an interface. This involves bringing the interface up,
262 // and optionally setting promiscuous mode.
263 // NOTE: is UP really the right thing here? There is no IP address
264 // for this interface. In theory this should not matter because
265 // we have a bound socket which should receive all packets for
269 real_handle_start(int promiscuous)
272 real_update_ifflags(IFF_UP | IFF_PROMISC, 0);
274 real_update_ifflags(IFF_UP, IFF_PROMISC);
279 // Stopping an interface means clearing the UP flag
281 real_handle_stop(void)
284 real_update_ifflags(0, IFF_UP | IFF_PROMISC);
287 // Enabling/disabling multicast support.
289 real_handle_multiall(int on)
291 struct packet_mreq req;
293 req.mr_ifindex = real_ifindex;
294 req.mr_type = PACKET_MR_ALLMULTI;
296 if (setsockopt(ether_fd, SOL_PACKET, on ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, (void*)&req, sizeof(req)) < 0) {
297 fprintf(stderr, "rawether (%s): failed to manipulate multicast-all flag, exiting\n", real_devname);
302 // When the application exists make sure that the interface goes down again.
308 real_update_ifflags(0, IFF_UP | IFF_PROMISC);
313 real_init(char* devname)
315 struct sockaddr_ll addr;
316 struct ifreq request;
318 tx_fn = &real_handle_tx;
319 rx_fn = &real_handle_rx;
320 start_fn = &real_handle_start;
321 stop_fn = &real_handle_stop;
322 multicast_fn = &real_handle_multiall;
324 if (strlen(devname) >= IFNAMSIZ) {
325 snprintf(msg, MSG_SIZE, "Invalid real network device name \"%s\", too long.\n", devname);
328 strcpy(real_devname, devname);
330 // All ioctl() operations need a socket. We might as well create the
331 // raw socket immediately and use that.
332 ether_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
334 snprintf(msg, MSG_SIZE, "Unable to create a raw socket for accessing network device\n"
335 " Error %s (errno %d)\n", strerror(errno), errno);
339 strncpy(request.ifr_name, real_devname, IFNAMSIZ);
340 if (ioctl(ether_fd, SIOCGIFINDEX, &request) < 0) {
341 snprintf(msg, MSG_SIZE, "Device %s does not correspond to a valid interface.\n"
342 " Error %s (errno %d)\n", real_devname, strerror(errno), errno);
345 real_ifindex = request.ifr_ifindex;
347 // The interface exists. Now check that it is usable.
348 strncpy(request.ifr_name, real_devname, IFNAMSIZ);
349 if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) {
350 snprintf(msg, MSG_SIZE, "Failed to get current interface flags for %s\n"
351 " Error %s (errno %d)\n", real_devname, strerror(errno), errno);
355 if (request.ifr_flags & (IFF_UP | IFF_RUNNING)) {
356 snprintf(msg, MSG_SIZE, "Network device %s is already up and running.\n"
357 " Exclusive access is required\n", real_devname);
360 if (request.ifr_flags & IFF_LOOPBACK) {
361 report_error("Loopback devices cannot be used for synthetic target ethernet emulation.\n");
363 if (request.ifr_flags & IFF_POINTOPOINT) {
364 report_error("Point-to-point devices cannot be used for synthetic target ethernet emulation.\n");
366 if (request.ifr_flags & IFF_MULTICAST) {
367 multicast_supported = 1;
370 // Make sure the interface is down. There is no point in receiving packets just yet.
371 real_update_ifflags(0, IFF_UP | IFF_PROMISC);
373 // The flags look ok. Now get hold of the hardware address.
374 strncpy(request.ifr_name, real_devname, IFNAMSIZ);
375 if (ioctl(ether_fd, SIOCGIFHWADDR, &request) < 0) {
376 snprintf(msg, MSG_SIZE, "Failed to get hardware address for %s\n"
377 " Error %s (errno %d)\n", real_devname, strerror(errno), errno);
380 if (ARPHRD_ETHER != request.ifr_hwaddr.sa_family) {
381 snprintf(msg, MSG_SIZE, "Device %s is not an ethernet device.\n", real_devname);
384 memcpy(MAC, request.ifr_hwaddr.sa_data, 6);
386 // The device is useable. Now just bind the socket to the appropriate address.
387 addr.sll_family = AF_PACKET;
388 addr.sll_protocol = htons(ETH_P_ALL);
389 addr.sll_ifindex = real_ifindex;
390 if (bind(ether_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
391 snprintf(msg, MSG_SIZE, "Failed to bind socket for direct hardware address to %s\n"
392 " Error %s (errno %d)\n", real_devname, strerror(errno), errno);
396 // Make sure the interface gets shut down when rawether exits.
399 // And that should be it.
402 // ----------------------------------------------------------------------------
405 // See /usr/src/linux-2.x.y/Documentation/networking/tuntap.txt for more
406 // information on the tun/tap driver.
408 // Basically during initialization this code opens /dev/net/tun, then
409 // performs a TUNSETIFF ioctl() to initialize it. This causes a
410 // new network device tap?? to appear. Any ethernet frames written
411 // by the Linux kernel to this device can be read from the
412 // dev/net/tun file descriptor, and ethernet frames can be written to
413 // the same descriptor. The net effect is a virtual ethernet segment
414 // with one interface managed by the Linux kernel and another
415 // interface (or, theoretically, several) accessible via the file
418 // The Linux kernel will invent a MAC address for its interface. An
419 // additional one is needed for eCos. This is either invented or
420 // specified in the target definition file.
422 // Old Linux kernels may not have the required support. This is detected
423 // by an autoconf test for <linux/if_tun.h>
424 #ifdef HAVE_LINUX_IF_TUN_H
427 tap_handle_tx(unsigned char* buffer, int size)
431 result = write(ether_fd, buffer, size);
433 fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
436 fprintf(stderr, " %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
437 buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
438 buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11],
439 buffer[12], buffer[13]);
443 // Receive a single packet from the socket. It is assumed that the
444 // tuntap code inside the kernel will preserve packet boundaries.
446 // For now it is assumed that all incoming packets are intended for
447 // eCos. That may not be accurate, and additional filtering in
448 // software might be appropriate. In promiscuous mode all packets
449 // should be accepted, obviously. Otherwise all broadcasts should
450 // be accepted, as should all messages intended for this specific
451 // interface's MAC address. Multicasts should be accepted only if
459 // select() has succeeded so this read() should never block.
460 size = read(ether_fd, rx_buffer + 4, MTU);
462 fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, strerror(errno), errno);
466 return; // Ignore errors, just go around the main loop again.
468 if ((size < 14) || (size > MTU)) {
469 return; // Invalid packet size. Discard the packet.
473 fprintf(stderr, " %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
474 rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7], rx_buffer[8], rx_buffer[9],
475 rx_buffer[10], rx_buffer[11], rx_buffer[12], rx_buffer[13], rx_buffer[14], rx_buffer[15],
476 rx_buffer[16], rx_buffer[17]);
480 // eCos is not currently expecting packets, so discard them.
484 // It looks this packet should get forwarded to eCos.
485 rx_buffer[0] = SYNTH_ETH_RX;
487 rx_buffer[2] = size & 0x00FF;
488 rx_buffer[3] = (size >> 8) & 0x00FF;
490 result = write(1, rx_buffer, 4 + size);
491 } while ((-1 == result) && (EINTR == errno));
493 if (result != (size + 4)) {
494 fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
499 // Nothing much can be done for start or stop. Just set the flag and
500 // let the rx and tx code discard packets when appropriate.
502 // For now the device is implicitly promiscuous and accepts all
503 // multicasts. Given the nature of a tap device it is unlikely that
504 // any packets will arrive which are not destined here.
505 // FIXME: this may have to change if bridging is enabled.
507 tap_handle_start(int promiscuous)
513 tap_handle_stop(void)
519 tap_handle_multiall(int on)
524 tap_init(int argc, char** argv)
526 char* devname = NULL;
531 tx_fn = &tap_handle_tx;
532 rx_fn = &tap_handle_rx;
533 start_fn = &tap_handle_start;
534 stop_fn = &tap_handle_stop;
535 multicast_fn = &tap_handle_multiall;
537 // Which device? By default let the system pick one, but the user
538 // can override this.
543 // Work out the MAC address. By default a random one is generated,
544 // but the user can specify one to avoid a source of randomness.
545 // This MAC address is not actually needed by any of the code here,
546 // but should be returned to eCos.
548 unsigned int mac_data[6]; // sscanf() needs unsigned ints
549 int result = sscanf(argv[1], "%x:%x:%x:%x:%x:%x",
550 &(mac_data[0]), &(mac_data[1]), &(mac_data[2]),
551 &(mac_data[3]), &(mac_data[4]), &(mac_data[5]));
553 if (strncmp(argv[1], "persistent", 10)) {
554 snprintf(msg, MSG_SIZE, "Invalid MAC address %s\n", argv[1]);
558 MAC[0] = mac_data[0];
559 MAC[1] = mac_data[1];
560 MAC[2] = mac_data[2];
561 MAC[3] = mac_data[3];
562 MAC[4] = mac_data[4];
563 MAC[5] = mac_data[5];
569 if ( 1 != have_mac) {
573 MAC[2] = rand() & 0x0FF;
574 MAC[3] = rand() & 0x0FF;
575 MAC[4] = rand() & 0x0FF;
576 MAC[5] = rand() & 0x0FF;
579 // Should we make the TAP device persistent. When persistence is
580 // enabled, the tap device is not removed when rawether
581 // exits. This makes daemons happier. They can keep running
582 // between invocations of rawether.
584 persistent = !strncmp(argv[1], "persistent", 10);
587 ether_fd = open("/dev/net/tun", O_RDWR);
589 snprintf(msg, MSG_SIZE, "Failed to open /dev/net/tun, errno %s (%d)\n", strerror(errno), errno);
593 memset(&ifr, 0, sizeof(ifr));
594 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
595 if (NULL != devname) {
596 strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
598 if (ioctl(ether_fd, TUNSETIFF, (void*)&ifr) < 0) {
599 snprintf(msg, MSG_SIZE, "Failed to initialize /dev/net/tun, errno %s (%d)\n", strerror(errno), errno);
603 // Supporting multicasts is a no-op
604 multicast_supported = 1;
607 if (ioctl(ether_fd, TUNSETPERSIST, 1) < 0) {
608 snprintf(msg, MSG_SIZE, "Failed to set persistent flag, errno %s (%d)\n",
609 strerror(errno), errno);
617 tap_init(int argc, char** argv)
619 snprintf(msg, MSG_SIZE, "Ethertap support was not available when the host-side support was built\n");
622 #endif // HAVE_LINUX_IF_TUN_H
624 // ----------------------------------------------------------------------------
625 // Receive a single request from ecosynth. This consists of a four-byte
626 // header, optionally followed by a tx packet. EOF indicates that
627 // ecosynth has exited, so this process should just exit immediately
628 // as well. Any problems should be reported to stderr, followed by
631 // Currently rawether is single-threaded. Theoretically this could
632 // cause a deadlock situation where the I/O auxiliary is trying to send
633 // rawether a request and is blocked on the write, while rawether is trying
634 // to send data to the I/O auxiliary. In practice the pipes should do
635 // enough buffering to avoid complications, especially since rawether
636 // gives priority to requests from the auxiliary.
639 handle_ecosynth_request(void)
641 unsigned char req[4];
645 result = read(0, req, 4);
647 // select() succeeded but no data. EOF. So exit
651 // EINTR? EAGAIN? The latter should not happen since the pipe
652 // has not been put into non-blocking mode.
653 if ((EINTR == errno) || (EAGAIN == errno)) {
656 fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n");
657 fprintf(stderr, " %s\n", strerror(errno));
662 fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n Expected 4 bytes, only received %d\n", result);
668 size = req[2] + (req[3] << 8);
671 fprintf(stderr, "rawether dbg: request %x from auxiliary\n", code);
678 fprintf(stderr, "rawether: attempt to send invalid ethernet packet of only %d bytes\n"
679 "Ethernet packets should be at least 14 bytes.\n", size);
683 fprintf(stderr, "rawether: attempt to send invalid ethernet packet of %d bytes\n"
684 "Only packets of up to %d bytes are supported.\n", size, MTU);
688 result = read(0, tx_buffer, size);
689 } while ((-1 == result) && (EINTR == errno));
691 // EOF, at an inopportune moment
695 fprintf(stderr, "rawether: error reading ethernet packet from I/O auxiliary\n"
696 "Expected %d bytes but only read %d\n", size, result);
700 (*tx_fn)(tx_buffer, size);
703 case SYNTH_ETH_START:
715 case SYNTH_ETH_MULTIALL:
717 (*multicast_fn)(arg);
721 // SYNTH_ETH_RX and SYNTH_ETH_GETPARAMS are handled inside ethernet.tcl
724 fprintf(stderr, "rawether: protocol violation, received unknown request %d\n", code);
729 // The main loop. This waits for an event either from ecosynth or from
730 // the underlying ethernet device, using select. Requests from
731 // ecosynth are handled, and take priority to prevent the connecting
732 // pipe from filling up and ecosynth blocking. Incoming ethernet
733 // frames are forwarded to ecosynth.
739 struct timeval timeout;
744 FD_SET(0, &read_set);
745 FD_SET(ether_fd, &read_set);
746 timeout.tv_sec = 24 * 60 * 60;
749 result = select(ether_fd + 1, &read_set, NULL, NULL, &timeout);
754 if (FD_ISSET(0, &read_set)) {
755 handle_ecosynth_request();
756 } else if (FD_ISSET(ether_fd, &read_set)) {
762 // ----------------------------------------------------------------------------
765 main(int argc, char**argv)
767 // Ignore incoming ctrl-C's. We are in the same process group as the
768 // eCos application which may sensibly be ctrl-C'd, but that should
769 // result in the auxiliary detecting EOF and closing the pipe to
770 // this process, which in turn causes this process to exit completely.
771 signal(SIGINT, SIG_IGN);
774 report_error("Expected at least one argument, \"real\" or \"ethertap\"\n");
776 if (0 == strcmp("real", argv[1])) {
779 } else if (0 == strcmp("ethertap", argv[1])) {
781 tap_init(argc - 2, argv + 2);
783 snprintf(msg, MSG_SIZE, "Invalid argument %s, expected \"real\" or \"ethertap\"\n", argv[1]);
787 // If the device-specific initialization succeeded we must be set.