tap.cc

来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· CC 代码 · 共 543 行

CC
543
字号
/* * Copyright (c) 2003, 2004, 2005 * The Regents of The University of Michigan * All Rights Reserved * * This code is part of the M5 simulator. * * Permission is granted to use, copy, create derivative works and * redistribute this software and such derivative works for any * purpose, so long as the copyright notice above, this grant of * permission, and the disclaimer below appear in all copies made; and * so long as the name of The University of Michigan is not used in * any advertising or publicity pertaining to the use or distribution * of this software without specific, written prior authorization. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH * DAMAGES. * * Authors: Nathan L. Binkert */extern "C" {#include <pcap.h>}#include <dnet.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <errno.h>#include <fcntl.h>#include <libgen.h>#include <netdb.h>#include <poll.h>#include <signal.h>#include <unistd.h>#include <list>#include <string>#include "base/cprintf.hh"#define panic(arg...) \  do { cprintf("Panic: " arg); exit(1); } while (0)char *program = "ethertap";voidusage(){    cprintf(        "usage: \n"        "\t%s [-b bufsize] [-d] [-f filter] [-p port] [-v] <device> <host>\n"        "\t%s [-b bufsize] [-d] [-f filter] [-l] [-p port] [-v] <device>\n",        program, program);    exit(2);}int verbose = 0;#define DPRINTF(args...) do { \    if (verbose >= 1) \        cprintf(args); \} while (0)#define DDUMP(args...) do { \    if (verbose >= 2) \        dump((const u_char *)args); \} while (0)voiddump(const u_char *data, int len){        int c, i, j;        for (i = 0; i < len; i += 16) {                cprintf("%08x  ", i);                c = len - i;                if (c > 16) c = 16;                for (j = 0; j < c; j++) {                        cprintf("%02x ", data[i + j] & 0xff);                        if ((j & 0xf) == 7 && j > 0)                                cprintf(" ");                }                for (; j < 16; j++)                        cprintf("   ");                cprintf("  ");                for (j = 0; j < c; j++) {                        int ch = data[i + j] & 0x7f;                        cprintf("%c", (char)(isprint(ch) ? ch : ' '));                }                cprintf("\n");                if (c < 16)                        break;        }}bool quit = false;voidquit_now(int sigtype){    DPRINTF("User requested exit\n");    quit = true;}intSocket(int reuse){    int fd = ::socket(PF_INET, SOCK_STREAM, 0);    if (fd < 0)        panic("Can't create socket!\n");    if (reuse) {        int i = 1;        if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&i,                         sizeof(i)) < 0)            panic("setsockopt() SO_REUSEADDR failed!\n");    }    return fd;}voidListen(int fd, int port){    struct sockaddr_in sockaddr;    sockaddr.sin_family = PF_INET;    sockaddr.sin_addr.s_addr = INADDR_ANY;    sockaddr.sin_port = htons(port);    int ret = ::bind(fd, (struct sockaddr *)&sockaddr, sizeof (sockaddr));    if (ret == -1)        panic("bind() failed!\n");    if (::listen(fd, 1) == -1)        panic("listen() failed!\n");}// Open a connection.  Accept will block, so if you don't want it to,// make sure a connection is ready before you call accept.intAccept(int fd, bool nodelay){    struct sockaddr_in sockaddr;    socklen_t slen = sizeof (sockaddr);    int sfd = ::accept(fd, (struct sockaddr *)&sockaddr, &slen);    if (sfd == -1)        panic("accept() failed!\n");    if (nodelay) {        int i = 1;        ::setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i));    }    return sfd;}voidConnect(int fd, const std::string &host, int port){    struct sockaddr_in sockaddr;    if (::inet_aton(host.c_str(), &sockaddr.sin_addr) == 0) {        struct hostent *hp;        hp = ::gethostbyname(host.c_str());        if (!hp)            panic("Host %s not found\n", host);        sockaddr.sin_family = hp->h_addrtype;        memcpy(&sockaddr.sin_addr, hp->h_addr, hp->h_length);    }    sockaddr.sin_port = htons(port);    if (::connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0)        panic("could not connect to %s on port %d\n", host, port);    DPRINTF("connected to %s on port %d\n", host, port);}class Ethernet{  protected:    int fd;  public:    virtual ~Ethernet() {}    int getfd() const { return fd; }    virtual bool read(const char *&data, int &len) = 0;    virtual bool write(const char *data, int len) = 0;};class Tap : public Ethernet{  private:    char buffer[65536];    int fd;  public:    Tap(char *device);    ~Tap();    virtual bool read(const char *&data, int &len);    virtual bool write(const char *data, int len);};class PCap : public Ethernet{  private:    pcap_t *pcap;    eth_t *ethernet;  public:    PCap(char *device, char *filter = NULL);    ~PCap();    virtual bool read(const char *&data, int &len);    virtual bool write(const char *data, int len);};PCap::PCap(char *device, char *filter){    char errbuf[PCAP_ERRBUF_SIZE];    memset(errbuf, 0, sizeof errbuf);    pcap = pcap_open_live(device, 1500, 1, -1, errbuf);    if (pcap == NULL)        panic("pcap_open_live failed: %s\n", errbuf);    if (filter) {        bpf_program program;        bpf_u_int32 localnet, netmask;        if (pcap_lookupnet(device, &localnet, &netmask, errbuf) == -1) {            DPRINTF("pcap_lookupnet failed: %s\n", errbuf);            netmask = 0xffffff00;        }        if (pcap_compile(pcap, &program, filter, 1, netmask) == -1)            panic("pcap_compile failed, invalid filter:\n%s\n", filter);        if (pcap_setfilter(pcap, &program) == -1)            panic("pcap_setfilter failed\n");    }    ethernet = eth_open(device);    if (!ethernet)        panic("cannot open the ethernet device for writing\n");    fd = pcap_fileno(pcap);}PCap::~PCap(){    pcap_close(pcap);    eth_close(ethernet);}boolPCap::read(const char *&data, int &len){    pcap_pkthdr hdr;    data = (const char *)pcap_next(pcap, &hdr);    if (!data)        return false;    len = hdr.len;    return true;}boolPCap::write(const char *data, int len){    eth_send(ethernet, data, len);}Tap::Tap(char *device){    fd = open(device, O_RDWR, 0);    if (fd < 0)        panic("could not open %s: %s\n", device, strerror(errno));}Tap::~Tap(){    close(fd);}boolTap::read(const char *&data, int &len){    DPRINTF("tap read!\n");    data = buffer;    len = ::read(fd, buffer, sizeof(buffer));    if (len < 0)        return false;    return true;}boolTap::write(const char *data, int len){    int result = ::write(fd, data, len);    if (result < 0)        return false;    return true;}intmain(int argc, char *argv[]){    int port = 3500;    int bufsize = 2000;    bool listening = false;    char *device = NULL;    char *filter = NULL;    Ethernet *tap = NULL;    bool usetap = false;    char c;    int daemon = false;    std::string host;    int devfd;    program = basename(argv[0]);    while ((c = getopt(argc, argv, "b:df:lp:tv")) != -1) {        switch (c) {          case 'b':            bufsize = atoi(optarg);            break;          case 'd':            daemon = true;            break;          case 'f':            filter = optarg;            break;          case 'l':            listening = true;            break;          case 'p':            port = atoi(optarg);            break;          case 't':            usetap = true;            break;          case 'v':            verbose++;            break;          default:            usage();            break;        }    }    signal(SIGINT, quit_now);    signal(SIGTERM, quit_now);    signal(SIGHUP, quit_now);    if (daemon) {        verbose = 0;        switch(fork()) {          case -1:            panic("Fork failed\n");          case 0:            break;          default:            exit(0);        }    }    char *buffer = new char[bufsize];    argc -= optind;    argv += optind;    if (argc-- == 0)        usage();    device = *argv++;    if (listening) {        if (argc)            usage();    } else {        if (argc != 1)            usage();        host = *argv;    }    if (usetap) {        if (filter)            panic("-f parameter not valid with a tap device!");        tap = new Tap(device);    } else {        tap = new PCap(device, filter);    }    pollfd pfds[3];    pfds[0].fd = Socket(true);    pfds[0].events = POLLIN;    pfds[0].revents = 0;    if (listening)        Listen(pfds[0].fd, port);    else        Connect(pfds[0].fd, host, port);    pfds[1].fd = tap->getfd();    pfds[1].events = POLLIN;    pfds[1].revents = 0;    pfds[2].fd = 0;    pfds[2].events = POLLIN|POLLERR;    pfds[2].revents = 0;    pollfd *listen_pfd = listening ? &pfds[0] : NULL;    pollfd *tap_pfd = &pfds[1];    pollfd *client_pfd = listening ? NULL : &pfds[0];    int npfds = 2;    int32_t buffer_offset = 0;    int32_t data_len = 0;    DPRINTF("Begin poll loop\n");    while (!quit) {        int ret = ::poll(pfds, npfds, INFTIM);        if (ret < 0)            continue;        if (listen_pfd && listen_pfd->revents) {            if (listen_pfd->revents & POLLIN) {                int fd = Accept(listen_pfd->fd, false);                if (client_pfd) {                    DPRINTF("Connection rejected\n");                    close(fd);                } else {                    DPRINTF("Connection accepted\n");                    client_pfd = &pfds[2];                    client_pfd->fd = fd;                    npfds++;                }            }            listen_pfd->revents = 0;        }        DPRINTF("tap events: %x\n", tap_pfd->revents);        if (tap_pfd && tap_pfd->revents) {            if (tap_pfd->revents & POLLIN) {                const char *data; int len;                if (tap->read(data, len) && client_pfd) {                    DPRINTF("Received packet from ethernet len=%d\n", len);                    DDUMP(data, len);                    u_int32_t swaplen = htonl(len);                    write(client_pfd->fd, &swaplen, sizeof(swaplen));                    write(client_pfd->fd, data, len);                }            }            tap_pfd->revents = 0;        }        if (client_pfd && client_pfd->revents) {            if (client_pfd->revents & POLLIN) {                if (buffer_offset < data_len + sizeof(u_int32_t)) {                    int len = read(client_pfd->fd, buffer + buffer_offset,                                   bufsize - buffer_offset);                    if (len <= 0) {                        perror("read");                        goto error;                    }                    buffer_offset += len;                    if (data_len == 0)                        data_len = ntohl(*(u_int32_t *)buffer);                    DPRINTF("Received data from peer: len=%d buffer_offset=%d "                            "data_len=%d\n", len, buffer_offset, data_len);                }                while (data_len != 0 &&                       buffer_offset >= data_len + sizeof(u_int32_t)) {                    char *data = buffer + sizeof(u_int32_t);                    tap->write(data, data_len);                    DPRINTF("Sent packet to ethernet len = %d\n", data_len);                    DDUMP(data, data_len);                    buffer_offset -= data_len + sizeof(u_int32_t);                    if (buffer_offset > 0 && data_len > 0) {                        memmove(buffer, data + data_len, buffer_offset);                        data_len = ntohl(*(u_int32_t *)buffer);                    } else                        data_len = 0;                }            }            if (client_pfd->revents & POLLERR) {              error:                DPRINTF("Error on client socket\n");                close(client_pfd->fd);                client_pfd = NULL;                if (listening)                    npfds--;                else {                    DPRINTF("Calling it quits because of poll error\n");                    quit = true;                }            }            if (client_pfd)                client_pfd->revents = 0;        }    }    delete [] buffer;    delete tap;    if (listen_pfd)        close(listen_pfd->fd);    if (client_pfd)        close(client_pfd->fd);    return 0;}

⌨️ 快捷键说明

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