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 + -
显示快捷键?