rawether.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 796 行 · 第 1/2 页

C
796
字号
    // And that should be it.
}

// ----------------------------------------------------------------------------
// Ethertap device.
//
// See /usr/src/linux-2.x.y/Documentation/networking/tuntap.txt for more
// information on the tun/tap driver.
//
// Basically during initialization this code opens /dev/net/tun, then
// performs a TUNSETIFF ioctl() to initialize it. This causes a
// new network device tap?? to appear. Any ethernet frames written
// by the Linux kernel to this device can be read from the
// dev/net/tun file descriptor, and ethernet frames can be written to
// the same descriptor. The net effect is a virtual ethernet segment
// with one interface managed by the Linux kernel and another
// interface (or, theoretically, several) accessible via the file
// descriptor.
//
// The Linux kernel will invent a MAC address for its interface. An
// additional one is needed for eCos. This is either invented or
// specified in the target definition file.
//
// Old Linux kernels may not have the required support. This is detected
// by an autoconf test for <linux/if_tun.h>
#ifdef HAVE_LINUX_IF_TUN_H

static void
tap_handle_tx(unsigned char* buffer, int size)
{
    int result;

    result = write(ether_fd, buffer, size);
#if (DEBUG > 0)
    fprintf(stderr, "rawether dbg: tx %d bytes -> %d\n", size, result);
#endif
#if (DEBUG > 1)
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
            buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5],
            buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11],
            buffer[12], buffer[13]);
#endif    
}

// Receive a single packet from the socket. It is assumed that the
// tuntap code inside the kernel will preserve packet boundaries.
//
// For now it is assumed that all incoming packets are intended for
// eCos. That may not be accurate, and additional filtering in
// software might be appropriate. In promiscuous mode all packets
// should be accepted, obviously. Otherwise all broadcasts should
// be accepted, as should all messages intended for this specific
// interface's MAC address. Multicasts should be accepted only if
// enabled.
static void
tap_handle_rx(void)
{
    int             size;
    int             result;

    // select() has succeeded so this read() should never block.
    size = read(ether_fd, rx_buffer + 4, MTU);
#if (DEBUG > 0)
    fprintf(stderr, "rawether dbg: rx returned %d, errno %s (%d)\n", size, strerror(errno), errno);
#endif
    
    if (size < 0) {
        return;     // Ignore errors, just go around the main loop again.
    }
    if ((size < 14) || (size > MTU)) {
        return;     // Invalid packet size. Discard the packet.
    }

#if (DEBUG > 1)
    fprintf(stderr, "    %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x %x:%x\n",
            rx_buffer[4], rx_buffer[5], rx_buffer[6], rx_buffer[7], rx_buffer[8], rx_buffer[9],
            rx_buffer[10], rx_buffer[11], rx_buffer[12], rx_buffer[13], rx_buffer[14], rx_buffer[15],
            rx_buffer[16], rx_buffer[17]);
#endif    

    if (!up) {
        // eCos is not currently expecting packets, so discard them.
        return;
    }

    // It looks this packet should get forwarded to eCos.
    rx_buffer[0] = SYNTH_ETH_RX;
    rx_buffer[1] = 0;
    rx_buffer[2] = size & 0x00FF;
    rx_buffer[3] = (size >> 8) & 0x00FF;
    do {
        result = write(1, rx_buffer, 4 + size);
    } while ((-1 == result) && (EINTR == errno));

    if (result != (size + 4)) {
        fprintf(stderr, "rawether(%s): failed to send ethernet packet to I/O auxiliary, exiting.\n", real_devname);
        exit(1);
    }
}

// Nothing much can be done for start or stop. Just set the flag and
// let the rx and tx code discard packets when appropriate.
//
// For now the device is implicitly promiscuous and accepts all
// multicasts. Given the nature of a tap device it is unlikely that
// any packets will arrive which are not destined here.
// FIXME: this may have to change if bridging is enabled.
static void
tap_handle_start(int promiscuous)
{
    up = 1;
}

static void
tap_handle_stop(void)
{
    up = 0;
}

static void
tap_handle_multiall(int on)
{
}

static void
tap_init(int argc, char** argv)
{
    char* devname   = NULL;
    struct ifreq    ifr;
    int persistent  = 0;
    int have_mac    = 0;

    tx_fn           = &tap_handle_tx;
    rx_fn           = &tap_handle_rx;
    start_fn        = &tap_handle_start;
    stop_fn         = &tap_handle_stop;
    multicast_fn    = &tap_handle_multiall;
    
    // Which device? By default let the system pick one, but the user
    // can override this.
    if (0 != argc) {
        devname = argv[0];
    }
    
    // Work out the MAC address. By default a random one is generated,
    // but the user can specify one to avoid a source of randomness.
    // This MAC address is not actually needed by any of the code here,
    // but should be returned to eCos.
    if (2 <= argc) {
        unsigned int mac_data[6];   // sscanf() needs unsigned ints
	int result = sscanf(argv[1], "%x:%x:%x:%x:%x:%x",
			    &(mac_data[0]), &(mac_data[1]), &(mac_data[2]),
			    &(mac_data[3]), &(mac_data[4]), &(mac_data[5]));
	if (6 != result) {
	  if (strncmp(argv[1], "persistent", 10)) {
	    snprintf(msg, MSG_SIZE, "Invalid MAC address %s\n", argv[1]);
	    report_error(msg);
	  } 
	} else {
	  MAC[0] = mac_data[0];
	  MAC[1] = mac_data[1];
	  MAC[2] = mac_data[2];
	  MAC[3] = mac_data[3];
	  MAC[4] = mac_data[4];
	  MAC[5] = mac_data[5];
	  argv += 1;
	  argc -= 1;
	  have_mac = 1;
	}
    } 
    if ( 1 != have_mac) {
      srand(time(NULL));
      MAC[0] = 0;
      MAC[1] = 0x0FF;
      MAC[2] = rand() & 0x0FF;
      MAC[3] = rand() & 0x0FF;
      MAC[4] = rand() & 0x0FF;
      MAC[5] = rand() & 0x0FF;
    }

    // Should we make the TAP device persistent. When persistence is
    // enabled, the tap device is not removed when rawether
    // exits. This makes daemons happier. They can keep running
    // between invocations of rawether.
    if (2 <= argc ) {
      persistent = !strncmp(argv[1], "persistent", 10);
    }
    
    ether_fd = open("/dev/net/tun", O_RDWR);
    if (ether_fd < 0) {
        snprintf(msg, MSG_SIZE, "Failed to open /dev/net/tun, errno %s (%d)\n", strerror(errno), errno);
        report_error(msg);
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
    if (NULL != devname) {
        strncpy(ifr.ifr_name, devname, IFNAMSIZ - 1);
    }
    if (ioctl(ether_fd, TUNSETIFF, (void*)&ifr) < 0) {
        snprintf(msg, MSG_SIZE, "Failed to initialize /dev/net/tun, errno %s (%d)\n", strerror(errno), errno);
        report_error(msg);
    }

    // Supporting multicasts is a no-op
    multicast_supported = 1;
    
    if (persistent) {
      if (ioctl(ether_fd, TUNSETPERSIST, 1) < 0) {
	snprintf(msg, MSG_SIZE, "Failed to set persistent flag, errno %s (%d)\n",
		 strerror(errno), errno);
	report_error(msg);
      }
    }
    // All done.
}
#else
static void
tap_init(int argc, char** argv)
{
    snprintf(msg, MSG_SIZE, "Ethertap support was not available when the host-side support was built\n");
    report_error(msg);
}
#endif  // HAVE_LINUX_IF_TUN_H

// ----------------------------------------------------------------------------
// Receive a single request from ecosynth. This consists of a four-byte
// header, optionally followed by a tx packet. EOF indicates that
// ecosynth has exited, so this process should just exit immediately
// as well. Any problems should be reported to stderr, followed by
// termination.
//
// Currently rawether is single-threaded. Theoretically this could
// cause a deadlock situation where the I/O auxiliary is trying to send
// rawether a request and is blocked on the write, while rawether is trying
// to send data to the I/O auxiliary. In practice the pipes should do
// enough buffering to avoid complications, especially since rawether
// gives priority to requests from the auxiliary.

static void
handle_ecosynth_request(void)
{
    unsigned char   req[4];
    int             result;
    int             code, arg, size;

    result = read(0, req, 4);
    if (result == 0) {
        // select() succeeded but no data. EOF. So exit
        exit(0);
    }
    if (result < 0) {
        // EINTR? EAGAIN? The latter should not happen since the pipe
        // has not been put into non-blocking mode.
        if ((EINTR == errno) || (EAGAIN == errno)) {
            return;
        } else {
            fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n");
            fprintf(stderr, "    %s\n", strerror(errno));
            exit(1);
        }
    }
    if (result < 4) {
        fprintf(stderr, "rawether: unexpected error reading request from ecosynth\n    Expected 4 bytes, only received %d\n", result);
        exit(1);
    }

    code = req[0];
    arg  = req[1];
    size = req[2] + (req[3] << 8);

#if (DEBUG > 1)
    fprintf(stderr, "rawether dbg: request %x from auxiliary\n", code);
#endif    

    switch(code) {
      case SYNTH_ETH_TX:
        {
            if (size < 14) {
                fprintf(stderr, "rawether: attempt to send invalid ethernet packet of only %d bytes\n"
                        "Ethernet packets should be at least 14 bytes.\n", size);
                exit(1);
            }
            if (size > MTU) {
                fprintf(stderr, "rawether: attempt to send invalid ethernet packet of %d bytes\n"
                        "Only packets of up to %d bytes are supported.\n", size, MTU);
                exit(1);
            }
            do {
                result = read(0, tx_buffer, size);
            } while ((-1 == result) && (EINTR == errno));
            if (0 == result) {
                // EOF, at an inopportune moment
                exit(0);
            }
            if (result < size) {
                fprintf(stderr, "rawether: error reading ethernet packet from I/O auxiliary\n"
                        "Expected %d bytes but only read %d\n", size, result);
                exit(1);
            }

            (*tx_fn)(tx_buffer, size);
            break;
        }
      case SYNTH_ETH_START:
        {
            (*start_fn)(arg);
            break;
        }

      case SYNTH_ETH_STOP:
        {
            (*stop_fn)();
            break;
        }

      case SYNTH_ETH_MULTIALL:
        {
            (*multicast_fn)(arg);
            break;
        }

        // SYNTH_ETH_RX and SYNTH_ETH_GETPARAMS are handled inside ethernet.tcl
        
      default:
        fprintf(stderr, "rawether: protocol violation, received unknown request %d\n", code);
        exit(1);
    }
}

// The main loop. This waits for an event either from ecosynth or from
// the underlying ethernet device, using select. Requests from
// ecosynth are handled, and take priority to prevent the connecting
// pipe from filling up and ecosynth blocking. Incoming ethernet
// frames are forwarded to ecosynth.

static void
mainloop(void)
{
    fd_set          read_set;
    struct timeval  timeout;
    int result;

    for ( ; ; ) {
        FD_ZERO(&read_set);
        FD_SET(0, &read_set);
        FD_SET(ether_fd, &read_set);
        timeout.tv_sec  = 24 * 60 * 60;
        timeout.tv_usec = 0;

        result = select(ether_fd + 1, &read_set, NULL, NULL, &timeout);
        if (result <= 0) {
            continue;
        }

        if (FD_ISSET(0, &read_set)) {
            handle_ecosynth_request();
        } else if (FD_ISSET(ether_fd, &read_set)) {
            (*rx_fn)();
        }
    }
}

// ----------------------------------------------------------------------------

int
main(int argc, char**argv)
{
    // Ignore incoming ctrl-C's. We are in the same process group as the
    // eCos application which may sensibly be ctrl-C'd, but that should
    // result in the auxiliary detecting EOF and closing the pipe to
    // this process, which in turn causes this process to exit completely.
    signal(SIGINT, SIG_IGN);
    
    if (2 > argc ) {
        report_error("Expected at least one argument, \"real\" or \"ethertap\"\n");
    }
    if (0 == strcmp("real", argv[1])) {
        real_ether = 1;
        real_init(argv[2]);
    } else if (0 == strcmp("ethertap", argv[1])) {
        ethertap = 1;
        tap_init(argc - 2, argv + 2);
    } else {
        snprintf(msg, MSG_SIZE, "Invalid argument %s, expected \"real\" or \"ethertap\"\n", argv[1]);
        report_error(msg);
    }

    // If the device-specific initialization succeeded we must be set.
    report_success();

    mainloop();
    
    return 0;
}


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?