📄 kerneltun.cc
字号:
ifr.ifr_mtu = _mtu_out; if (ioctl(s, SIOCSIFMTU, &ifr) != 0) errh->warning("could not set interface MTU: %s", strerror(errno)); }#endif#if defined(SIOCGIFFLAGS) && defined(SIOCSIFFLAGS) if (ioctl(s, SIOCGIFFLAGS, &ifr) != 0) { errh->error("SIOCGIFFLAGS failed: %s", strerror(errno)); goto out; } if (_tap) ifr.ifr_flags = (addr ? ifr.ifr_flags & ~IFF_NOARP : ifr.ifr_flags | IFF_NOARP); ifr.ifr_flags = (addr ? ifr.ifr_flags | IFF_UP | IFF_PROMISC : ifr.ifr_flags & ~IFF_UP & ~IFF_PROMISC); if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0) { errh->error("SIOCSIFFLAGS failed: %s", strerror(errno)); goto out; }#else# error "Lacking SIOCGIFFLAGS and/or SIOCSIFFLAGS"#endif out: close(s); return (errh->nerrors() == before ? 0 : -1);}intKernelTun::setup_tun(ErrorHandler *errh){// #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__) if (!_tap) { int mode = IFF_BROADCAST; if (ioctl(_fd, TUNSIFMODE, &mode) != 0) return errh->error("TUNSIFMODE failed: %s", strerror(errno)); }#endif#if defined(__OpenBSD__) if (!_tap) { 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 defined(TUNSIFHEAD) || defined(__FreeBSD__) // Each read/write prefixed with a 32-bit address family, // just as in OpenBSD. if (!_tap && _type == BSD_TUN) { int yes = 1; if (ioctl(_fd, TUNSIFHEAD, &yes) != 0) return errh->error("TUNSIFHEAD failed: %s", strerror(errno)); }#endif // set addresses and MTU if (updown(_near, _mask, errh) < 0) return -1; if (_gw) { String cmd = "/sbin/route -n add default ";#if defined(__linux__) cmd += "gw " + _gw.unparse();#else cmd += _gw.unparse();#endif if (system(cmd.c_str()) != 0) return errh->error("%s: %s", cmd.c_str(), strerror(errno)); } // calculate maximum packet size needed to receive data from // tun/tap. if (_tap) { if (_type == LINUX_UNIVERSAL) _mtu_in = _mtu_out + 18; else if (_type == LINUX_ETHERTAP) _mtu_in = _mtu_out + 16; else _mtu_in = _mtu_out + 14; } else if (_type == LINUX_UNIVERSAL) _mtu_in = _mtu_out + 4; else if (_type == BSD_TUN) _mtu_in = _mtu_out + 4; else if (_type == BSD_TAP || _type == NETBSD_TAP || _type == NETBSD_TUN) _mtu_in = _mtu_out; else if (_type == OSX_TUN) _mtu_in = _mtu_out + 4; // + 0? else /* _type == LINUX_ETHERTAP */ _mtu_in = _mtu_out + 16; return 0;}intKernelTun::initialize(ErrorHandler *errh){ if (alloc_tun(errh) < 0) return -1; if (setup_tun(errh) < 0) return -1; if (input_is_pull(0)) { ScheduleInfo::join_scheduler(this, &_task, errh); _signal = Notifier::upstream_empty_signal(this, 0, &_task); } if (_adjust_headroom) { if (_tap && _type == LINUX_UNIVERSAL) _headroom += (4 - (_headroom + 2) % 4) % 4; // default 4/2 alignment else _headroom += (4 - _headroom % 4) % 4; // default 4/0 alignment } add_select(_fd, SELECT_READ); return 0;}voidKernelTun::cleanup(CleanupStage){ if (_fd >= 0) { if (_type != LINUX_UNIVERSAL && _type != NETBSD_TAP) updown(0, ~0, ErrorHandler::default_handler()); close(_fd); remove_select(_fd, SELECT_READ); }}voidKernelTun::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); bool ok = false; if (_tap) { if (_type == LINUX_UNIVERSAL) // 2-byte padding, 2-byte Ethernet type, then Ethernet header p->pull(4); else if (_type == LINUX_ETHERTAP) // 2-byte padding, then Ethernet header p->pull(2); ok = true; } else if (_type == LINUX_UNIVERSAL) { // 2-byte padding followed by an Ethernet type uint16_t etype = *(uint16_t *)(p->data() + 2); p->pull(4); if (etype != htons(ETHERTYPE_IP) && etype != htons(ETHERTYPE_IP6)) checked_output_push(1, p->clone()); else ok = fake_pcap_force_ip(p, FAKE_DLT_RAW); } else if (_type == BSD_TUN) { // 4-byte address family followed by IP header int af = ntohl(*(unsigned *)p->data()); p->pull(4); if (af != AF_INET && af != AF_INET6) { click_chatter("KernelTun(%s): don't know AF %d", _dev_name.c_str(), af); checked_output_push(1, p->clone()); } else ok = fake_pcap_force_ip(p, FAKE_DLT_RAW); } else if (_type == OSX_TUN || _type == NETBSD_TUN) { ok = fake_pcap_force_ip(p, FAKE_DLT_RAW); } else { /* _type == LINUX_ETHERTAP */ // 2-byte padding followed by a mostly-useless Ethernet header uint16_t etype = *(uint16_t *)(p->data() + 14); p->pull(16); if (etype != htons(ETHERTYPE_IP) && etype != htons(ETHERTYPE_IP6)) checked_output_push(1, p->clone()); else ok = fake_pcap_force_ip(p, FAKE_DLT_RAW); } if (ok) { p->timestamp_anno().set_now(); output(0).push(p); } else checked_output_push(1, p); } else { if (!_ignore_q_errs || !_printed_read_err || (errno != ENOBUFS)) { _printed_read_err = true; perror("KernelTun read"); } }}boolKernelTun::run_task(Task *){ Packet *p = input(0).pull(); if (p) push(0, p); else if (!_signal) return false; _task.fast_reschedule(); return p != 0;}voidKernelTun::push(int, Packet *p){ const click_ip *iph = 0; int check_length; // sanity checks if (_tap) { if (p->length() < sizeof(click_ether)) { click_chatter("%s(%s): packet too small", class_name(), _dev_name.c_str()); goto kill; } // use network length for MTU check_length = p->length() - sizeof(click_ether); } else { iph = p->ip_header(); // check IP header if (!p->has_network_header() || p->network_length() < (int) sizeof(click_ip)) { click_chatter("%s(%s): no network header", class_name(), _dev_name.c_str()); kill: p->kill(); return; } else if (iph->ip_v != 4 && iph->ip_v != 6) { click_chatter("%s(%s): unknown IP version %d", class_name(), _dev_name.c_str(), iph->ip_v); goto kill; } // strip link headers p->change_headroom_and_length(p->headroom() + p->network_header_offset(), p->network_length()); check_length = p->length(); } // check MTU if (check_length > _mtu_out) { click_chatter("%s(%s): packet larger than MTU (%d)", class_name(), _dev_name.c_str(), _mtu_out); goto kill; } WritablePacket *q; if (_tap) { if (_type == LINUX_UNIVERSAL) { // 2-byte padding, 2-byte Ethernet type, then Ethernet header uint16_t ethertype = ((const click_ether *) p->data())->ether_type; if ((q = p->push(4))) ((uint16_t *) q->data())[1] = ethertype; p = q; } else if (_type == LINUX_ETHERTAP) { // 2-byte padding, then Ethernet header p = p->push(2); } else { /* existing packet is OK */; } } else 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; p = q; } 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; p = q; } else if (_type == LINUX_ETHERTAP) { uint16_t ethertype = (iph->ip_v == 4 ? htons(ETHERTYPE_IP) : htons(ETHERTYPE_IP6)); if ((q = p->push(16))) { /* ethertap driver is very picky about what address we use * here. e.g. if we have the wrong address, linux might ignore * all the packets, or accept udp or icmp, but ignore tcp. * aaarrrgh, well this works. -ddc */ memcpy(q->data(), "\x00\x00\xFE\xFD\x00\x00\x00\x00\xFE\xFD\x00\x00\x00\x00", 14); *(uint16_t *)(q->data() + 14) = ethertype; } p = q; } else { /* existing packet is OK */; } if (p) { int w = write(_fd, p->data(), p->length()); if (w != (int) p->length() && (errno != ENOBUFS || !_ignore_q_errs || !_printed_write_err)) { _printed_write_err = true; click_chatter("%s(%s): write failed: %s", class_name(), _dev_name.c_str(), strerror(errno)); } p->kill(); } else click_chatter("%s(%s): out of memory", class_name(), _dev_name.c_str());}StringKernelTun::print_dev_name(Element *e, void *){ KernelTun *kt = (KernelTun *) e; return kt->_dev_name;}voidKernelTun::add_handlers(){ if (input_is_pull(0)) add_task_handlers(&_task); add_read_handler("dev_name", print_dev_name, 0);}CLICK_ENDDECLSELEMENT_REQUIRES(userlevel FakePcap)EXPORT_ELEMENT(KernelTun)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -