| /* |
| * 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-2018 OpenVPN Inc <sales@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; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #ifndef TUN_H |
| #define TUN_H |
| |
| #ifdef _WIN32 |
| #include <winioctl.h> |
| #include <tap-windows.h> |
| #include <setupapi.h> |
| #include <cfgmgr32.h> |
| #endif |
| |
| #include "buffer.h" |
| #include "error.h" |
| #include "mtu.h" |
| #include "win32.h" |
| #include "event.h" |
| #include "proto.h" |
| #include "misc.h" |
| #include "networking.h" |
| #include "ring_buffer.h" |
| |
| #ifdef _WIN32 |
| #define WINTUN_COMPONENT_ID "wintun" |
| |
| enum windows_driver_type { |
| WINDOWS_DRIVER_UNSPECIFIED, |
| WINDOWS_DRIVER_TAP_WINDOWS6, |
| WINDOWS_DRIVER_WINTUN |
| }; |
| #endif |
| |
| #if defined(_WIN32) || defined(TARGET_ANDROID) |
| |
| #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1) |
| |
| /* time constants for --ip-win32 adaptive */ |
| #define IPW32_SET_ADAPTIVE_DELAY_WINDOW 300 |
| #define IPW32_SET_ADAPTIVE_TRY_NETSH 20 |
| |
| struct tuntap_options { |
| /* --ip-win32 options */ |
| bool ip_win32_defined; |
| |
| #define IPW32_SET_MANUAL 0 /* "--ip-win32 manual" */ |
| #define IPW32_SET_NETSH 1 /* "--ip-win32 netsh" */ |
| #define IPW32_SET_IPAPI 2 /* "--ip-win32 ipapi" */ |
| #define IPW32_SET_DHCP_MASQ 3 /* "--ip-win32 dynamic" */ |
| #define IPW32_SET_ADAPTIVE 4 /* "--ip-win32 adaptive" */ |
| #define IPW32_SET_N 5 |
| int ip_win32_type; |
| |
| #ifdef _WIN32 |
| HANDLE msg_channel; |
| #endif |
| |
| /* --ip-win32 dynamic options */ |
| bool dhcp_masq_custom_offset; |
| int dhcp_masq_offset; |
| int dhcp_lease_time; |
| |
| /* --tap-sleep option */ |
| int tap_sleep; |
| |
| /* --dhcp-option options */ |
| |
| bool dhcp_options; |
| |
| const char *domain; /* DOMAIN (15) */ |
| |
| const char *netbios_scope; /* NBS (47) */ |
| |
| int netbios_node_type; /* NBT 1,2,4,8 (46) */ |
| |
| #define N_DHCP_ADDR 4 /* Max # of addresses allowed for |
| * DNS, WINS, etc. */ |
| |
| /* DNS (6) */ |
| in_addr_t dns[N_DHCP_ADDR]; |
| int dns_len; |
| |
| /* WINS (44) */ |
| in_addr_t wins[N_DHCP_ADDR]; |
| int wins_len; |
| |
| /* NTP (42) */ |
| in_addr_t ntp[N_DHCP_ADDR]; |
| int ntp_len; |
| |
| /* NBDD (45) */ |
| in_addr_t nbdd[N_DHCP_ADDR]; |
| int nbdd_len; |
| |
| #define N_SEARCH_LIST_LEN 10 /* Max # of entries in domin-search list */ |
| |
| /* SEARCH (119), MacOS, Linux, Win10 1809+ */ |
| const char *domain_search_list[N_SEARCH_LIST_LEN]; |
| int domain_search_list_len; |
| |
| /* DISABLE_NBT (43, Vendor option 001) */ |
| bool disable_nbt; |
| |
| bool dhcp_renew; |
| bool dhcp_pre_release; |
| |
| bool register_dns; |
| |
| struct in6_addr dns6[N_DHCP_ADDR]; |
| int dns6_len; |
| }; |
| |
| #elif TARGET_LINUX |
| |
| struct tuntap_options { |
| int txqueuelen; |
| }; |
| |
| #else /* if defined(_WIN32) || defined(TARGET_ANDROID) */ |
| |
| struct tuntap_options { |
| int dummy; /* not used */ |
| }; |
| |
| #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */ |
| |
| /* |
| * Define a TUN/TAP dev. |
| */ |
| |
| struct tuntap |
| { |
| #define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF) |
| int type; /* DEV_TYPE_x as defined in proto.h */ |
| |
| #define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF) |
| int topology; /* one of the TOP_x values */ |
| |
| bool did_ifconfig_setup; |
| bool did_ifconfig_ipv6_setup; |
| |
| bool persistent_if; /* if existed before, keep on program end */ |
| |
| struct tuntap_options options; /* options set on command line */ |
| |
| char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */ |
| |
| /* number of TX buffers */ |
| int txqueuelen; |
| |
| /* ifconfig parameters */ |
| in_addr_t local; |
| in_addr_t remote_netmask; |
| |
| struct in6_addr local_ipv6; |
| struct in6_addr remote_ipv6; |
| int netbits_ipv6; |
| |
| #ifdef _WIN32 |
| HANDLE hand; |
| struct overlapped_io reads; |
| struct overlapped_io writes; |
| struct rw_handle rw_handle; |
| |
| /* used for setting interface address via IP Helper API |
| * or DHCP masquerade */ |
| bool ipapi_context_defined; |
| ULONG ipapi_context; |
| ULONG ipapi_instance; |
| in_addr_t adapter_netmask; |
| |
| /* Windows adapter index for TAP-Windows adapter, |
| * ~0 if undefined */ |
| DWORD adapter_index; |
| |
| enum windows_driver_type windows_driver; |
| int standby_iter; |
| |
| HANDLE wintun_send_ring_handle; |
| HANDLE wintun_receive_ring_handle; |
| struct tun_ring *wintun_send_ring; |
| struct tun_ring *wintun_receive_ring; |
| #else /* ifdef _WIN32 */ |
| int fd; /* file descriptor for TUN/TAP dev */ |
| #endif /* ifdef _WIN32 */ |
| |
| #ifdef TARGET_SOLARIS |
| int ip_fd; |
| #endif |
| |
| #ifdef HAVE_NET_IF_UTUN_H |
| bool is_utun; |
| #endif |
| /* used for printing status info only */ |
| unsigned int rwflags_debug; |
| |
| /* Some TUN/TAP drivers like to be ioctled for mtu |
| * after open */ |
| int post_open_mtu; |
| }; |
| |
| static inline bool |
| tuntap_defined(const struct tuntap *tt) |
| { |
| #ifdef _WIN32 |
| return tt && tt->hand != NULL; |
| #else |
| return tt && tt->fd >= 0; |
| #endif |
| } |
| |
| #ifdef _WIN32 |
| static inline bool |
| tuntap_is_wintun(struct tuntap *tt) |
| { |
| return tt && tt->windows_driver == WINDOWS_DRIVER_WINTUN; |
| } |
| |
| static inline bool |
| tuntap_ring_empty(struct tuntap *tt) |
| { |
| return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail); |
| } |
| #endif |
| |
| /* |
| * Function prototypes |
| */ |
| |
| void open_tun(const char *dev, const char *dev_type, const char *dev_node, |
| struct tuntap *tt); |
| |
| void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx); |
| |
| int write_tun(struct tuntap *tt, uint8_t *buf, int len); |
| |
| int read_tun(struct tuntap *tt, uint8_t *buf, int len); |
| |
| void tuncfg(const char *dev, const char *dev_type, const char *dev_node, |
| int persist_mode, const char *username, |
| const char *groupname, const struct tuntap_options *options, |
| openvpn_net_ctx_t *ctx); |
| |
| const char *guess_tuntap_dev(const char *dev, |
| const char *dev_type, |
| const char *dev_node, |
| struct gc_arena *gc); |
| |
| struct tuntap *init_tun(const char *dev, /* --dev option */ |
| const char *dev_type, /* --dev-type option */ |
| int topology, /* one of the TOP_x values */ |
| const char *ifconfig_local_parm, /* --ifconfig parm 1 */ |
| const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */ |
| const char *ifconfig_ipv6_local_parm, /* --ifconfig parm 1 / IPv6 */ |
| int ifconfig_ipv6_netbits_parm, /* --ifconfig parm 1 / bits */ |
| const char *ifconfig_ipv6_remote_parm, /* --ifconfig parm 2 / IPv6 */ |
| struct addrinfo *local_public, |
| struct addrinfo *remote_public, |
| const bool strict_warn, |
| struct env_set *es, |
| openvpn_net_ctx_t *ctx); |
| |
| void init_tun_post(struct tuntap *tt, |
| const struct frame *frame, |
| const struct tuntap_options *options); |
| |
| void do_ifconfig_setenv(const struct tuntap *tt, |
| struct env_set *es); |
| |
| /** |
| * do_ifconfig - configure the tunnel interface |
| * |
| * @param tt the tuntap interface context |
| * @param ifname the human readable interface name |
| * @param mtu the MTU value to set the interface to |
| * @param es the environment to be used when executing the commands |
| * @param ctx the networking API opaque context |
| */ |
| void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu, |
| const struct env_set *es, openvpn_net_ctx_t *ctx); |
| |
| bool is_dev_type(const char *dev, const char *dev_type, const char *match_type); |
| |
| int dev_type_enum(const char *dev, const char *dev_type); |
| |
| const char *dev_type_string(const char *dev, const char *dev_type); |
| |
| const char *ifconfig_options_string(const struct tuntap *tt, bool remote, bool disable, struct gc_arena *gc); |
| |
| bool is_tun_p2p(const struct tuntap *tt); |
| |
| void check_subnet_conflict(const in_addr_t ip, |
| const in_addr_t netmask, |
| const char *prefix); |
| |
| void warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx); |
| |
| /* |
| * Inline functions |
| */ |
| |
| static inline void |
| tun_adjust_frame_parameters(struct frame *frame, int size) |
| { |
| frame_add_to_extra_tun(frame, size); |
| } |
| |
| /* |
| * Should ifconfig be called before or after |
| * tun dev open? |
| */ |
| |
| #define IFCONFIG_BEFORE_TUN_OPEN 0 |
| #define IFCONFIG_AFTER_TUN_OPEN 1 |
| |
| #define IFCONFIG_DEFAULT IFCONFIG_AFTER_TUN_OPEN |
| |
| static inline int |
| ifconfig_order(void) |
| { |
| #if defined(TARGET_LINUX) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(TARGET_SOLARIS) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(TARGET_OPENBSD) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(TARGET_DARWIN) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(TARGET_NETBSD) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(_WIN32) |
| return IFCONFIG_AFTER_TUN_OPEN; |
| #elif defined(TARGET_ANDROID) |
| return IFCONFIG_BEFORE_TUN_OPEN; |
| #else /* if defined(TARGET_LINUX) */ |
| return IFCONFIG_DEFAULT; |
| #endif |
| } |
| |
| #define ROUTE_BEFORE_TUN 0 |
| #define ROUTE_AFTER_TUN 1 |
| #define ROUTE_ORDER_DEFAULT ROUTE_AFTER_TUN |
| |
| static inline int |
| route_order(void) |
| { |
| #if defined(TARGET_ANDROID) |
| return ROUTE_BEFORE_TUN; |
| #else |
| return ROUTE_ORDER_DEFAULT; |
| #endif |
| } |
| |
| |
| #ifdef _WIN32 |
| |
| struct tap_reg |
| { |
| const char *guid; |
| enum windows_driver_type windows_driver; |
| struct tap_reg *next; |
| }; |
| |
| struct panel_reg |
| { |
| const char *name; |
| const char *guid; |
| struct panel_reg *next; |
| }; |
| |
| struct device_instance_id_interface |
| { |
| const char *net_cfg_instance_id; |
| const char *device_interface_list; |
| struct device_instance_id_interface *next; |
| }; |
| |
| int ascii2ipset(const char *name); |
| |
| const char *ipset2ascii(int index); |
| |
| const char *ipset2ascii_all(struct gc_arena *gc); |
| |
| void verify_255_255_255_252(in_addr_t local, in_addr_t remote); |
| |
| const IP_ADAPTER_INFO *get_adapter_info_list(struct gc_arena *gc); |
| |
| const IP_ADAPTER_INFO *get_tun_adapter(const struct tuntap *tt, const IP_ADAPTER_INFO *list); |
| |
| const IP_ADAPTER_INFO *get_adapter_info(DWORD index, struct gc_arena *gc); |
| |
| const IP_PER_ADAPTER_INFO *get_per_adapter_info(const DWORD index, struct gc_arena *gc); |
| |
| const IP_ADAPTER_INFO *get_adapter(const IP_ADAPTER_INFO *ai, DWORD index); |
| |
| bool is_adapter_up(const struct tuntap *tt, const IP_ADAPTER_INFO *list); |
| |
| bool is_ip_in_adapter_subnet(const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask); |
| |
| DWORD adapter_index_of_ip(const IP_ADAPTER_INFO *list, |
| const in_addr_t ip, |
| int *count, |
| in_addr_t *netmask); |
| |
| void show_tap_win_adapters(int msglev, int warnlev); |
| |
| void show_adapters(int msglev); |
| |
| void tap_allow_nonadmin_access(const char *dev_node); |
| |
| void show_valid_win32_tun_subnets(void); |
| |
| const char *tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc); |
| |
| void tun_show_debug(struct tuntap *tt); |
| |
| bool dhcp_release_by_adapter_index(const DWORD adapter_index); |
| |
| bool dhcp_renew_by_adapter_index(const DWORD adapter_index); |
| |
| void fork_register_dns_action(struct tuntap *tt); |
| |
| void ipconfig_register_dns(const struct env_set *es); |
| |
| void tun_standby_init(struct tuntap *tt); |
| |
| bool tun_standby(struct tuntap *tt); |
| |
| int tun_read_queue(struct tuntap *tt, int maxsize); |
| |
| int tun_write_queue(struct tuntap *tt, struct buffer *buf); |
| |
| int tun_finalize(HANDLE h, struct overlapped_io *io, struct buffer *buf); |
| |
| static inline bool |
| tuntap_stop(int status) |
| { |
| /* |
| * This corresponds to the STATUS_NO_SUCH_DEVICE |
| * error in tapdrvr.c. |
| */ |
| if (status < 0) |
| { |
| return openvpn_errno() == ERROR_FILE_NOT_FOUND; |
| } |
| return false; |
| } |
| |
| static inline bool |
| tuntap_abort(int status) |
| { |
| /* |
| * Typically generated when driver is halted. |
| */ |
| if (status < 0) |
| { |
| return openvpn_errno() == ERROR_OPERATION_ABORTED; |
| } |
| return false; |
| } |
| |
| static inline int |
| tun_write_win32(struct tuntap *tt, struct buffer *buf) |
| { |
| int err = 0; |
| int status = 0; |
| if (overlapped_io_active(&tt->writes)) |
| { |
| status = tun_finalize(tt->hand, &tt->writes, NULL); |
| if (status < 0) |
| { |
| err = GetLastError(); |
| } |
| } |
| tun_write_queue(tt, buf); |
| if (status < 0) |
| { |
| SetLastError(err); |
| return status; |
| } |
| else |
| { |
| return BLEN(buf); |
| } |
| } |
| |
| static inline int |
| read_tun_buffered(struct tuntap *tt, struct buffer *buf) |
| { |
| return tun_finalize(tt->hand, &tt->reads, buf); |
| } |
| |
| static inline ULONG |
| wintun_ring_packet_align(ULONG size) |
| { |
| return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1); |
| } |
| |
| static inline ULONG |
| wintun_ring_wrap(ULONG value) |
| { |
| return value & (WINTUN_RING_CAPACITY - 1); |
| } |
| |
| static inline void |
| read_wintun(struct tuntap *tt, struct buffer *buf) |
| { |
| struct tun_ring *ring = tt->wintun_send_ring; |
| ULONG head = ring->head; |
| ULONG tail = ring->tail; |
| ULONG content_len; |
| struct TUN_PACKET *packet; |
| ULONG aligned_packet_size; |
| |
| *buf = tt->reads.buf_init; |
| buf->len = 0; |
| |
| if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) |
| { |
| msg(M_INFO, "Wintun: ring capacity exceeded"); |
| buf->len = -1; |
| return; |
| } |
| |
| if (head == tail) |
| { |
| /* nothing to read */ |
| return; |
| } |
| |
| content_len = wintun_ring_wrap(tail - head); |
| if (content_len < sizeof(struct TUN_PACKET_HEADER)) |
| { |
| msg(M_INFO, "Wintun: incomplete packet header in send ring"); |
| buf->len = -1; |
| return; |
| } |
| |
| packet = (struct TUN_PACKET *) &ring->data[head]; |
| if (packet->size > WINTUN_MAX_PACKET_SIZE) |
| { |
| msg(M_INFO, "Wintun: packet too big in send ring"); |
| buf->len = -1; |
| return; |
| } |
| |
| aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + packet->size); |
| if (aligned_packet_size > content_len) |
| { |
| msg(M_INFO, "Wintun: incomplete packet in send ring"); |
| buf->len = -1; |
| return; |
| } |
| |
| buf_write(buf, packet->data, packet->size); |
| |
| head = wintun_ring_wrap(head + aligned_packet_size); |
| ring->head = head; |
| } |
| |
| static inline bool |
| is_ip_packet_valid(const struct buffer *buf) |
| { |
| const struct openvpn_iphdr *ih = (const struct openvpn_iphdr *)BPTR(buf); |
| |
| if (OPENVPN_IPH_GET_VER(ih->version_len) == 4) |
| { |
| if (BLEN(buf) < sizeof(struct openvpn_iphdr)) |
| { |
| return false; |
| } |
| } |
| else if (OPENVPN_IPH_GET_VER(ih->version_len) == 6) |
| { |
| if (BLEN(buf) < sizeof(struct openvpn_ipv6hdr)) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static inline int |
| write_wintun(struct tuntap *tt, struct buffer *buf) |
| { |
| struct tun_ring *ring = tt->wintun_receive_ring; |
| ULONG head = ring->head; |
| ULONG tail = ring->tail; |
| ULONG aligned_packet_size; |
| ULONG buf_space; |
| struct TUN_PACKET *packet; |
| |
| /* wintun marks ring as corrupted (overcapacity) if it receives invalid IP packet */ |
| if (!is_ip_packet_valid(buf)) |
| { |
| msg(D_LOW, "write_wintun(): drop invalid IP packet"); |
| return 0; |
| } |
| |
| if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY)) |
| { |
| msg(M_INFO, "write_wintun(): head/tail value is over capacity"); |
| return -1; |
| } |
| |
| aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + BLEN(buf)); |
| buf_space = wintun_ring_wrap(head - tail - WINTUN_PACKET_ALIGN); |
| if (aligned_packet_size > buf_space) |
| { |
| msg(M_INFO, "write_wintun(): ring is full"); |
| return 0; |
| } |
| |
| /* copy packet size and data into ring */ |
| packet = (struct TUN_PACKET * )&ring->data[tail]; |
| packet->size = BLEN(buf); |
| memcpy(packet->data, BPTR(buf), BLEN(buf)); |
| |
| /* move ring tail */ |
| ring->tail = wintun_ring_wrap(tail + aligned_packet_size); |
| if (ring->alertable != 0) |
| { |
| SetEvent(tt->rw_handle.write); |
| } |
| |
| return BLEN(buf); |
| } |
| |
| static inline int |
| write_tun_buffered(struct tuntap *tt, struct buffer *buf) |
| { |
| if (tt->windows_driver == WINDOWS_DRIVER_WINTUN) |
| { |
| return write_wintun(tt, buf); |
| } |
| else |
| { |
| return tun_write_win32(tt, buf); |
| } |
| } |
| |
| #else /* ifdef _WIN32 */ |
| |
| static inline bool |
| tuntap_stop(int status) |
| { |
| return false; |
| } |
| |
| static inline bool |
| tuntap_abort(int status) |
| { |
| return false; |
| } |
| |
| static inline void |
| tun_standby_init(struct tuntap *tt) |
| { |
| } |
| |
| static inline bool |
| tun_standby(struct tuntap *tt) |
| { |
| return true; |
| } |
| |
| #endif /* ifdef _WIN32 */ |
| |
| /* |
| * TUN/TAP I/O wait functions |
| */ |
| |
| static inline event_t |
| tun_event_handle(const struct tuntap *tt) |
| { |
| #ifdef _WIN32 |
| return &tt->rw_handle; |
| #else |
| return tt->fd; |
| #endif |
| } |
| |
| static inline void |
| tun_set(struct tuntap *tt, |
| struct event_set *es, |
| unsigned int rwflags, |
| void *arg, |
| unsigned int *persistent) |
| { |
| if (tuntap_defined(tt)) |
| { |
| /* if persistent is defined, call event_ctl only if rwflags has changed since last call */ |
| if (!persistent || *persistent != rwflags) |
| { |
| event_ctl(es, tun_event_handle(tt), rwflags, arg); |
| if (persistent) |
| { |
| *persistent = rwflags; |
| } |
| } |
| #ifdef _WIN32 |
| if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ)) |
| { |
| tun_read_queue(tt, 0); |
| } |
| #endif |
| tt->rwflags_debug = rwflags; |
| } |
| } |
| |
| const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc); |
| |
| #endif /* TUN_H */ |