rawether.c
来自「开放源码实时操作系统源码.」· 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 eCos
static 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 ecosynth
static 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 1514
static 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 256
static unsigned char msg[MSG_SIZE];
// Report an error to ecosynth during initialization. This means a
// single byte 0, followed by a string.
static void
report_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 void
report_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 void
real_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 void
real_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 void
real_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 void
real_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 flag
static void
real_handle_stop(void)
{
up = 0;
real_update_ifflags(0, IFF_UP | IFF_PROMISC);
}
// Enabling/disabling multicast support.
static void
real_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 void
real_atexit(void)
{
if (up) {
real_update_ifflags(0, IFF_UP | IFF_PROMISC);
}
}
static void
real_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 + -
显示快捷键?