// -*- c-basic-offset: 4 -*-
/*
* fromhost.{cc,hh} -- receives packets to Linux through the
* TUN Universal TUN/TAP module
* John Bicket
*
* Copyright (c) 2004 Massachusetts Institute of Technology
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include "fromhost.hh"
#include <click/error.hh>
#include <click/bitvector.hh>
#include <click/confparse.hh>
#include <click/straccum.hh>
#include <click/glue.hh>
#include <clicknet/ether.h>
#include <click/standard/scheduleinfo.hh>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/if_tun.h>
CLICK_DECLS
FromHost::FromHost()
: _fd(-1),
_macaddr((const unsigned char *)"\000\001\002\003\004\005"),
_task(this)
{
}
FromHost::~FromHost()
{
}
int
FromHost::configure(Vector<String> &conf, ErrorHandler *errh)
{
_headroom = Packet::DEFAULT_HEADROOM;
_mtu_out = DEFAULT_MTU;
if (cp_va_parse(conf, this, errh,
cpString, "device name", &_dev_name,
cpIPPrefix, "destination IP address", &_near, &_mask,
cpOptional,
cpKeywords,
"ETHER", cpEthernetAddress, "fake device Ethernet address", &_macaddr,
"HEADROOM", cpUnsigned, "default headroom for generated packets", &_headroom,
"MTU", cpInteger, "MTU", &_mtu_out,
cpEnd) < 0)
return -1;
if (!_dev_name) {
return errh->error("must specify device name");
}
return 0;
}
int
FromHost::try_linux_universal(ErrorHandler *errh)
{
int fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
if (fd < 0) {
int e = errno;
errh->error("open /dev/net/tun: %s", strerror(e));
return -e;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
/* we want an ethertap-like interface */
ifr.ifr_flags = IFF_TAP;
/*
* setting ifr_name this allows us to select an aribitrary
* interface name.
*/
strcpy(ifr.ifr_name, _dev_name.c_str());
int err = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (err < 0) {
errh->warning("Linux universal tun failed for %s: %s",
_dev_name.c_str(),
strerror(errno));
close(fd);
return -errno;
}
_dev_name = ifr.ifr_name;
_fd = fd;
return 0;
}
int
FromHost::setup_tun(struct in_addr near, struct in_addr mask, ErrorHandler *errh)
{
char tmp[512], tmp0[64], tmp1[64];
if (_macaddr) {
sprintf(tmp, "/sbin/ifconfig %s hw ether %s", _dev_name.c_str(),
_macaddr.s().c_str());
if (system(tmp) != 0) {
errh->error("%s: %s", tmp, strerror(errno));
}
sprintf(tmp, "/sbin/ifconfig %s arp", _dev_name.c_str());
if (system(tmp) != 0)
return errh->error("%s: couldn't set arp flags: %s", tmp, strerror(errno));
}
strcpy(tmp0, inet_ntoa(near));
strcpy(tmp1, inet_ntoa(mask));
sprintf(tmp, "/sbin/ifconfig %s %s netmask %s up 2>/dev/null", _dev_name.c_str(), tmp0, tmp1);
if (system(tmp) != 0) {
return errh->error("%s: `%s' failed\n(Perhaps Ethertap is in a kernel module that you haven't loaded yet?)", _dev_name.c_str(), tmp);
}
// calculate maximum packet size needed to receive data from
// tun/tap.
_mtu_in = _mtu_out + 4;
return 0;
}
void
FromHost::dealloc_tun()
{
String cmd = "/sbin/ifconfig " + _dev_name + " down";
if (system(cmd.c_str()) != 0)
click_chatter("%s: failed: %s", name().c_str(), cmd.c_str());
}
int
FromHost::initialize(ErrorHandler *errh)
{
if (try_linux_universal(errh) < 0)
return -1;
if (setup_tun(_near, _mask, errh) < 0)
return -1;
ScheduleInfo::join_scheduler(this, &_task, errh);
_nonfull_signal = Notifier::downstream_full_signal(this, 0, &_task);
add_select(_fd, SELECT_READ);
return 0;
}
void
FromHost::cleanup(CleanupStage)
{
if (_fd >= 0) {
close(_fd);
remove_select(_fd, SELECT_READ);
}
}
void
FromHost::selected(int fd)
{
if (fd != _fd)
return;
WritablePacket *p = Packet::make(_headroom, 0, _mtu_in, 0);
if (!p) {
click_chatter("out of memory!");
return;
}
int cc = read(_fd, p->data(), _mtu_in);
if (cc > 0) {
p->take(_mtu_in - cc);
// 2-byte padding followed by an Ethernet type
p->pull(4);
p->set_mac_header(p->data());
const click_ip *ip = reinterpret_cast<const click_ip *>(p->data() + sizeof(click_ether));
p->set_dst_ip_anno(IPAddress(ip->ip_dst));
p->set_ip_header(ip, ip->ip_hl << 2);
p->timestamp_anno().set_now();
output(0).push(p);
} else {
p->kill();
perror("FromHost read");
}
if (!_nonfull_signal) {
remove_select(_fd, SELECT_READ);
return;
}
}
bool
FromHost::run_task()
{
if (!_nonfull_signal)
return false;
add_select(_fd, SELECT_READ);
return true;
}
enum {H_DEV_NAME, H_SIGNAL};
String
FromHost::read_param(Element *e, void *thunk)
{
FromHost *td = (FromHost *)e;
switch ((uintptr_t) thunk) {
case H_DEV_NAME: return td->dev_name();
case H_SIGNAL: return String(td->_nonfull_signal.active());
default:
return "";
}
}
void
FromHost::add_handlers()
{
add_read_handler("dev_name", read_param, (void *) H_DEV_NAME);
add_read_handler("signal", read_param, (void *) H_SIGNAL);
}
CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel linux)
EXPORT_ELEMENT(FromHost)
syntax highlighted by Code2HTML, v. 0.9.1