📄 kerneltap.cc
字号:
// -*- c-basic-offset: 4 -*-/* * kerneltap.{cc,hh} -- element accesses network via /dev/tap device * Robert Morris, Douglas S. J. De Couto, Eddie Kohler * * Copyright (c) 1999-2000 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 "kerneltap.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>#if defined(__linux__) && defined(HAVE_LINUX_IF_TUN_H)# define KERNELTUN_LINUX 1#elif defined(HAVE_NET_IF_TUN_H)# define KERNELTUN_NET 1#elif defined(__APPLE__)# define KERNELTUN_OSX 1 // assume tun driver installed from http://chrisp.de/en/projects/tunnel.html// this driver doesn't produce or expect packets with an address family prepended#endif#include <net/if.h>#if HAVE_NET_IF_TUN_H# include <net/if_tun.h>#elif HAVE_LINUX_IF_TUN_H# include <linux/if_tun.h>#endifCLICK_DECLSKernelTap::KernelTap() : Element(1, 1), _fd(-1), _task(this), _macaddr((const unsigned char *)"\000\001\002\003\004\005"), _ignore_q_errs(false), _printed_write_err(false), _printed_read_err(false){}KernelTap::~KernelTap(){}intKernelTap::configure(Vector<String> &conf, ErrorHandler *errh){ _gw = IPAddress(); _headroom = Packet::DEFAULT_HEADROOM; _mtu_out = DEFAULT_MTU; if (cp_va_parse(conf, this, errh, cpIPPrefix, "network address", &_near, &_mask, cpOptional, cpKeywords, "HEADROOM", cpUnsigned, "default headroom for generated packets", &_headroom, "ETHER", cpEthernetAddress, "fake device Ethernet address", &_macaddr, "IGNORE_QUEUE_OVERFLOWS", cpBool, "ignore queue overflow errors?", &_ignore_q_errs, "MTU", cpInteger, "MTU", &_mtu_out, cpEnd) < 0) return -1; if (_gw) { // then it was set to non-zero by arg // check net part matches unsigned int g = _gw.in_addr().s_addr; unsigned int m = _mask.in_addr().s_addr; unsigned int n = _near.in_addr().s_addr; if ((g & m) != (n & m)) { _gw = 0; errh->warning("not setting up default route\n(network address and gateway are on different networks)"); } } return 0;}#if KERNELTUN_LINUXintKernelTap::try_linux_universal(ErrorHandler *errh){ int fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK); if (fd < 0) return -errno; struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP; int err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { errh->warning("Linux universal tun failed: %s", strerror(errno)); close(fd); return -errno; } _dev_name = ifr.ifr_name; _type = LINUX_UNIVERSAL; _fd = fd; return 0;}#endifintKernelTap::try_tun(const String &dev_name, ErrorHandler *){ String filename = "/dev/" + dev_name; int fd = open(filename.cc(), O_RDWR | O_NONBLOCK); if (fd < 0) return -errno; _dev_name = dev_name; _fd = fd; return 0;}/* * Find an available kernel tap, or report error if none are available. * Does not set up the tap. * On success, _dev_name, _type, and _fd are valid. */intKernelTap::alloc_tun(ErrorHandler *errh){#if !KERNELTUN_LINUX && !KERNELTUN_NET && !KERNELTUN_OSX return errh->error("KernelTap is not yet supported on this system.\n(Please report this message to click@pdos.lcs.mit.edu.)");#endif int error, saved_error = 0; String saved_device, saved_message; StringAccum tried; #if KERNELTUN_LINUX _type = LINUX_UNIVERSAL; if ((error = try_linux_universal(errh)) >= 0) return error; else if (!saved_error || error != -ENOENT) { saved_error = error, saved_device = "net/tun"; if (error == -ENODEV) saved_message = "\n(Perhaps you need to enable tun in your kernel or load the `tun' module.)"; } tried << "/dev/net/tun, ";#endif#ifdef __linux__ _type = LINUX_ETHERTAP; String dev_prefix = "tap";#elif defined(KERNELTUN_OSX) _type = OSX_TUN; String dev_prefix = "tun";#else _type = BSD_TUN; String dev_prefix = "tun";#endif for (int i = 0; i < 6; i++) { if ((error = try_tun(dev_prefix + String(i), errh)) >= 0) return error; else if (!saved_error || error != -ENOENT) saved_error = error, saved_device = dev_prefix + String(i), saved_message = String(); tried << "/dev/" << dev_prefix << i << ", "; } if (saved_error == -ENOENT) { tried.pop_back(2); return errh->error("could not find a tap device\n(checked %s)\nYou may need to enable tap support in your kernel.", tried.cc()); } else return errh->error("could not allocate device /dev/%s: %s%s", saved_device.cc(), strerror(-saved_error), saved_message.cc());}intKernelTap::setup_tun(struct in_addr near, struct in_addr mask, ErrorHandler *errh){ char tmp[512], tmp0[64], tmp1[64];// #if defined(__OpenBSD__) && !defined(TUNSIFMODE)// /* see OpenBSD bug: http://cvs.openbsd.org/cgi-bin/wwwgnats.pl/full/782 */// #define TUNSIFMODE _IOW('t', 88, int)// #endif#if defined(TUNSIFMODE) || defined(__FreeBSD__) { int mode = IFF_BROADCAST; if (ioctl(_fd, TUNSIFMODE, &mode) != 0) return errh->error("TUNSIFMODE failed: %s", strerror(errno)); }#endif#if defined(__OpenBSD__) { struct tuninfo ti; memset(&ti, 0, sizeof(struct tuninfo)); if (ioctl(_fd, TUNGIFINFO, &ti) != 0) return errh->error("TUNGIFINFO failed: %s", strerror(errno)); ti.flags &= IFF_BROADCAST; if (ioctl(_fd, TUNSIFINFO, &ti) != 0) return errh->error("TUNSIFINFO failed: %s", strerror(errno)); }#endif if (_macaddr) { sprintf(tmp, "/sbin/ifconfig %s hw ether %s", _dev_name.cc(), _macaddr.s().cc()); if (system(tmp) != 0) { errh->error("%s: %s", tmp, strerror(errno)); } sprintf(tmp, "/sbin/ifconfig %s arp", _dev_name.cc()); if (system(tmp) != 0) return errh->error("%s: %s", tmp, strerror(errno)); } #if defined(TUNSIFHEAD) || defined(__FreeBSD__) // Each read/write prefixed with a 32-bit address family, // just as in OpenBSD. int yes = 1; if (ioctl(_fd, TUNSIFHEAD, &yes) != 0) return errh->error("TUNSIFHEAD failed: %s", strerror(errno));#endif strcpy(tmp0, inet_ntoa(near)); strcpy(tmp1, inet_ntoa(mask)); sprintf(tmp, "/sbin/ifconfig %s %s netmask %s up 2>/dev/null", _dev_name.cc(), tmp0, tmp1); if (system(tmp) != 0) {# if defined(__linux__) // Is Ethertap available? If it is moduleified, then it might not be. // beside the ethertap module, you may also need the netlink_dev // module to be loaded. return errh->error("%s: `%s' failed\n(Perhaps Ethertap is in a kernel module that you haven't loaded yet?)", _dev_name.cc(), tmp);# else return errh->error("%s: `%s' failed", _dev_name.cc(), tmp);# endif } if (_gw) {#if defined(__linux__) sprintf(tmp, "/sbin/route -n add default gw %s", _gw.s().cc());#elif defined(__FreeBSD__) || defined(__OpenBSD__) sprintf(tmp, "/sbin/route -n add default %s", _gw.s().cc());#endif if (system(tmp) != 0) return errh->error("%s: %s", tmp, strerror(errno)); } // calculate maximum packet size needed to receive data from // tun/tap. if (_type == LINUX_UNIVERSAL) _mtu_in = _mtu_out + 4; else if (_type == BSD_TUN) _mtu_in = _mtu_out + 4; else if (_type == OSX_TUN) _mtu_in = _mtu_out + 4; // + 0? else /* _type == LINUX_ETHERTAP */ _mtu_in = _mtu_out + 16; return 0;}voidKernelTap::dealloc_tun(){ String cmd = "/sbin/ifconfig " + _dev_name + " down"; if (system(cmd.cc()) != 0) click_chatter("%s: failed: %s", id().cc(), cmd.cc());}intKernelTap::initialize(ErrorHandler *errh){ if (alloc_tun(errh) < 0) return -1; if (setup_tun(_near, _mask, errh) < 0) return -1; if (input_is_pull(0)) ScheduleInfo::join_scheduler(this, &_task, errh); add_select(_fd, SELECT_READ); return 0;}voidKernelTap::cleanup(CleanupStage){ if (_fd >= 0) { close(_fd); remove_select(_fd, SELECT_READ); if (_type != LINUX_UNIVERSAL) dealloc_tun(); }}voidKernelTap::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); if (_type == LINUX_UNIVERSAL) { // 2-byte padding followed by an Ethernet type p->pull(4); } else if (_type == BSD_TUN) { // 4-byte address family followed by IP header p->pull(4); } else if (_type == OSX_TUN) { } else { /* _type == LINUX_ETHERTAP */ p->pull(2); } p->timestamp_anno().set_now(); output(0).push(p); } else { if (!_ignore_q_errs || !_printed_read_err || (errno != ENOBUFS)) { _printed_read_err = true; perror("KernelTap read"); } }}boolKernelTap::run_task(){ Packet *p = input(0).pull(); if (p) push(0, p); _task.fast_reschedule(); return p != 0;}voidKernelTap::push(int, Packet *p){ // Every packet has a 14-byte Ethernet header. // Extract the packet type, then ignore the Ether header. const click_ip *iph = p->ip_header(); click_ether *e = (click_ether *) p->data(); if (!iph) { click_chatter("KernelTap(%s): no network header", _dev_name.cc()); p->kill(); } if (p->length() < sizeof(*e)){ click_chatter("KernelTap(%s): packet too small", _dev_name.c_str()); p->kill(); return; } WritablePacket *q; if (_type == LINUX_UNIVERSAL) { // 2-byte padding followed by an Ethernet type uint32_t ethertype = (iph->ip_v == 4 ? htonl(ETHERTYPE_IP) : htonl(ETHERTYPE_IP6)); if ((q = p->push(4))) *(uint32_t *)(q->data()) = ethertype; } else if (_type == BSD_TUN) { uint32_t af = (iph->ip_v == 4 ? htonl(AF_INET) : htonl(AF_INET6)); if ((q = p->push(4))) *(uint32_t *)(q->data()) = af; } else if (_type == OSX_TUN) { // send raw IP q = p->uniqueify(); } else { /* _type == LINUX_ETHERTAP */ q = p->push(2); } if (q) { int w = write(_fd, q->data(), q->length()); if (w != (int) q->length() && (errno != ENOBUFS || !_ignore_q_errs || !_printed_write_err)) { _printed_write_err = true; click_chatter("KernelTap(%s): write failed: %s", _dev_name.cc(), strerror(errno)); } q->kill(); } else click_chatter("%{element}: out of memory", this);}StringKernelTap::print_dev_name(Element *e, void *) { KernelTap *kt = (KernelTap *) e; return kt->_dev_name;}voidKernelTap::add_handlers(){ if (input_is_pull(0)) add_task_handlers(&_task); add_read_handler("dev_name", print_dev_name, 0);}CLICK_ENDDECLSELEMENT_REQUIRES(userlevel)EXPORT_ELEMENT(KernelTap)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -