]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/devs/eth/synth/ecosynth/v2_0/host/rawether.c
Initial revision
[karo-tx-redboot.git] / packages / devs / eth / synth / ecosynth / v2_0 / host / rawether.c
1 //============================================================================
2 //
3 //     rawether.c
4 //
5 //     A utility program to perform low-level ethernet operations
6 //
7 //============================================================================
8 //####COPYRIGHTBEGIN####
9 //                                                                          
10 // ----------------------------------------------------------------------------
11 // Copyright (C) 2002 Bart Veer
12 //
13 // This file is part of the eCos host tools.
14 //
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) 
18 // any later version.
19 // 
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 
23 // more details.
24 // 
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 // ----------------------------------------------------------------------------
29 //                                                                          
30 //####COPYRIGHTEND####
31 //============================================================================
32 //#####DESCRIPTIONBEGIN####
33 //
34 // Author(s):   bartv
35 // Contact(s):  bartv, andrew.lunn@ascom.ch
36 // Date:        2002/08/07
37 // Version:     0.01
38 // Description:
39 //
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.
43 //
44 //####DESCRIPTIONEND####
45 //============================================================================
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <time.h>
51 #include <signal.h>
52 #include <limits.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <errno.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <sys/socket.h>
59 #include <sys/ioctl.h>
60 #include <net/if.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>
68 #endif
69
70 // The protocol between host and target is defined by a private
71 // target-side header.
72 #include "../src/protocol.h"
73
74 // Allow debug builds. Set this flag to 0, 1 or 2
75 #define DEBUG 0
76
77 // ----------------------------------------------------------------------------
78 // Statics.
79
80 // Are we using a real ethernet device or ethertap?
81 static int real_ether = 0;
82 static int ethertap   = 0;
83
84 // The six-byte MAC address, which must be returned to eCos
85 static unsigned char MAC[6];
86
87 // Does the driver support multicasting?
88 static int multicast_supported = 0;
89
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;
93
94 // Is the interface up?
95 static int up = 0;
96
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.
99 #define MTU 1514
100 static unsigned char tx_buffer[MTU];
101 static unsigned char rx_buffer[MTU+4];
102
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);
109
110
111 // ----------------------------------------------------------------------------
112 // A utility buffer for messages.
113 #define MSG_SIZE 256
114 static unsigned char msg[MSG_SIZE];
115
116 // Report an error to ecosynth during initialization. This means a
117 // single byte 0, followed by a string.
118 static void
119 report_error(char* msg)
120 {
121     write(1, "0", 1);
122     write(1, msg, strlen(msg));
123     close(1);
124     exit(0);
125 }
126
127 // Report success to ecosynth. This means a byte 1 followed by
128 // the MAC address.
129 static void
130 report_success(void)
131 {
132     write(1, "1", 1);
133     memcpy(msg, MAC, 6);
134     msg[6] = multicast_supported;
135     write(1, msg, 7);
136 }
137
138
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)).
143
144 // The device name. Needed for various ioctl()'s.
145 static char real_devname[IFNAMSIZ];
146
147 // The interface index.
148 static int real_ifindex = -1;
149
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.
154 static void
155 real_handle_tx(unsigned char* buffer, int size)
156 {
157     int result;
158
159     result = send(ether_fd, buffer, size, MSG_DONTWAIT);
160     if (result < 0) {
161         // It appears that one retry is worthwhile, to clear pending
162         // errors or something.
163         result = send(ether_fd, buffer, size, MSG_DONTWAIT);
164     }
165 #if (DEBUG > 0)
166     fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
167 #endif
168 #if (DEBUG > 1)
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]);
173 #endif    
174 }
175
176 // Receive a single ethernet frame, using the static rxbuffer. If the
177 // interface is not currently up discard it. Otherwise forward it on
178 // to ecosynth.
179
180 static void
181 real_handle_rx(void)
182 {
183     int             size;
184     int             result;
185
186     size = recv(ether_fd, rx_buffer + 4, MTU, MSG_TRUNC);
187     
188 #if (DEBUG > 0)
189     fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, strerror(errno), errno);
190 #endif
191     
192     if (size < 0) {
193         return;     // Ignore errors, just go around the main loop again.
194     }
195     if ((size < 14) || (size > MTU)) {
196         return;     // Invalid packet size. Discard the packet.
197     }
198
199 #if (DEBUG > 1)
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]);
204 #endif    
205
206     if (!up) {
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.
210         return;
211     }
212
213     // It looks this packet should get forwarded to eCos.
214     rx_buffer[0] = SYNTH_ETH_RX;
215     rx_buffer[1] = 0;
216     rx_buffer[2] = size & 0x00FF;
217     rx_buffer[3] = (size >> 8) & 0x00FF;
218     do {
219         result = write(1, rx_buffer, 4 + size);
220     } while ((-1 == result) && (EINTR == errno));
221
222     if (result != (size + 4)) {
223         fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
224         exit(1);
225     }
226 }
227
228 // Utility to manipulate interface flags. This involves retrieving the
229 // current flags, or'ing in some bits, and'ing out others, and updating.
230 static void
231 real_update_ifflags(int set_bits, int clear_bits)
232 {
233     struct ifreq    request;
234     int             flags;
235     
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);
239         exit(1);
240     }
241     
242     flags = request.ifr_flags;
243
244     flags |= set_bits;
245     flags &= ~clear_bits;
246
247     if (flags == request.ifr_flags) {
248         // Nothing is changing.
249         return;
250     }
251
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);
256         exit(1);
257     }
258 }
259
260
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
266 // this interface.
267
268 static void
269 real_handle_start(int promiscuous)
270 {
271     if (promiscuous) {
272         real_update_ifflags(IFF_UP | IFF_PROMISC, 0);
273     } else {
274         real_update_ifflags(IFF_UP, IFF_PROMISC);
275     }
276     up = 1;
277 }
278
279 // Stopping an interface means clearing the UP flag
280 static void
281 real_handle_stop(void)
282 {
283     up = 0;
284     real_update_ifflags(0, IFF_UP | IFF_PROMISC);
285 }
286
287 // Enabling/disabling multicast support.
288 static void
289 real_handle_multiall(int on)
290 {
291     struct packet_mreq req;
292
293     req.mr_ifindex  = real_ifindex;
294     req.mr_type     = PACKET_MR_ALLMULTI;
295     req.mr_alen     = 0;
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);
298         exit(1);
299     }
300 }
301
302 // When the application exists make sure that the interface goes down again.
303
304 static void
305 real_atexit(void)
306 {
307     if (up) {
308         real_update_ifflags(0, IFF_UP | IFF_PROMISC);
309     }
310 }
311
312 static void
313 real_init(char* devname)
314 {
315     struct sockaddr_ll  addr;
316     struct ifreq        request;
317
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;
323     
324     if (strlen(devname) >= IFNAMSIZ) {
325         snprintf(msg, MSG_SIZE, "Invalid real network device name \"%s\", too long.\n", devname);
326         report_error(msg);
327     }
328     strcpy(real_devname, devname);
329
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));
333     if (ether_fd < 0) {
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);
336         report_error(msg);
337     }
338     
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);
343         report_error(msg);
344     }
345     real_ifindex = request.ifr_ifindex;
346
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);
352         report_error(msg);
353     }
354
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);
358         report_error(msg);
359     }
360     if (request.ifr_flags & IFF_LOOPBACK) {
361         report_error("Loopback devices cannot be used for synthetic target ethernet emulation.\n");
362     }
363     if (request.ifr_flags & IFF_POINTOPOINT) {
364         report_error("Point-to-point devices cannot be used for synthetic target ethernet emulation.\n");
365     }
366     if (request.ifr_flags & IFF_MULTICAST) {
367         multicast_supported = 1;
368     }
369         
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);
372     
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);
378         report_error(msg);
379     }
380     if (ARPHRD_ETHER != request.ifr_hwaddr.sa_family) {
381         snprintf(msg, MSG_SIZE, "Device %s is not an ethernet device.\n", real_devname);
382         report_error(msg);
383     }
384     memcpy(MAC, request.ifr_hwaddr.sa_data, 6);
385
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);
393         report_error(msg);
394     }
395
396     // Make sure the interface gets shut down when rawether exits.
397     atexit(real_atexit);
398
399     // And that should be it.
400 }
401
402 // ----------------------------------------------------------------------------
403 // Ethertap device.
404 //
405 // See /usr/src/linux-2.x.y/Documentation/networking/tuntap.txt for more
406 // information on the tun/tap driver.
407 //
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
416 // descriptor.
417 //
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.
421 //
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
425
426 static void
427 tap_handle_tx(unsigned char* buffer, int size)
428 {
429     int result;
430
431     result = write(ether_fd, buffer, size);
432 #if (DEBUG > 0)
433     fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
434 #endif
435 #if (DEBUG > 1)
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]);
440 #endif    
441 }
442
443 // Receive a single packet from the socket. It is assumed that the
444 // tuntap code inside the kernel will preserve packet boundaries.
445 //
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
452 // enabled.
453 static void
454 tap_handle_rx(void)
455 {
456     int             size;
457     int             result;
458
459     // select() has succeeded so this read() should never block.
460     size = read(ether_fd, rx_buffer + 4, MTU);
461 #if (DEBUG > 0)
462     fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, strerror(errno), errno);
463 #endif
464     
465     if (size < 0) {
466         return;     // Ignore errors, just go around the main loop again.
467     }
468     if ((size < 14) || (size > MTU)) {
469         return;     // Invalid packet size. Discard the packet.
470     }
471
472 #if (DEBUG > 1)
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]);
477 #endif    
478
479     if (!up) {
480         // eCos is not currently expecting packets, so discard them.
481         return;
482     }
483
484     // It looks this packet should get forwarded to eCos.
485     rx_buffer[0] = SYNTH_ETH_RX;
486     rx_buffer[1] = 0;
487     rx_buffer[2] = size & 0x00FF;
488     rx_buffer[3] = (size >> 8) & 0x00FF;
489     do {
490         result = write(1, rx_buffer, 4 + size);
491     } while ((-1 == result) && (EINTR == errno));
492
493     if (result != (size + 4)) {
494         fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
495         exit(1);
496     }
497 }
498
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.
501 //
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.
506 static void
507 tap_handle_start(int promiscuous)
508 {
509     up = 1;
510 }
511
512 static void
513 tap_handle_stop(void)
514 {
515     up = 0;
516 }
517
518 static void
519 tap_handle_multiall(int on)
520 {
521 }
522
523 static void
524 tap_init(int argc, char** argv)
525 {
526     char* devname   = NULL;
527     struct ifreq    ifr;
528     int persistent  = 0;
529     int have_mac    = 0;
530
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;
536     
537     // Which device? By default let the system pick one, but the user
538     // can override this.
539     if (0 != argc) {
540         devname = argv[0];
541     }
542     
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.
547     if (2 <= argc) {
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]));
552         if (6 != result) {
553           if (strncmp(argv[1], "persistent", 10)) {
554             snprintf(msg, MSG_SIZE, "Invalid MAC address %s\n", argv[1]);
555             report_error(msg);
556           } 
557         } else {
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];
564           argv += 1;
565           argc -= 1;
566           have_mac = 1;
567         }
568     } 
569     if ( 1 != have_mac) {
570       srand(time(NULL));
571       MAC[0] = 0;
572       MAC[1] = 0x0FF;
573       MAC[2] = rand() & 0x0FF;
574       MAC[3] = rand() & 0x0FF;
575       MAC[4] = rand() & 0x0FF;
576       MAC[5] = rand() & 0x0FF;
577     }
578
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.
583     if (2 <= argc ) {
584       persistent = !strncmp(argv[1], "persistent", 10);
585     }
586     
587     ether_fd = open("/dev/net/tun", O_RDWR);
588     if (ether_fd < 0) {
589         snprintf(msg, MSG_SIZE, "Failed to open /dev/net/tun, errno %s (%d)\n", strerror(errno), errno);
590         report_error(msg);
591     }
592
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);
597     }
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);
600         report_error(msg);
601     }
602
603     // Supporting multicasts is a no-op
604     multicast_supported = 1;
605     
606     if (persistent) {
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);
610         report_error(msg);
611       }
612     }
613     // All done.
614 }
615 #else
616 static void
617 tap_init(int argc, char** argv)
618 {
619     snprintf(msg, MSG_SIZE, "Ethertap support was not available when the host-side support was built\n");
620     report_error(msg);
621 }
622 #endif  // HAVE_LINUX_IF_TUN_H
623
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
629 // termination.
630 //
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.
637
638 static void
639 handle_ecosynth_request(void)
640 {
641     unsigned char   req[4];
642     int             result;
643     int             code, arg, size;
644
645     result = read(0, req, 4);
646     if (result == 0) {
647         // select() succeeded but no data. EOF. So exit
648         exit(0);
649     }
650     if (result < 0) {
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)) {
654             return;
655         } else {
656             fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n");
657             fprintf(stderr, "    %s\n", strerror(errno));
658             exit(1);
659         }
660     }
661     if (result < 4) {
662         fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n    Expected 4 bytes, only received %d\n", result);
663         exit(1);
664     }
665
666     code = req[0];
667     arg  = req[1];
668     size = req[2] + (req[3] << 8);
669
670 #if (DEBUG > 1)
671     fprintf(stderr, "rawether dbg: request %x from auxiliary\n", code);
672 #endif    
673
674     switch(code) {
675       case SYNTH_ETH_TX:
676         {
677             if (size < 14) {
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);
680                 exit(1);
681             }
682             if (size > MTU) {
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);
685                 exit(1);
686             }
687             do {
688                 result = read(0, tx_buffer, size);
689             } while ((-1 == result) && (EINTR == errno));
690             if (0 == result) {
691                 // EOF, at an inopportune moment
692                 exit(0);
693             }
694             if (result < size) {
695                 fprintf(stderr, "rawether: error reading ethernet packet from I/O auxiliary\n"
696                         "Expected %d bytes but only read %d\n", size, result);
697                 exit(1);
698             }
699
700             (*tx_fn)(tx_buffer, size);
701             break;
702         }
703       case SYNTH_ETH_START:
704         {
705             (*start_fn)(arg);
706             break;
707         }
708
709       case SYNTH_ETH_STOP:
710         {
711             (*stop_fn)();
712             break;
713         }
714
715       case SYNTH_ETH_MULTIALL:
716         {
717             (*multicast_fn)(arg);
718             break;
719         }
720
721         // SYNTH_ETH_RX and SYNTH_ETH_GETPARAMS are handled inside ethernet.tcl
722         
723       default:
724         fprintf(stderr, "rawether: protocol violation, received unknown request %d\n", code);
725         exit(1);
726     }
727 }
728
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.
734
735 static void
736 mainloop(void)
737 {
738     fd_set          read_set;
739     struct timeval  timeout;
740     int result;
741
742     for ( ; ; ) {
743         FD_ZERO(&read_set);
744         FD_SET(0, &read_set);
745         FD_SET(ether_fd, &read_set);
746         timeout.tv_sec  = 24 * 60 * 60;
747         timeout.tv_usec = 0;
748
749         result = select(ether_fd + 1, &read_set, NULL, NULL, &timeout);
750         if (result <= 0) {
751             continue;
752         }
753
754         if (FD_ISSET(0, &read_set)) {
755             handle_ecosynth_request();
756         } else if (FD_ISSET(ether_fd, &read_set)) {
757             (*rx_fn)();
758         }
759     }
760 }
761
762 // ----------------------------------------------------------------------------
763
764 int
765 main(int argc, char**argv)
766 {
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);
772     
773     if (2 > argc ) {
774         report_error("Expected at least one argument, \"real\" or \"ethertap\"\n");
775     }
776     if (0 == strcmp("real", argv[1])) {
777         real_ether = 1;
778         real_init(argv[2]);
779     } else if (0 == strcmp("ethertap", argv[1])) {
780         ethertap = 1;
781         tap_init(argc - 2, argv + 2);
782     } else {
783         snprintf(msg, MSG_SIZE, "Invalid argument %s, expected \"real\" or \"ethertap\"\n", argv[1]);
784         report_error(msg);
785     }
786
787     // If the device-specific initialization succeeded we must be set.
788     report_success();
789
790     mainloop();
791     
792     return 0;
793 }
794
795