diff --git a/.gitignore b/.gitignore index 0d68ec4bb8e..52322c48b94 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ nbproject test-driver compile stamp-h2 +.vscode/settings.json diff --git a/src/Makefile.am b/src/Makefile.am index 313d289fc8c..815c5792b2a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,4 +12,4 @@ MAINTAINERCLEANFILES = \ $(srcdir)/Makefile.in -SUBDIRS = compat openvpn openvpnmsica openvpnserv plugins tapctl +SUBDIRS = compat openvpn openvpnmsica openvpnserv plugins tapctl diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am index f0e0ad237d0..9a628234543 100644 --- a/src/openvpn/Makefile.am +++ b/src/openvpn/Makefile.am @@ -126,6 +126,7 @@ openvpn_SOURCES = \ syshead.h \ tls_crypt.c tls_crypt.h \ tun.c tun.h \ + tun2tap.c tun2tap.h \ vlan.c vlan.h \ win32.h win32.c \ cryptoapi.h cryptoapi.c diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h index e448fc370dc..620e4c0c211 100644 --- a/src/openvpn/errlevel.h +++ b/src/openvpn/errlevel.h @@ -149,6 +149,7 @@ #define D_PUSH_DEBUG LOGLEV(7, 73, M_DEBUG) /* show push/pull debugging info */ #define D_VLAN_DEBUG LOGLEV(7, 74, M_DEBUG) /* show VLAN tagging/untagging debug info */ +#define D_TUN2TAP LOGLEV(7, 75, M_DEBUG) /* show tun2tap debug messages */ #define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */ #define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */ diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c index fd08f12df7c..e0369a590db 100644 --- a/src/openvpn/forward.c +++ b/src/openvpn/forward.c @@ -42,6 +42,7 @@ #include "dhcp.h" #include "common.h" #include "ssl_verify.h" +#include "tun2tap.h" #include "memdbg.h" @@ -949,7 +950,6 @@ read_incoming_link(struct context *c) status = link_socket_read(c->c2.link_socket, &c->c2.buf, &c->c2.from); - if (socket_connection_reset(c->c2.link_socket, status)) { #if PORT_SHARE @@ -1277,6 +1277,8 @@ read_incoming_tun(struct context *c) c->c2.buf.len = read_tun(c->c1.tuntap, BPTR(&c->c2.buf), MAX_RW_SIZE_TUN(&c->c2.frame)); #endif /* ifdef _WIN32 */ + check_tun2tap_send(c, TUN2TAP_FLAG_ENCAP); + #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify(BPTR(&c->c2.buf), BLEN(&c->c2.buf), @@ -1890,6 +1892,11 @@ process_outgoing_tun(struct context *c) &c->c2.n_trunc_tun_write); #endif + if (!check_tun2tap_send(c, TUN2TAP_FLAG_DECAP)) + { + goto cleanup; + } + #ifdef _WIN32 size = write_tun_buffered(c->c1.tuntap, &c->c2.to_tun); #else @@ -1930,6 +1937,7 @@ process_outgoing_tun(struct context *c) MAX_RW_SIZE_TUN(&c->c2.frame)); } +cleanup: buf_reset(&c->c2.to_tun); perf_pop(); diff --git a/src/openvpn/forward.h b/src/openvpn/forward.h index ff898133d81..4842f245c40 100644 --- a/src/openvpn/forward.h +++ b/src/openvpn/forward.h @@ -50,6 +50,7 @@ #include "openvpn.h" #include "occ.h" #include "ping.h" +#include "tun2tap.h" #define IOW_TO_TUN (1<<0) #define IOW_TO_LINK (1<<1) diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 70cd493a592..66cf92739a2 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -1126,6 +1126,19 @@ do_genkey(const struct options *options) } } +/* +* post check for tun2tap +*/ +void +do_check_tun2tap(const struct options *options) +{ + if (options && options->tun2tap && dev_type_enum(options->dev, options->dev_type) != DEV_TYPE_TUN) + { + msg(M_FATAL|M_OPTERR, + "options --tun2tap should only be used in tun mode"); + } +} + /* * Persistent TUN/TAP device management mode? */ @@ -1718,6 +1731,7 @@ do_init_tun(struct context *c) c->c1.tuntap = init_tun(c->options.dev, c->options.dev_type, c->options.topology, + c->options.tun2tap, c->options.ifconfig_local, c->options.ifconfig_remote_netmask, c->options.ifconfig_ipv6_local, @@ -1823,10 +1837,77 @@ do_open_tun(struct context *c) open_tun(c->options.dev, c->options.dev_type, c->options.dev_node, c->c1.tuntap); + /* + * detect tun2tap + */ + if (c->c1.tuntap->tun2tap && !c->options.lladdr){ + char *lladdr_tmp = NULL; + char mac_addr[OPENVPN_ETH_ALEN] = {0}; + char buf[4*OPENVPN_ETH_ALEN] = {0}; + int i = 0; + int offset = 0; + ASSERT(rand_bytes((unsigned char *)mac_addr, OPENVPN_ETH_ALEN)); + /* magic mac addr: 00:cc:xx:xx:xx:xx */ + mac_addr[0] = 0; + mac_addr[1] = 0; + mac_addr[2] = 'c'; + mac_addr[3] = 'c'; + for(; i < OPENVPN_ETH_ALEN; i++){ + if (i != OPENVPN_ETH_ALEN - 1){ + offset += sprintf(buf+offset, "%02x:", (unsigned char)mac_addr[i]); + } else { + offset += sprintf(buf+offset, "%02x", (unsigned char)mac_addr[i]); + } + } + lladdr_tmp = (char *)malloc(strlen((const char *)buf) + 1); + memcpy(lladdr_tmp, buf, strlen((const char *)buf)); + lladdr_tmp[strlen((const char *)buf)] = 0; + c->options.lladdr = lladdr_tmp; + } /* set the hardware address */ if (c->options.lladdr) { - set_lladdr(c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es); + int i = 0; + char *buf = strdup(c->options.lladdr); + char mac_addr[OPENVPN_ETH_ALEN] = {0}; + unsigned int mac_addr_tmp[OPENVPN_ETH_ALEN] = {0}; + int len = strlen(buf); + while(len-- > 0){ + if (buf[len] >= 'A' && buf[len] <= 'Z'){ + /* x-X=z-Z => x=z-Z+X */ + buf[len] += 'a'- 'A'; + } + } + sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x" + , &mac_addr_tmp[0] + , &mac_addr_tmp[1] + , &mac_addr_tmp[2] + , &mac_addr_tmp[3] + , &mac_addr_tmp[4] + , &mac_addr_tmp[5] + ); + while(i < OPENVPN_ETH_ALEN) + { + mac_addr[i] = 0xff & mac_addr_tmp[i]; + i++; + } + dmsg(D_TUN2TAP, "local addr is: %02x:%02x:%02x:%02x:%02x:%02x" + , (unsigned char)mac_addr[0] + , (unsigned char)mac_addr[1] + , (unsigned char)mac_addr[2] + , (unsigned char)mac_addr[3] + , (unsigned char)mac_addr[4] + , (unsigned char)mac_addr[5] + ); + memcpy(c->options.lladdr_v, mac_addr, sizeof(mac_addr)); + if (c->c1.tuntap->tun2tap && (mac_addr[0] & 1)){ + msg(M_INFO, "mac %s is mcast addr (mac[0]&1 == true)", buf); + ASSERT(0); + } + /* set_lladdr is use command to set mac address for interface, we cant set mac for tun device */ + if (TUNNEL_TYPE(c->c1.tuntap) == DEV_TYPE_TAP) + set_lladdr(c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es); + free(buf); } /* do ifconfig */ diff --git a/src/openvpn/init.h b/src/openvpn/init.h index 0e6258f0b8d..94aae9c53b8 100644 --- a/src/openvpn/init.h +++ b/src/openvpn/init.h @@ -56,6 +56,8 @@ bool print_openssl_info(const struct options *options); bool do_genkey(const struct options *options); +void do_check_tun2tap(const struct options *options); + bool do_persist_tuntap(const struct options *options, openvpn_net_ctx_t *ctx); bool possibly_become_daemon(const struct options *options); diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 7f61350d8e5..27a203a2b44 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -295,7 +295,7 @@ void multi_init(struct multi_context *m, struct context *t, bool tcp_mode, int thread_mode) { int dev = DEV_TYPE_UNDEF; - + msg(D_MULTI_LOW, "MULTI: multi_init called, r=%d v=%d", t->options.real_hash_size, t->options.virtual_hash_size); @@ -2585,7 +2585,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } perf_pop(); - if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN) + if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TUN && !m->top.options.tun2tap) { /* extract packet source and dest addresses */ mroute_flags = mroute_extract_addr_from_packet(&src, @@ -2663,7 +2663,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst } #endif } - else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP) + else if (TUNNEL_TYPE(m->top.c1.tuntap) == DEV_TYPE_TAP || m->top.options.tun2tap) { uint16_t vid = 0; #ifdef ENABLE_PF diff --git a/src/openvpn/openvpn.c b/src/openvpn/openvpn.c index dc7001dce6c..c9239662112 100644 --- a/src/openvpn/openvpn.c +++ b/src/openvpn/openvpn.c @@ -235,6 +235,9 @@ openvpn_main(int argc, char *argv[]) break; } + /* check tun2tap for tun mode */ + do_check_tun2tap(&c.options); + /* tun/tap persist command? */ if (do_persist_tuntap(&c.options, &c.net_ctx)) { diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj index 53ac5482a58..7df558e9ad9 100644 --- a/src/openvpn/openvpn.vcxproj +++ b/src/openvpn/openvpn.vcxproj @@ -198,6 +198,7 @@ + @@ -288,6 +289,7 @@ + diff --git a/src/openvpn/openvpn.vcxproj.filters b/src/openvpn/openvpn.vcxproj.filters index 80eb52b3895..1c505041c06 100644 --- a/src/openvpn/openvpn.vcxproj.filters +++ b/src/openvpn/openvpn.vcxproj.filters @@ -204,6 +204,9 @@ Source Files + + Source Files + Source Files @@ -479,6 +482,9 @@ Header Files + + Header Files + Header Files diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 2d2089e3be6..ed200f96b78 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -2061,9 +2061,9 @@ options_postprocess_verify_ce(const struct options *options, const struct connec } - if (options->lladdr && dev != DEV_TYPE_TAP) + if (options->lladdr && (dev == DEV_TYPE_TUN && !options->tun2tap) && dev != DEV_TYPE_TAP) { - msg(M_USAGE, "--lladdr can only be used in --dev tap mode"); + msg(M_USAGE, "--lladdr can only be used in --dev tap mode or tun2tap"); } /* @@ -3703,6 +3703,7 @@ options_string(const struct options *o, tt = init_tun(o->dev, o->dev_type, o->topology, + o->tun2tap, o->ifconfig_local, o->ifconfig_remote_netmask, o->ifconfig_ipv6_local, @@ -3727,6 +3728,12 @@ options_string(const struct options *o, buf_printf(&out, ",ifconfig %s", ios); } } + + if (tt && tt->tun2tap) + { + buf_printf(&out, ",tun2tap"); + } + if (tt_local) { free(tt); @@ -5445,6 +5452,11 @@ add_option(struct options *options, goto err; } } + else if (streq(p[0], "tun2tap") && !p[1]) + { + /* check in post do_xxxx */ + options->tun2tap = true; + } else if (streq(p[0], "topology") && p[1] && !p[2]) { VERIFY_PERMISSION(OPT_P_UP); diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 375a4fc9712..2aeb19c65e9 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -213,7 +213,7 @@ struct options /* persist parms */ bool persist_config; int persist_mode; - + bool tun2tap; const char *key_pass_file; bool show_ciphers; bool show_digests; @@ -248,6 +248,7 @@ struct options const char *dev_type; const char *dev_node; const char *lladdr; + uint8_t lladdr_v[OPENVPN_ETH_ALEN]; int topology; /* one of the TOP_x values from proto.h */ const char *ifconfig_local; const char *ifconfig_remote_netmask; diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c index 8e692977e98..f26b03b8f2d 100644 --- a/src/openvpn/tun.c +++ b/src/openvpn/tun.c @@ -556,7 +556,7 @@ is_tun_p2p(const struct tuntap *tt) bool tun = false; if (tt->type == DEV_TYPE_TAP - || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET) + || (tt->type == DEV_TYPE_TUN && (tt->topology == TOP_SUBNET || tt->tun2tap)) || tt->type == DEV_TYPE_NULL) { tun = false; @@ -624,6 +624,7 @@ 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 bool tun2tap, /* --tun2tap */ 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 */ @@ -644,6 +645,13 @@ init_tun(const char *dev, /* --dev option */ tt->type = dev_type_enum(dev, dev_type); tt->topology = topology; + if (tt->type == DEV_TYPE_TUN) + { + tt->tun2tap = tun2tap; + } else { + tt->tun2tap = false; + } + if (ifconfig_local_parm && ifconfig_remote_netmask_parm) { bool tun = false; @@ -1150,7 +1158,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, argv_printf(&argv, "%s %s netmask 255.255.255.255", IFCONFIG_PATH, ifname); } - else if (tt->topology == TOP_SUBNET) + else if (tt->topology == TOP_SUBNET || tt->tun2tap) { argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_local, @@ -1169,7 +1177,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, solaris_error_close(tt, es, ifname, false); } - if (!tun && tt->topology == TOP_SUBNET) + if (!tun && (tt->topology == TOP_SUBNET || tt->tun2tap)) { /* Add a network route for the local tun interface */ struct route_ipv4 r; @@ -1200,7 +1208,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); } - else if (tt->topology == TOP_SUBNET) + else if (tt->topology == TOP_SUBNET || tt->tun2tap) { remote_end = create_arbitrary_remote( tt ); argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up -link0", @@ -1218,7 +1226,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, openvpn_execve_check(&argv, es, S_FATAL, "OpenBSD ifconfig failed"); /* Add a network route for the local tun interface */ - if (!tun && tt->topology == TOP_SUBNET) + if (!tun && (tt->topology == TOP_SUBNET || tt->tun2tap)) { struct route_ipv4 r; CLEAR(r); @@ -1238,7 +1246,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); } - else if (tt->topology == TOP_SUBNET) + else if (tt->topology == TOP_SUBNET || tt->tun2tap) { remote_end = create_arbitrary_remote(tt); argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up", IFCONFIG_PATH, @@ -1260,7 +1268,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, openvpn_execve_check(&argv, es, S_FATAL, "NetBSD ifconfig failed"); /* Add a network route for the local tun interface */ - if (!tun && tt->topology == TOP_SUBNET) + if (!tun && (tt->topology == TOP_SUBNET || tt->tun2tap)) { struct route_ipv4 r; CLEAR(r); @@ -1292,7 +1300,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, } else { - if (tt->topology == TOP_SUBNET) + if (tt->topology == TOP_SUBNET || tt->tun2tap) { argv_printf(&argv, "%s %s %s %s netmask %s mtu %d up", IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_local, @@ -1310,7 +1318,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, openvpn_execve_check(&argv, es, S_FATAL, "Mac OS X ifconfig failed"); /* Add a network route for the local tun interface */ - if (!tun && tt->topology == TOP_SUBNET) + if (!tun && (tt->topology == TOP_SUBNET || tt->tun2tap)) { struct route_ipv4 r; CLEAR(r); @@ -1332,7 +1340,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, IFCONFIG_PATH, ifname, ifconfig_local, ifconfig_remote_netmask, tun_mtu); } - else if (tt->topology == TOP_SUBNET) + else if (tt->topology == TOP_SUBNET || tt->tun2tap) { remote_end = create_arbitrary_remote( tt ); argv_printf(&argv, "%s %s %s %s mtu %d netmask %s up", IFCONFIG_PATH, @@ -1349,7 +1357,7 @@ do_ifconfig_ipv4(struct tuntap *tt, const char *ifname, int tun_mtu, openvpn_execve_check(&argv, es, S_FATAL, "FreeBSD ifconfig failed"); /* Add a network route for the local tun interface */ - if (!tun && tt->topology == TOP_SUBNET) + if (!tun && (tt->topology == TOP_SUBNET || tt->tun2tap)) { struct route_ipv4 r; CLEAR(r); diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h index b38e7e90bff..6d7b9e594ba 100644 --- a/src/openvpn/tun.h +++ b/src/openvpn/tun.h @@ -149,7 +149,6 @@ struct tuntap #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; @@ -157,6 +156,8 @@ struct tuntap struct tuntap_options options; /* options set on command line */ + bool tun2tap; /* options set on --tun2tap for tun mode */ + char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */ /* number of TX buffers */ @@ -168,6 +169,7 @@ struct tuntap struct in6_addr local_ipv6; struct in6_addr remote_ipv6; + uint8_t remote_mac_addr[OPENVPN_ETH_ALEN]; int netbits_ipv6; #ifdef _WIN32 @@ -263,6 +265,7 @@ const char *guess_tuntap_dev(const char *dev, 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 bool tun2tap, /* --tun2tap */ 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 */ diff --git a/src/openvpn/tun2tap.c b/src/openvpn/tun2tap.c new file mode 100644 index 00000000000..5ca5378d9ca --- /dev/null +++ b/src/openvpn/tun2tap.c @@ -0,0 +1,179 @@ +/* + * 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 + * + * 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. + * + * Author: pengtianabc@hotmail.com + * Date: 2020-4-12 11:17:33 + * Function: simulate arp logical for tun device on client + * TODO: ship for server, but i think that not worth, most server is on linux that easy to use tap +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "tun2tap.h" + +#include "memdbg.h" + +void build_arp_reply(struct context *c, struct openvpn_ethhdr *hdr_out, struct openvpn_arp *arp_in, struct openvpn_arp *arp_out); +void build_arp_reply(struct context *c, struct openvpn_ethhdr *hdr_out, struct openvpn_arp *arp_in, struct openvpn_arp *arp_out) +{ + if (!c || !hdr_out || !arp_in || !arp_out) + { + return; + } + memset(hdr_out, 0, sizeof(*hdr_out)); + memset(arp_out, 0, sizeof(*arp_out)); + hdr_out->proto = htons(OPENVPN_ETH_P_ARP); + arp_out->mac_addr_type = htons(0x0001); + arp_out->proto_addr_type = htons(0x0800); + arp_out->mac_addr_size = 0x06; + arp_out->proto_addr_size = 0x04; + arp_out->arp_command = htons(ARP_REPLY); + memcpy(arp_out->mac_src, c->options.lladdr_v, OPENVPN_ETH_ALEN); + memcpy(arp_out->mac_dest, arp_in->mac_src, OPENVPN_ETH_ALEN); + memcpy(hdr_out->source, c->options.lladdr_v, OPENVPN_ETH_ALEN); + memcpy(hdr_out->dest, c->c1.tuntap->remote_mac_addr, OPENVPN_ETH_ALEN); + arp_out->ip_src = arp_in->ip_dest; + arp_out->ip_dest = arp_in->ip_src; +} + +/* + * arp check and build for tun + */ +bool +check_tun2tap_arp_dowork(struct context *c, int flag) +{ + int ret = true; + if (!c->options.tun2tap){ + return ret; + } + if (TUN2TAP_FLAG_ENCAP == flag){ + dmsg(D_TUN2TAP, "TUN2TAP: encap data from tun"); + /* read from tun, check and send arp to link */ + if ( BLEN(&c->c2.buf) >= sizeof(struct openvpn_iphdr)){ + struct openvpn_ethhdr hdr = {0}; + struct openvpn_iphdr *ip_hdr = (struct openvpn_iphdr *)BPTR(&c->c2.buf); + /* + struct openvpn_ipv6hdr *ipv6_hdr = (struct openvpn_ipv6hdr *)BPTR(&c->c2.buf); + */ + int v = OPENVPN_IPH_GET_VER(ip_hdr->version_len); + memcpy(hdr.dest, c->c1.tuntap->remote_mac_addr, OPENVPN_ETH_ALEN); + memcpy(hdr.source, c->options.lladdr_v, OPENVPN_ETH_ALEN); + /* + * if client send the first packet to server after connected, remote_mac_addr is zero + * so we build a arp request to replace this packet. + */ + if (0 == memcmp(hdr.dest, "\x00\x00\x00\x00\x00\x00", OPENVPN_ETH_ALEN)){ + struct openvpn_arp arp = { 0 }; + arp.mac_addr_type = htons(0x0001); + arp.proto_addr_type = htons(0x0800); + arp.mac_addr_size = 0x06; + arp.proto_addr_size = 0x04; + arp.arp_command = htons(ARP_REQUEST); + memcpy(arp.mac_src, hdr.source, OPENVPN_ETH_ALEN); + memcpy(arp.mac_dest, "\x00\x00\x00\x00\x00\x00", OPENVPN_ETH_ALEN); + memcpy(hdr.dest, "\xff\xff\xff\xff\xff\xff", OPENVPN_ETH_ALEN); + hdr.proto = htons(OPENVPN_ETH_P_ARP); + switch (v) + { + case 4: + arp.ip_src = ip_hdr->saddr; + arp.ip_dest = ip_hdr->daddr; + break; + case 6: + /* + do nothing, arp has been removed in ipv6 + so we will not receive any ipv6 packet without mac + this case will never in + */ + default: + break; + } + buf_clear(&c->c2.buf); + buf_write(&c->c2.buf, &hdr, sizeof(hdr)); + buf_write(&c->c2.buf, &arp, sizeof(arp)); + } else { + switch (v) + { + case 4: + hdr.proto = htons(OPENVPN_ETH_P_IPV4); + break; + case 6: + hdr.proto = htons(OPENVPN_ETH_P_IPV6); + default: + break; + } + ASSERT(buf_write_prepend(&c->c2.buf, &hdr, sizeof(hdr))); + } + } + } else if (TUN2TAP_FLAG_DECAP == flag){ + dmsg(D_TUN2TAP, "TUN2TAP: decap data to tun"); + /* will write to tun, check is arp request, and save remote addr */ + if (BLEN(&c->c2.buf) >= sizeof(struct openvpn_ethhdr)){ + struct openvpn_ethhdr *hdr = (struct openvpn_ethhdr *) BPTR(&c->c2.buf); + memcpy(c->c1.tuntap->remote_mac_addr, hdr->source, OPENVPN_ETH_ALEN); + if (hdr->proto == htons(OPENVPN_ETH_P_ARP)){ + dmsg(D_TUN2TAP, "TUN2TAP: processing arp from tun"); + if (BLEN(&c->c2.buf) >= sizeof(struct openvpn_ethhdr) + sizeof(struct openvpn_arp)){ + struct openvpn_arp *arp_in = (struct openvpn_arp *)(BPTR(&c->c2.buf) + sizeof(struct openvpn_ethhdr)); + if (arp_in->arp_command == htons(ARP_REPLY)){ + dmsg(D_TUN2TAP, "TUN2TAP: ignore arp reply"); + buf_clear(&c->c2.buf); + ret = false; + } else if (arp_in->arp_command == htons(ARP_REQUEST) ){ + if (arp_in->ip_dest == htonl(c->c1.tuntap->local)){ + /* + * build reply and write arp to link + */ + struct openvpn_ethhdr hdr_out; + struct openvpn_arp arp_out; + build_arp_reply(c, &hdr_out, arp_in, &arp_out); + buf_clear(&c->c2.buf); + ASSERT(buf_init(&c->c2.buf, FRAME_HEADROOM(&c->c2.frame))); + ASSERT(buf_safe(&c->c2.buf, MAX_RW_SIZE_TUN(&c->c2.frame))); + buf_write(&c->c2.buf, &hdr_out, sizeof(hdr_out)); + buf_write(&c->c2.buf, &arp_out, sizeof(arp_out)); + dmsg(D_TUN2TAP, "TUN2TAP: build arp reply success"); + encrypt_sign(c, true); + } else { + dmsg(D_TUN2TAP, "TUN2TAP: ignore any arp request not to me dest:%x me:%x", ntohl(arp_in->ip_dest), c->c1.tuntap->local); + buf_clear(&c->c2.buf); + ret = false; + } + } else{ + dmsg(D_TUN2TAP, "TUN2TAP: ignore uknown arp type: %x", ntohs(arp_in->arp_command)); + buf_clear(&c->c2.buf); + ret = false; + } + } + } else { + dmsg(D_TUN2TAP, "TUN2TAP: trim ethhdr for tun"); + buf_advance(&c->c2.to_tun, sizeof(struct openvpn_ethhdr)); + } + } + } + return ret; +} diff --git a/src/openvpn/tun2tap.h b/src/openvpn/tun2tap.h new file mode 100644 index 00000000000..5c64daec0ec --- /dev/null +++ b/src/openvpn/tun2tap.h @@ -0,0 +1,44 @@ +/* + * 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 + * + * 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. + * + * Author: pengtianabc@hotmail.com + */ + +#ifndef TUN2TAP_H +#define TUN2TAP_H + +#include "init.h" +#include "forward.h" +#define TUN2TAP_FLAG_ENCAP 1 +#define TUN2TAP_FLAG_DECAP (1<<1) + +/* + * arp check and build for tun + */ +static inline bool +check_tun2tap_send(struct context *c, int flag) +{ + bool check_tun2tap_arp_dowork(struct context *c, int flag); + return check_tun2tap_arp_dowork(c, flag); +} + +#endif /* ifndef TUN2TAP_H */