rawether.c
来自「eCos操作系统源码」· C语言 代码 · 共 796 行 · 第 1/2 页
C
796 行
//============================================================================//// rawether.c//// A utility program to perform low-level ethernet operations////============================================================================//####COPYRIGHTBEGIN####// // ----------------------------------------------------------------------------// Copyright (C) 2002 Bart Veer//// This file is part of the eCos host tools.//// This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 of the License, or (at your option) // any later version.// // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details.// // You should have received a copy of the GNU General Public License along with// this program; if not, write to the Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.// ----------------------------------------------------------------------------// //####COPYRIGHTEND####//============================================================================//#####DESCRIPTIONBEGIN####//// Author(s): bartv// Contact(s): bartv, andrew.lunn@ascom.ch// Date: 2002/08/07// Version: 0.01// Description://// This program is fork'ed by the ethernet.tcl script running inside// the synthetic target auxiliary. It is responsible for performing// low-level ethernet I/O.////####DESCRIPTIONEND####//============================================================================#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include <signal.h>#include <limits.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <sys/param.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <net/if.h>#include <net/if_arp.h>#include <netinet/in.h>#include <netinet/if_ether.h>#include <linux/if_packet.h>#include <linux/if_ether.h>#ifdef HAVE_LINUX_IF_TUN_H# include <linux/if_tun.h>#endif// The protocol between host and target is defined by a private// target-side header.#include "../src/protocol.h"// Allow debug builds. Set this flag to 0, 1 or 2#define DEBUG 0// ----------------------------------------------------------------------------// Statics.// Are we using a real ethernet device or ethertap?static int real_ether = 0;static int ethertap = 0;// The six-byte MAC address, which must be returned to eCosstatic unsigned char MAC[6];// Does the driver support multicasting?static int multicast_supported = 0;// The file descriptor for incoming data ethernet packets.// Used for select() together with fd 0 corresponding to ecosynthstatic int ether_fd = -1;// Is the interface up?static int up = 0;// Space for incoming and outgoing packets. In the case of rx_buffer// there are an extra four bytes at the front for the protocol header.#define MTU 1514static unsigned char tx_buffer[MTU];static unsigned char rx_buffer[MTU+4];// Indirect to get to the actual implementation functions.static void (*tx_fn)(unsigned char*, int);static void (*rx_fn)(void);static void (*start_fn)(int);static void (*stop_fn)(void);static void (*multicast_fn)(int);// ----------------------------------------------------------------------------// A utility buffer for messages.#define MSG_SIZE 256static unsigned char msg[MSG_SIZE];// Report an error to ecosynth during initialization. This means a// single byte 0, followed by a string.static voidreport_error(char* msg){ write(1, "0", 1); write(1, msg, strlen(msg)); close(1); exit(0);}// Report success to ecosynth. This means a byte 1 followed by// the MAC address.static voidreport_success(void){ write(1, "1", 1); memcpy(msg, MAC, 6); msg[6] = multicast_supported; write(1, msg, 7);}// ----------------------------------------------------------------------------// Real ethernet. This involves creating a SOCK_RAW socket and binding it// to the appropriate interface. Relevant documentation can be found in// the man pages (packet(7) and netdevice(7)).// The device name. Needed for various ioctl()'s.static char real_devname[IFNAMSIZ];// The interface index.static int real_ifindex = -1;// Transmit a single ethernet frame. The socket should be set up so a// simple send() operation should do the trick. Errors such as EAGAIN,// indicating that the network device is still busy, are ignored.// Ethernet is not a reliable communication medium.static voidreal_handle_tx(unsigned char* buffer, int size){ int result; result = send(ether_fd, buffer, size, MSG_DONTWAIT); if (result < 0) { // It appears that one retry is worthwhile, to clear pending // errors or something. result = send(ether_fd, buffer, size, MSG_DONTWAIT); }#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 ethernet frame, using the static rxbuffer. If the// interface is not currently up discard it. Otherwise forward it on// to ecosynth.static voidreal_handle_rx(void){ int size; int result; size = recv(ether_fd, rx_buffer + 4, MTU, MSG_TRUNC); #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. // This may not actually be necessary because the interface // is only up when eCos wants it to be up. 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); }}// Utility to manipulate interface flags. This involves retrieving the// current flags, or'ing in some bits, and'ing out others, and updating.static voidreal_update_ifflags(int set_bits, int clear_bits){ struct ifreq request; int flags; strncpy(request.ifr_name, real_devname, IFNAMSIZ); if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) { fprintf(stderr, "rawether (%s): failed to get interface flags, exiting\n", real_devname); exit(1); } flags = request.ifr_flags; flags |= set_bits; flags &= ~clear_bits; if (flags == request.ifr_flags) { // Nothing is changing. return; } strncpy(request.ifr_name, real_devname, IFNAMSIZ); request.ifr_flags = flags; if (ioctl(ether_fd, SIOCSIFFLAGS, &request) < 0) { fprintf(stderr, "rawether (%s): failed to update interface flags, exiting\n", real_devname); exit(1); }}// Starting an interface. This involves bringing the interface up,// and optionally setting promiscuous mode.// NOTE: is UP really the right thing here? There is no IP address// for this interface. In theory this should not matter because// we have a bound socket which should receive all packets for// this interface.static voidreal_handle_start(int promiscuous){ if (promiscuous) { real_update_ifflags(IFF_UP | IFF_PROMISC, 0); } else { real_update_ifflags(IFF_UP, IFF_PROMISC); } up = 1;}// Stopping an interface means clearing the UP flagstatic voidreal_handle_stop(void){ up = 0; real_update_ifflags(0, IFF_UP | IFF_PROMISC);}// Enabling/disabling multicast support.static voidreal_handle_multiall(int on){ struct packet_mreq req; req.mr_ifindex = real_ifindex; req.mr_type = PACKET_MR_ALLMULTI; req.mr_alen = 0; if (setsockopt(ether_fd, SOL_PACKET, on ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, (void*)&req, sizeof(req)) < 0) { fprintf(stderr, "rawether (%s): failed to manipulate multicast-all flag, exiting\n", real_devname); exit(1); }}// When the application exists make sure that the interface goes down again.static voidreal_atexit(void){ if (up) { real_update_ifflags(0, IFF_UP | IFF_PROMISC); }}static voidreal_init(char* devname){ struct sockaddr_ll addr; struct ifreq request; tx_fn = &real_handle_tx; rx_fn = &real_handle_rx; start_fn = &real_handle_start; stop_fn = &real_handle_stop; multicast_fn = &real_handle_multiall; if (strlen(devname) >= IFNAMSIZ) { snprintf(msg, MSG_SIZE, "Invalid real network device name \"%s\", too long.\n", devname); report_error(msg); } strcpy(real_devname, devname); // All ioctl() operations need a socket. We might as well create the // raw socket immediately and use that. ether_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (ether_fd < 0) { snprintf(msg, MSG_SIZE, "Unable to create a raw socket for accessing network device\n" " Error %s (errno %d)\n", strerror(errno), errno); report_error(msg); } strncpy(request.ifr_name, real_devname, IFNAMSIZ); if (ioctl(ether_fd, SIOCGIFINDEX, &request) < 0) { snprintf(msg, MSG_SIZE, "Device %s does not correspond to a valid interface.\n" " Error %s (errno %d)\n", real_devname, strerror(errno), errno); report_error(msg); } real_ifindex = request.ifr_ifindex; // The interface exists. Now check that it is usable. strncpy(request.ifr_name, real_devname, IFNAMSIZ); if (ioctl(ether_fd, SIOCGIFFLAGS, &request) < 0) { snprintf(msg, MSG_SIZE, "Failed to get current interface flags for %s\n" " Error %s (errno %d)\n", real_devname, strerror(errno), errno); report_error(msg); } if (request.ifr_flags & (IFF_UP | IFF_RUNNING)) { snprintf(msg, MSG_SIZE, "Network device %s is already up and running.\n" " Exclusive access is required\n", real_devname); report_error(msg); } if (request.ifr_flags & IFF_LOOPBACK) { report_error("Loopback devices cannot be used for synthetic target ethernet emulation.\n"); } if (request.ifr_flags & IFF_POINTOPOINT) { report_error("Point-to-point devices cannot be used for synthetic target ethernet emulation.\n"); } if (request.ifr_flags & IFF_MULTICAST) { multicast_supported = 1; } // Make sure the interface is down. There is no point in receiving packets just yet. real_update_ifflags(0, IFF_UP | IFF_PROMISC); // The flags look ok. Now get hold of the hardware address. strncpy(request.ifr_name, real_devname, IFNAMSIZ); if (ioctl(ether_fd, SIOCGIFHWADDR, &request) < 0) { snprintf(msg, MSG_SIZE, "Failed to get hardware address for %s\n" " Error %s (errno %d)\n", real_devname, strerror(errno), errno); report_error(msg); } if (ARPHRD_ETHER != request.ifr_hwaddr.sa_family) { snprintf(msg, MSG_SIZE, "Device %s is not an ethernet device.\n", real_devname); report_error(msg); } memcpy(MAC, request.ifr_hwaddr.sa_data, 6); // The device is useable. Now just bind the socket to the appropriate address. addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); addr.sll_ifindex = real_ifindex; if (bind(ether_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { snprintf(msg, MSG_SIZE, "Failed to bind socket for direct hardware address to %s\n" " Error %s (errno %d)\n", real_devname, strerror(errno), errno); report_error(msg); } // Make sure the interface gets shut down when rawether exits. atexit(real_atexit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?