📄 kerneltun.cc
字号:
// -*- c-basic-offset: 4 -*-/* * kerneltun.{cc,hh} -- element accesses network via /dev/tun device * Robert Morris, Douglas S. J. De Couto, Eddie Kohler * * Copyright (c) 1999-2000 Massachusetts Institute of Technology * Copyright (c) 2006 Regents of the University of California * * 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 "kerneltun.hh"#include "fakepcap.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#if defined(HAVE_NET_IF_TAP_H)# define KERNELTAP_NET 1#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>#endif#if HAVE_NET_IF_TAP_H# include <net/if_tap.h>#endif#if defined(__NetBSD__)# include <sys/param.h># include <sys/sysctl.h>#endif#if defined(__FreeBSD__)# include <net/ethernet.h>#endifCLICK_DECLSKernelTun::KernelTun() : _fd(-1), _tap(false), _task(this), _ignore_q_errs(false), _printed_write_err(false), _printed_read_err(false){}KernelTun::~KernelTun(){}void *KernelTun::cast(const char *n){ if (strcmp(n, "KernelTun") == 0) return this; else return Element::cast(n);}intKernelTun::configure(Vector<String> &conf, ErrorHandler *errh){ _gw = IPAddress(); _headroom = Packet::default_headroom; _adjust_headroom = false; _headroom += (4 - _headroom % 4) % 4; // default 4/0 alignment _mtu_out = DEFAULT_MTU; if (cp_va_kparse(conf, this, errh, "ADDR", cpkP+cpkM, cpIPPrefix, &_near, &_mask, "GATEWAY", cpkP, cpIPAddress, &_gw, "TAP", 0, cpBool, &_tap, "HEADROOM", cpkC, &_adjust_headroom, cpUnsigned, &_headroom, "ETHER", 0, cpEthernetAddress, &_macaddr, "IGNORE_QUEUE_OVERFLOWS", 0, cpBool, &_ignore_q_errs, "MTU", 0, cpInteger, &_mtu_out,#if KERNELTUN_LINUX "DEV_NAME", cpkD, cpString, &_dev_name, // deprecated "DEVNAME", 0, cpString, &_dev_name,#endif 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)"); } } if (_mtu_out < (int) sizeof(click_ip)) return errh->error("MTU must be greater than %d", sizeof(click_ip)); if (_headroom > 8192) return errh->error("HEADROOM too big"); else _adjust_headroom = !_adjust_headroom; return 0;}#if KERNELTUN_LINUXintKernelTun::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 = (_tap ? IFF_TAP : IFF_TUN); if (_dev_name) // Setting ifr_name allows us to select an arbitrary interface name. strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name)); 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; _fd = fd; _type = LINUX_UNIVERSAL; return 0;}#endifintKernelTun::try_tun(const String &dev_name, ErrorHandler *){ String filename = "/dev/" + dev_name; int fd = open(filename.c_str(), 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. */intKernelTun::alloc_tun(ErrorHandler *errh){#if !KERNELTUN_LINUX && !KERNELTUN_NET && !KERNELTUN_OSX && !KERNELTAP_NET return errh->error("%s is not yet supported on this system.\n(Please report this message to click@pdos.lcs.mit.edu.)", class_name());#endif int error, saved_error = 0; String saved_device, saved_message; StringAccum tried;#if KERNELTUN_LINUX 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 String dev_prefix;#ifdef __linux__ _type = LINUX_ETHERTAP; dev_prefix = "tap";#elif defined(KERNELTUN_OSX) _type = OSX_TUN; dev_prefix = "tun";#elif defined(__NetBSD__) && !defined(TUNSIFHEAD) _type = (_tap ? NETBSD_TAP : NETBSD_TUN); dev_prefix = (_tap ? "tap" : "tun");#else _type = (_tap ? BSD_TAP : BSD_TUN); dev_prefix = (_tap ? "tap" : "tun");#endif#if defined(__NetBSD__) && !defined(TUNSIFHEAD) if (_type == NETBSD_TAP) { // In NetBSD, two ways to create a tap: // 1. open /dev/tap cloning interface. // 2. do ifconfig tapN create (SIOCIFCREATE), and then open(/dev/tapN). // We use the cloning interface. if ((error = try_tun(dev_prefix, errh)) >= 0) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (ioctl(_fd, TAPGIFNAME, &ifr) != 0) return errh->error("TAPGIFNAME failed: %s", strerror(errno)); _dev_name = ifr.ifr_name; return error; } else if (!saved_error || error != -ENOENT) saved_error = error, saved_device = dev_prefix, saved_message = String(); tried << "/dev/" << dev_prefix; goto error_out; }#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 defined(__NetBSD__) && !defined(TUNSIFHEAD) error_out:#endif if (saved_error == -ENOENT) { tried.pop_back(2); return errh->error("could not find a tap device\n(checked %s)\nYou may need to load a kernel module to support tap.", tried.c_str()); } else return errh->error("could not allocate device /dev/%s: %s%s", saved_device.c_str(), strerror(-saved_error), saved_message.c_str());}intKernelTun::updown(IPAddress addr, IPAddress mask, ErrorHandler *errh){ int before = errh->nerrors(); int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (s < 0) return errh->error("socket() failed: %s", strerror(errno)); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, _dev_name.c_str(), sizeof(ifr.ifr_name));#if defined(SIOCSIFADDR) && defined(SIOCSIFNETMASK) for (int trynum = 0; trynum < 2; trynum++) { struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; sin->sin_family = AF_INET;# if HAVE_SOCKADDR_IN_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in);# endif sin->sin_port = 0; // Try setting the netmask twice. On FreeBSD, we need to set the mask // *before* we set the address, or there's nasty behavior where the // tunnel cannot be assigned a different address. (Or something like // that, I forget now.) But on Linux, you must set the mask *after* // the address. sin->sin_addr = mask; if (ioctl(s, SIOCSIFNETMASK, &ifr) == 0) trynum++; else if (trynum == 1) { errh->error("SIOCSIFNETMASK failed: %s", strerror(errno)); goto out; } sin->sin_addr = addr; if (trynum < 2 && ioctl(s, SIOCSIFADDR, &ifr) != 0) { errh->error("SIOCSIFADDR failed: %s", strerror(errno)); goto out; } }#else# error "Lacking SIOCSIFADDR and/or SIOCSIFNETMASK"#endif#if defined(SIOCSIFHWADDR) if (_macaddr) { ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, _macaddr.data(), sizeof(_macaddr)); if (ioctl(s, SIOCSIFHWADDR, &ifr) != 0) errh->warning("could not set interface Ethernet address: %s", strerror(errno)); }#elif defined(__NetBSD__) if (_macaddr && _tap) { String tap = "net.link.tap." + _dev_name, mac = _macaddr.unparse_colon(); int r = sysctlbyname(tap.c_str(), (void *) 0, (size_t *) 0, (void *) mac.c_str(), mac.length()); if (r < 0) errh->warning("could not set interface Ethernet address: %s", strerror(errno)); } else if (_macaddr) errh->warning("could not set interface Ethernet address: no support for /dev/tun");#elif defined(__FreeBSD__) if (_macaddr && _tap) { ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; ifr.ifr_addr.sa_family = AF_LINK; memcpy(ifr.ifr_addr.sa_data, _macaddr.data(), ETHER_ADDR_LEN); if (ioctl(s, SIOCSIFLLADDR, &ifr) != 0) errh->warning("could not set interface Ethernet address: %s", strerror(errno)); } else if (_macaddr) errh->warning("could not set interface Ethernet address: no support for /dev/tun");#else if (_macaddr) errh->warning("could not set interface Ethernet address: no support");#endif#if defined(SIOCSIFMTU) if (_mtu_out != DEFAULT_MTU) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -