Use printf instead of cprintf to avoid having to have .o files from gem5. Stop disabling optimization. Placate the style checker by rearranging the header include order. Include some missing standard headers. Switch from the deprecated -I- gcc option to using -iquote. Make the "program" variable a const char *. When checking the return value of getopt, don't check against the char c which may mask the -1 return value. Instead, store the return value in an int, and then later cast it to a char when it's actually consumed. Change-Id: Ibec51927d0cdbd98db8e53081be2c4931e20333c Reviewed-on: https://gem5-review.googlesource.com/3600 Reviewed-by: Nathan Binkert <nate@binkert.org> Maintainer: Nathan Binkert <nate@binkert.org>
536 lines
13 KiB
C++
536 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2003-2005 The Regents of The University of Michigan
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Authors: Nathan Binkert
|
|
*/
|
|
|
|
extern "C" {
|
|
#include <pcap.h>
|
|
|
|
}
|
|
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <libgen.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <poll.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cerrno>
|
|
#include <csignal>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <list>
|
|
#include <string>
|
|
|
|
#define panic(arg...) \
|
|
do { printf("Panic: " arg); exit(1); } while (0)
|
|
|
|
const char *program = "ethertap";
|
|
void
|
|
usage()
|
|
{
|
|
printf(
|
|
"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) \
|
|
printf(args); \
|
|
} while (0)
|
|
|
|
#define DDUMP(args...) do { \
|
|
if (verbose >= 2) \
|
|
dump((const u_char *)args); \
|
|
} while (0)
|
|
|
|
void
|
|
dump(const u_char *data, int len)
|
|
{
|
|
int c, i, j;
|
|
|
|
for (i = 0; i < len; i += 16) {
|
|
printf("%08x ", i);
|
|
c = len - i;
|
|
if (c > 16) c = 16;
|
|
|
|
for (j = 0; j < c; j++) {
|
|
printf("%02x ", data[i + j] & 0xff);
|
|
if ((j & 0xf) == 7 && j > 0)
|
|
printf(" ");
|
|
}
|
|
|
|
for (; j < 16; j++)
|
|
printf(" ");
|
|
printf(" ");
|
|
|
|
for (j = 0; j < c; j++) {
|
|
int ch = data[i + j] & 0x7f;
|
|
printf("%c", (char)(isprint(ch) ? ch : ' '));
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
if (c < 16)
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool quit = false;
|
|
void
|
|
quit_now(int sigtype)
|
|
{
|
|
DPRINTF("User requested exit\n");
|
|
quit = true;
|
|
}
|
|
|
|
|
|
int
|
|
Socket(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;
|
|
}
|
|
|
|
void
|
|
Listen(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.
|
|
int
|
|
Accept(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;
|
|
}
|
|
|
|
void
|
|
Connect(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.c_str());
|
|
|
|
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.c_str(), port);
|
|
|
|
DPRINTF("connected to %s on port %d\n", host.c_str(), 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;
|
|
|
|
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");
|
|
}
|
|
|
|
fd = pcap_fileno(pcap);
|
|
}
|
|
|
|
PCap::~PCap()
|
|
{
|
|
pcap_close(pcap);
|
|
}
|
|
|
|
bool
|
|
PCap::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;
|
|
}
|
|
|
|
bool
|
|
PCap::write(const char *data, int len)
|
|
{
|
|
return pcap_inject(pcap, data, len) == 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);
|
|
}
|
|
|
|
bool
|
|
Tap::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;
|
|
}
|
|
|
|
bool
|
|
Tap::write(const char *data, int len)
|
|
{
|
|
int result = ::write(fd, data, len);
|
|
if (result < 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int
|
|
main(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]);
|
|
|
|
int ret;
|
|
while ((ret = getopt(argc, argv, "b:df:lp:tv")) != -1) {
|
|
char c = ret;
|
|
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, -1);
|
|
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;
|
|
}
|