tap.cc
来自「linux下基于c++的处理器仿真平台。具有处理器流水线」· CC 代码 · 共 544 行
CC
544 行
/* * Copyright (c) 2003, 2004, 2005 * The Regents of The University of Michigan * All Rights Reserved * * This code is part of the M5 simulator, developed by Nathan Binkert, * Erik Hallnor, Steve Raasch, and Steve Reinhardt, with contributions * from Ron Dreslinski, Dave Greene, Lisa Hsu, Kevin Lim, Ali Saidi, * and Andrew Schultz. * * 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. */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 + -
显示快捷键?