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