📄 tun.c
字号:
/* * OpenVPN -- An application to securely tunnel IP networks * over a single TCP/UDP port, with support for SSL/TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * Support routines for configuring and accessing TUN/TAP * virtual network adapters. * * This file is based on the TUN/TAP driver interface routines * from VTun by Maxim Krasnyansky <max_mk@yahoo.com>. */#ifdef WIN32#include "config-win32.h"#else#include "config.h"#endif#include "syshead.h"#include "tun.h"#include "fdmisc.h"#include "common.h"#include "misc.h"#include "socket.h"#include "manage.h"#include "memdbg.h"#ifdef TARGET_SOLARISstatic void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);#endifboolis_dev_type (const char *dev, const char *dev_type, const char *match_type){ ASSERT (match_type); if (!dev) return false; if (dev_type) return !strcmp (dev_type, match_type); else return !strncmp (dev, match_type, strlen (match_type));}intdev_type_enum (const char *dev, const char *dev_type){ if (is_dev_type (dev, dev_type, "tun")) return DEV_TYPE_TUN; else if (is_dev_type (dev, dev_type, "tap")) return DEV_TYPE_TAP; else if (is_dev_type (dev, dev_type, "null")) return DEV_TYPE_NULL; else return DEV_TYPE_UNDEF;}const char *dev_type_string (const char *dev, const char *dev_type){ switch (dev_type_enum (dev, dev_type)) { case DEV_TYPE_TUN: return "tun"; case DEV_TYPE_TAP: return "tap"; case DEV_TYPE_NULL: return "null"; default: return "[unknown-dev-type]"; }}const char *dev_component_in_dev_node (const char *dev_node){ const char *ret; const int dirsep = OS_SPECIFIC_DIRSEP; if (dev_node) { ret = strrchr (dev_node, dirsep); if (ret && *ret) ++ret; else ret = dev_node; if (*ret) return ret; } return NULL;}/* * Try to predict the actual TUN/TAP device instance name, * before the device is actually opened. */const char *guess_tuntap_dev (const char *dev, const char *dev_type, const char *dev_node, struct gc_arena *gc){#ifdef WIN32 const int dt = dev_type_enum (dev, dev_type); if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP) { return get_netsh_id (dev_node, gc); }#endif /* default case */ return dev;}/* * Called by the open_tun function of OSes to check if we * explicitly support IPv6. * * In this context, explicit means that the OS expects us to * do something special to the tun socket in order to support * IPv6, i.e. it is not transparent. * * ipv6_explicitly_supported should be set to false if we don't * have any explicit IPv6 code in the tun device handler. * * If ipv6_explicitly_supported is true, then we have explicit * OS-specific tun dev code for handling IPv6. If so, tt->ipv6 * is set according to the --tun-ipv6 command line option. */static voidipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt){ tt->ipv6 = false; if (ipv6_explicitly_supported) tt->ipv6 = ipv6; else if (ipv6) msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");}/* --ifconfig-nowarn disables some options sanity checking */static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";/* * If !tun, make sure ifconfig_remote_netmask looks * like a netmask. * * If tun, make sure ifconfig_remote_netmask looks * like an IPv4 address. */static voidifconfig_sanity_check (bool tun, in_addr_t addr){ struct gc_arena gc = gc_new (); const bool looks_like_netmask = ((addr & 0xFF000000) == 0xFF000000); if (tun) { if (looks_like_netmask) msg (M_WARN, "WARNING: Since you are using --dev tun, the second argument to --ifconfig must be an IP address. You are using something (%s) that looks more like a netmask. %s", print_in_addr_t (addr, 0, &gc), ifconfig_warn_how_to_silence); } else /* tap */ { if (!looks_like_netmask) msg (M_WARN, "WARNING: Since you are using --dev tap, the second argument to --ifconfig must be a netmask, for example something like 255.255.255.0. %s", ifconfig_warn_how_to_silence); } gc_free (&gc);}/* * For TAP-style devices, generate a broadcast address. */static in_addr_tgenerate_ifconfig_broadcast_addr (in_addr_t local, in_addr_t netmask){ return local | ~netmask;}/* * Check that --local and --remote addresses do not * clash with ifconfig addresses or subnet. */static voidcheck_addr_clash (const char *name, int type, in_addr_t public, in_addr_t local, in_addr_t remote_netmask){ struct gc_arena gc = gc_new ();#if 0 msg (M_INFO, "CHECK_ADDR_CLASH type=%d public=%s local=%s, remote_netmask=%s", type, print_in_addr_t (public, 0, &gc), print_in_addr_t (local, 0, &gc), print_in_addr_t (remote_netmask, 0, &gc));#endif if (public) { if (type == DEV_TYPE_TUN) { const in_addr_t test_netmask = 0xFFFFFF00; const in_addr_t public_net = public & test_netmask; const in_addr_t local_net = local & test_netmask; const in_addr_t remote_net = remote_netmask & test_netmask; if (public == local || public == remote_netmask) msg (M_WARN, "WARNING: --%s address [%s] conflicts with --ifconfig address pair [%s, %s]. %s", name, print_in_addr_t (public, 0, &gc), print_in_addr_t (local, 0, &gc), print_in_addr_t (remote_netmask, 0, &gc), ifconfig_warn_how_to_silence); if (public_net == local_net || public_net == remote_net) msg (M_WARN, "WARNING: potential conflict between --%s address [%s] and --ifconfig address pair [%s, %s] -- this is a warning only that is triggered when local/remote addresses exist within the same /24 subnet as --ifconfig endpoints. %s", name, print_in_addr_t (public, 0, &gc), print_in_addr_t (local, 0, &gc), print_in_addr_t (remote_netmask, 0, &gc), ifconfig_warn_how_to_silence); } else if (type == DEV_TYPE_TAP) { const in_addr_t public_network = public & remote_netmask; const in_addr_t virtual_network = local & remote_netmask; if (public_network == virtual_network) msg (M_WARN, "WARNING: --%s address [%s] conflicts with --ifconfig subnet [%s, %s] -- local and remote addresses cannot be inside of the --ifconfig subnet. %s", name, print_in_addr_t (public, 0, &gc), print_in_addr_t (local, 0, &gc), print_in_addr_t (remote_netmask, 0, &gc), ifconfig_warn_how_to_silence); } } gc_free (&gc);}/* * Complain if --dev tap and --ifconfig is used on an OS for which * we don't have a custom tap ifconfig template below. */static voidno_tap_ifconfig (){ msg (M_FATAL, "Sorry but you cannot use --dev tap and --ifconfig together on this OS because I have not yet been programmed to understand the appropriate ifconfig syntax to use for TAP-style devices on this OS. Your best alternative is to use an --up script and do the ifconfig command manually.");}/* * Return a string to be used for options compatibility check * between peers. */const char *ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, struct gc_arena *gc){ struct buffer out = alloc_buf_gc (256, gc); if (tt->did_ifconfig_setup && !disable) { if (tt->type == DEV_TYPE_TUN) { const char *l, *r; if (remote) { r = print_in_addr_t (tt->local, 0, gc); l = print_in_addr_t (tt->remote_netmask, 0, gc); } else { l = print_in_addr_t (tt->local, 0, gc); r = print_in_addr_t (tt->remote_netmask, 0, gc); } buf_printf (&out, "%s %s", r, l); } else if (tt->type == DEV_TYPE_TAP) { buf_printf (&out, "%s %s", print_in_addr_t (tt->local & tt->remote_netmask, 0, gc), print_in_addr_t (tt->remote_netmask, 0, gc)); } else buf_printf (&out, "[undef]"); } return BSTR (&out);}/* * Return a status string describing wait state. */const char *tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc){ struct buffer out = alloc_buf_gc (64, gc); if (tt) { if (rwflags & EVENT_READ) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_READ) ? "R" : "r");#ifdef WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->reads));#endif } if (rwflags & EVENT_WRITE) { buf_printf (&out, "T%s", (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w");#ifdef WIN32 buf_printf (&out, "%s", overlapped_io_state_ascii (&tt->writes));#endif } } else { buf_printf (&out, "T?"); } return BSTR (&out);}/* * Init tun/tap object. * * Set up tuntap structure for ifconfig, * but don't execute yet. */struct tuntap *init_tun (const char *dev, /* --dev option */ const char *dev_type, /* --dev-type option */ const char *ifconfig_local_parm, /* --ifconfig parm 1 */ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */ in_addr_t local_public, in_addr_t remote_public, const bool strict_warn, struct env_set *es){ struct gc_arena gc = gc_new (); struct tuntap *tt; ALLOC_OBJ (tt, struct tuntap); clear_tuntap (tt); tt->type = dev_type_enum (dev, dev_type); if (ifconfig_local_parm && ifconfig_remote_netmask_parm) { bool tun = false; const char *ifconfig_local = NULL; const char *ifconfig_remote_netmask = NULL; const char *ifconfig_broadcast = NULL; /* * We only handle TUN/TAP devices here, not --dev null devices. */ if (tt->type == DEV_TYPE_TUN) tun = true; else if (tt->type == DEV_TYPE_TAP) tun = false; else msg (M_FATAL, "'%s' is not a TUN/TAP device. The --ifconfig option works only for TUN/TAP devices.", dev); /* * Convert arguments to binary IPv4 addresses. */ tt->local = getaddr ( GETADDR_RESOLVE | GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL | GETADDR_FATAL, ifconfig_local_parm, 0, NULL, NULL); tt->remote_netmask = getaddr ( (tun ? GETADDR_RESOLVE : 0) | GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL | GETADDR_FATAL, ifconfig_remote_netmask_parm, 0, NULL, NULL); /* * Look for common errors in --ifconfig parms */ if (strict_warn) { ifconfig_sanity_check (tun, tt->remote_netmask); /* * If local_public or remote_public addresses are defined, * make sure they do not clash with our virtual subnet. */ check_addr_clash ("local", tt->type, local_public, tt->local, tt->remote_netmask); check_addr_clash ("remote", tt->type, remote_public, tt->local, tt->remote_netmask); } /* * Set ifconfig parameters */ ifconfig_local = print_in_addr_t (tt->local, 0, &gc); ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); /* * If TAP-style interface, generate broadcast address. */ if (!tun) { tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask); ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc); } /* * Set environmental variables with ifconfig parameters. */ if (es) { setenv_str (es, "ifconfig_local", ifconfig_local); if (tun) { setenv_str (es, "ifconfig_remote", ifconfig_remote_netmask); } else { setenv_str (es, "ifconfig_netmask", ifconfig_remote_netmask); setenv_str (es, "ifconfig_broadcast", ifconfig_broadcast); } } tt->did_ifconfig_setup = true; } gc_free (&gc); return tt;}/* * Platform specific tun initializations */voidinit_tun_post (struct tuntap *tt, const struct frame *frame, const struct tuntap_options *options){ tt->options = *options;#ifdef WIN32 overlapped_io_init (&tt->reads, frame, FALSE, true); overlapped_io_init (&tt->writes, frame, TRUE, true); tt->rw_handle.read = tt->reads.overlapped.hEvent; tt->rw_handle.write = tt->writes.overlapped.hEvent; tt->adapter_index = ~0;#endif}/* execute the ifconfig command through the shell */voiddo_ifconfig (struct tuntap *tt, const char *actual, /* actual device name */ int tun_mtu, const struct env_set *es){ struct gc_arena gc = gc_new (); if (tt->did_ifconfig_setup) { bool tun = false; const char *ifconfig_local = NULL; const char *ifconfig_remote_netmask = NULL; const char *ifconfig_broadcast = NULL; char command_line[256]; /* * We only handle TUN/TAP devices here, not --dev null devices. */ if (tt->type == DEV_TYPE_TUN) tun = true; else if (tt->type == DEV_TYPE_TAP) tun = false; else ASSERT (0); /* should have been caught in init_tun */ /* * Set ifconfig parameters */ ifconfig_local = print_in_addr_t (tt->local, 0, &gc); ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc); /* * If TAP-style device, generate broadcast address. */ if (!tun) ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc);#ifdef ENABLE_MANAGEMENT if (management) { management_set_state (management, OPENVPN_STATE_ASSIGN_IP,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -