diff options
Diffstat (limited to 'src/openvpn/route.c')
-rw-r--r-- | src/openvpn/route.c | 657 |
1 files changed, 259 insertions, 398 deletions
diff --git a/src/openvpn/route.c b/src/openvpn/route.c index 4199da3..24563ed 100644 --- a/src/openvpn/route.c +++ b/src/openvpn/route.c @@ -36,11 +36,12 @@ #include "common.h" #include "error.h" #include "route.h" -#include "misc.h" +#include "run_command.h" #include "socket.h" #include "manage.h" #include "win32.h" #include "options.h" +#include "networking.h" #include "memdbg.h" @@ -62,7 +63,7 @@ static bool del_route_ipv6_service(const struct route_ipv6 *, const struct tunta #endif -static void delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es); +static void delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es, openvpn_net_ctx_t *ctx); static void get_bypass_addresses(struct route_bypass *rb, const unsigned int flags); @@ -448,11 +449,6 @@ init_route_ipv6(struct route_ipv6 *r6, { r6->gateway = rl6->remote_endpoint_ipv6; } - else - { - msg(M_WARN, PACKAGE_NAME " ROUTE6: " PACKAGE_NAME " needs a gateway parameter for a --route-ipv6 option and no default was specified by either --route-ipv6-gateway or --ifconfig-ipv6 options"); - goto fail; - } /* metric */ @@ -613,7 +609,8 @@ init_route_list(struct route_list *rl, const char *remote_endpoint, int default_metric, in_addr_t remote_host, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); bool ret = true; @@ -634,7 +631,7 @@ init_route_list(struct route_list *rl, rl->spec.flags |= RTSA_DEFAULT_METRIC; } - get_default_gateway(&rl->rgi); + get_default_gateway(&rl->rgi, ctx); if (rl->rgi.flags & RGI_ADDR_DEFINED) { setenv_route_addr(es, "net_gateway", rl->rgi.gateway.addr, -1); @@ -768,7 +765,8 @@ init_route_ipv6_list(struct route_ipv6_list *rl6, const char *remote_endpoint, int default_metric, const struct in6_addr *remote_host_ipv6, - struct env_set *es) + struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); bool ret = true; @@ -793,7 +791,7 @@ init_route_ipv6_list(struct route_ipv6_list *rl6, msg(D_ROUTE, "GDG6: remote_host_ipv6=%s", remote_host_ipv6 ? print_in6_addr(*remote_host_ipv6, 0, &gc) : "n/a" ); - get_default_gateway_ipv6(&rl6->rgi6, remote_host_ipv6); + get_default_gateway_ipv6(&rl6->rgi6, remote_host_ipv6, ctx); if (rl6->rgi6.flags & RGI_ADDR_DEFINED) { setenv_str(es, "net_gateway_ipv6", print_in6_addr(rl6->rgi6.gateway.addr_ipv6, 0, &gc)); @@ -901,7 +899,8 @@ add_route3(in_addr_t network, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct route_ipv4 r; CLEAR(r); @@ -909,7 +908,7 @@ add_route3(in_addr_t network, r.network = network; r.netmask = netmask; r.gateway = gateway; - add_route(&r, tt, flags, rgi, es); + add_route(&r, tt, flags, rgi, es, ctx); } static void @@ -919,7 +918,8 @@ del_route3(in_addr_t network, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct route_ipv4 r; CLEAR(r); @@ -927,7 +927,7 @@ del_route3(in_addr_t network, r.network = network; r.netmask = netmask; r.gateway = gateway; - delete_route(&r, tt, flags, rgi, es); + delete_route(&r, tt, flags, rgi, es, ctx); } static void @@ -936,7 +936,8 @@ add_bypass_routes(struct route_bypass *rb, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { int i; for (i = 0; i < rb->n_bypass; ++i) @@ -949,7 +950,8 @@ add_bypass_routes(struct route_bypass *rb, tt, flags | ROUTE_REF_GW, rgi, - es); + es, + ctx); } } } @@ -960,7 +962,8 @@ del_bypass_routes(struct route_bypass *rb, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { int i; for (i = 0; i < rb->n_bypass; ++i) @@ -973,15 +976,18 @@ del_bypass_routes(struct route_bypass *rb, tt, flags | ROUTE_REF_GW, rgi, - es); + es, + ctx); } } } static void -redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { - const char err[] = "NOTE: unable to redirect default gateway --"; + const char err[] = "NOTE: unable to redirect IPv4 default gateway --"; if (rl && rl->flags & RG_ENABLE) { @@ -1035,7 +1041,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags | ROUTE_REF_GW, &rl->rgi, - es); + es, + ctx); rl->iflags |= RL_DID_LOCAL; } else @@ -1046,7 +1053,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un #endif /* ifndef TARGET_ANDROID */ /* route DHCP/DNS server traffic through original default gateway */ - add_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es); + add_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, + &rl->rgi, es, ctx); if (rl->flags & RG_REROUTE_GW) { @@ -1059,7 +1067,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); /* add new default route (2nd component) */ add_route3(0x80000000, @@ -1068,7 +1077,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); } else { @@ -1077,7 +1087,7 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un { /* delete default route */ del_route3(0, 0, rl->rgi.gateway.addr, tt, - flags | ROUTE_REF_GW, &rl->rgi, es); + flags | ROUTE_REF_GW, &rl->rgi, es, ctx); } /* add new default route */ @@ -1087,7 +1097,8 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un tt, flags, &rl->rgi, - es); + es, + ctx); } } @@ -1098,7 +1109,10 @@ redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, un } static void -undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +undo_redirect_default_route_to_vpn(struct route_list *rl, + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, + openvpn_net_ctx_t *ctx) { if (rl && rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY) { @@ -1111,12 +1125,14 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags | ROUTE_REF_GW, &rl->rgi, - es); + es, + ctx); rl->iflags &= ~RL_DID_LOCAL; } /* delete special DHCP/DNS bypass route */ - del_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es); + del_bypass_routes(&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, + &rl->rgi, es, ctx); if (rl->flags & RG_REROUTE_GW) { @@ -1129,7 +1145,8 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); /* delete default route (2nd component) */ del_route3(0x80000000, @@ -1138,7 +1155,8 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); } else { @@ -1149,12 +1167,13 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t tt, flags, &rl->rgi, - es); + es, + ctx); /* restore original default route if there was any */ if (rl->rgi.flags & RGI_ADDR_DEFINED) { add_route3(0, 0, rl->rgi.gateway.addr, tt, - flags | ROUTE_REF_GW, &rl->rgi, es); + flags | ROUTE_REF_GW, &rl->rgi, es, ctx); } } } @@ -1164,13 +1183,23 @@ undo_redirect_default_route_to_vpn(struct route_list *rl, const struct tuntap *t } void -add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +add_routes(struct route_list *rl, struct route_ipv6_list *rl6, + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, openvpn_net_ctx_t *ctx) { - redirect_default_route_to_vpn(rl, tt, flags, es); + redirect_default_route_to_vpn(rl, tt, flags, es, ctx); if (rl && !(rl->iflags & RL_ROUTES_ADDED) ) { struct route_ipv4 *r; + if (rl->routes && !tt->did_ifconfig_setup) + { + msg(M_INFO, "WARNING: OpenVPN was configured to add an IPv4 " + "route. However, no IPv4 has been configured for %s, " + "therefore the route installation may fail or may not work " + "as expected.", tt->actual_name); + } + #ifdef ENABLE_MANAGEMENT if (management && rl->routes) { @@ -1189,9 +1218,9 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt check_subnet_conflict(r->network, r->netmask, "route"); if (flags & ROUTE_DELETE_FIRST) { - delete_route(r, tt, flags, &rl->rgi, es); + delete_route(r, tt, flags, &rl->rgi, es, ctx); } - add_route(r, tt, flags, &rl->rgi, es); + add_route(r, tt, flags, &rl->rgi, es, ctx); } rl->iflags |= RL_ROUTES_ADDED; } @@ -1202,18 +1231,18 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt if (!tt->did_ifconfig_ipv6_setup) { msg(M_INFO, "WARNING: OpenVPN was configured to add an IPv6 " - "route over %s. However, no IPv6 has been configured for " - "this interface, therefore the route installation may " - "fail or may not work as expected.", tt->actual_name); + "route. However, no IPv6 has been configured for %s, " + "therefore the route installation may fail or may not work " + "as expected.", tt->actual_name); } for (r = rl6->routes_ipv6; r; r = r->next) { if (flags & ROUTE_DELETE_FIRST) { - delete_route_ipv6(r, tt, flags, es); + delete_route_ipv6(r, tt, flags, es, ctx); } - add_route_ipv6(r, tt, flags, es); + add_route_ipv6(r, tt, flags, es, ctx); } rl6->iflags |= RL_ROUTES_ADDED; } @@ -1221,19 +1250,20 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6, const struct tunt void delete_routes(struct route_list *rl, struct route_ipv6_list *rl6, - const struct tuntap *tt, unsigned int flags, const struct env_set *es) + const struct tuntap *tt, unsigned int flags, + const struct env_set *es, openvpn_net_ctx_t *ctx) { if (rl && rl->iflags & RL_ROUTES_ADDED) { struct route_ipv4 *r; for (r = rl->routes; r; r = r->next) { - delete_route(r, tt, flags, &rl->rgi, es); + delete_route(r, tt, flags, &rl->rgi, es, ctx); } rl->iflags &= ~RL_ROUTES_ADDED; } - undo_redirect_default_route_to_vpn(rl, tt, flags, es); + undo_redirect_default_route_to_vpn(rl, tt, flags, es, ctx); if (rl) { @@ -1245,7 +1275,7 @@ delete_routes(struct route_list *rl, struct route_ipv6_list *rl6, struct route_ipv6 *r6; for (r6 = rl6->routes_ipv6; r6; r6 = r6->next) { - delete_route_ipv6(r6, tt, flags, es); + delete_route_ipv6(r6, tt, flags, es, ctx); } rl6->iflags &= ~RL_ROUTES_ADDED; } @@ -1322,7 +1352,7 @@ print_default_gateway(const int msglevel, #ifdef _WIN32 if (rgi->flags & RGI_IFACE_DEFINED) { - buf_printf(&out, " I=%u", (unsigned int)rgi->adapter_index); + buf_printf(&out, " I=%lu", rgi->adapter_index); } #else if (rgi->flags & RGI_IFACE_DEFINED) @@ -1353,7 +1383,7 @@ print_default_gateway(const int msglevel, #ifdef _WIN32 if (rgi6->flags & RGI_IFACE_DEFINED) { - buf_printf(&out, " I=%u", (unsigned int)rgi6->adapter_index); + buf_printf(&out, " I=%lu", rgi6->adapter_index); } #else if (rgi6->flags & RGI_IFACE_DEFINED) @@ -1525,15 +1555,18 @@ add_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, /* may be NULL */ - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); +#if !defined(TARGET_LINUX) const char *network; -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) const char *netmask; #endif const char *gateway; +#endif bool status = false; int is_local_route; @@ -1544,11 +1577,13 @@ add_route(struct route_ipv4 *r, gc_init(&gc); +#if !defined(TARGET_LINUX) network = print_in_addr_t(r->network, 0, &gc); -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) netmask = print_in_addr_t(r->netmask, 0, &gc); #endif gateway = print_in_addr_t(r->gateway, 0, &gc); +#endif is_local_route = local_route(r->network, r->netmask, r->gateway, rgi); if (is_local_route == LR_ERROR) @@ -1557,64 +1592,44 @@ add_route(struct route_ipv4 *r, } #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s route add %s/%d", - iproute_path, - network, - netmask_to_netbits2(r->netmask)); - - if (r->flags & RT_METRIC_DEFINED) - { - argv_printf_cat(&argv, "metric %d", r->metric); - } + const char *iface = NULL; + int metric = -1; if (is_on_link(is_local_route, flags, rgi)) { - argv_printf_cat(&argv, "dev %s", rgi->iface); - } - else - { - argv_printf_cat(&argv, "via %s", gateway); + iface = rgi->iface; } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s add -net %s netmask %s", - ROUTE_PATH, - network, - netmask); + if (r->flags & RT_METRIC_DEFINED) { - argv_printf_cat(&argv, "metric %d", r->metric); + metric = r->metric; } - if (is_on_link(is_local_route, flags, rgi)) - { - argv_printf_cat(&argv, "dev %s", rgi->iface); - } - else + + status = true; + if (net_route_v4_add(ctx, &r->network, netmask_to_netbits2(r->netmask), + &r->gateway, iface, 0, metric) < 0) { - argv_printf_cat(&argv, "gw %s", gateway); + msg(M_WARN, "ERROR: Linux route add command failed"); + status = false; } -#endif /*ENABLE_IPROUTE*/ - argv_msg(D_ROUTE, &argv); - status = openvpn_execve_check(&argv, es, 0, "ERROR: Linux route add command failed"); - #elif defined (TARGET_ANDROID) - struct buffer out = alloc_buf_gc(128, &gc); + char out[128]; if (rgi) { - buf_printf(&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); + openvpn_snprintf(out, sizeof(out), "%s %s %s dev %s", network, netmask, gateway, rgi->iface); } else { - buf_printf(&out, "%s %s %s", network, netmask, gateway); + openvpn_snprintf(out, sizeof(out), "%s %s %s", network, netmask, gateway); } - management_android_control(management, "ROUTE", buf_bptr(&out)); + management_android_control(management, "ROUTE", out); #elif defined (_WIN32) { DWORD ai = TUN_ADAPTER_INDEX_INVALID; - argv_printf(&argv, "%s%sc ADD %s MASK %s %s", + argv_printf(&argv, "%s%s ADD %s MASK %s %s", get_win_sys_path(), WIN_ROUTE_PATH_SUFFIX, network, @@ -1627,7 +1642,7 @@ add_route(struct route_ipv4 *r, if (is_on_link(is_local_route, flags, rgi)) { ai = rgi->adapter_index; - argv_printf_cat(&argv, "IF %u", (unsigned int)ai); + argv_printf_cat(&argv, "IF %lu", ai); } argv_msg(D_ROUTE, &argv); @@ -1815,8 +1830,10 @@ done: { r->flags &= ~RT_ADDED; } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route setup */ + net_ctx_reset(ctx); } @@ -1825,7 +1842,7 @@ route_ipv6_clear_host_bits( struct route_ipv6 *r6 ) { /* clear host bit parts of route * (needed if routes are specified improperly, or if we need to - * explicitely setup/clear the "connected" network routes on some OSes) + * explicitly setup/clear the "connected" network routes on some OSes) */ int byte = 15; int bits_to_clear = 128 - r6->netbits; @@ -1844,7 +1861,9 @@ route_ipv6_clear_host_bits( struct route_ipv6 *r6 ) } void -add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); @@ -1853,7 +1872,9 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag const char *gateway; bool status = false; const char *device = tt->actual_name; - +#if defined(TARGET_LINUX) + int metric; +#endif bool gateway_needed = false; if (!(r6->flags & RT_DEFINED) ) @@ -1917,46 +1938,38 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag gateway_needed = true; } -#if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 route add %s/%d dev %s", - iproute_path, - network, - r6->netbits, - device); - if (gateway_needed) - { - argv_printf_cat(&argv, "via %s", gateway); - } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + if (gateway_needed && IN6_IS_ADDR_UNSPECIFIED(&r6->gateway)) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ROUTE6 WARNING: " PACKAGE_NAME " needs a gateway " + "parameter for a --route-ipv6 option and no default was set via " + "--ifconfig-ipv6 or --route-ipv6-gateway option. Not installing " + "IPv6 route to %s/%d.", network, r6->netbits); + status = false; + goto done; } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s -A inet6 add %s/%d dev %s", - ROUTE_PATH, - network, - r6->netbits, - device); - if (gateway_needed) +#if defined(TARGET_LINUX) + metric = -1; + if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0)) { - argv_printf_cat(&argv, "gw %s", gateway); + metric = r6->metric; } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + + status = true; + if (net_route_v6_add(ctx, &r6->network, r6->netbits, + gateway_needed ? &r6->gateway : NULL, device, 0, + metric) < 0) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ERROR: Linux IPv6 route can't be added"); + status = false; } -#endif /*ENABLE_IPROUTE*/ - argv_msg(D_ROUTE, &argv); - status = openvpn_execve_check(&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed"); #elif defined (TARGET_ANDROID) - struct buffer out = alloc_buf_gc(64, &gc); + char out[64]; - buf_printf(&out, "%s/%d %s", network, r6->netbits, device); + openvpn_snprintf(out, sizeof(out), "%s/%d %s", network, r6->netbits, device); - management_android_control(management, "ROUTE6", buf_bptr(&out)); + management_android_control(management, "ROUTE6", out); #elif defined (_WIN32) @@ -1979,7 +1992,7 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag device = buf_bptr(&out); /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */ - argv_printf(&argv, "%s%sc interface ipv6 add route %s/%d %s", + argv_printf(&argv, "%s%s interface ipv6 add route %s/%d %s", get_win_sys_path(), NETSH_PATH_SUFFIX, network, @@ -2114,6 +2127,7 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag msg(M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-up script"); #endif /* if defined(TARGET_LINUX) */ +done: if (status) { r6->flags |= RT_ADDED; @@ -2122,8 +2136,10 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag { r6->flags &= ~RT_ADDED; } - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route setup */ + net_ctx_reset(ctx); } static void @@ -2131,17 +2147,22 @@ delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, - const struct env_set *es) + const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); +#if !defined(TARGET_LINUX) const char *network; -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) const char *netmask; #endif -#if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if !defined(TARGET_ANDROID) const char *gateway; #endif +#else /* if !defined(TARGET_LINUX) */ + int metric; +#endif int is_local_route; if ((r->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED)) @@ -2151,13 +2172,15 @@ delete_route(struct route_ipv4 *r, gc_init(&gc); +#if !defined(TARGET_LINUX) network = print_in_addr_t(r->network, 0, &gc); -#if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX) +#if !defined(TARGET_AIX) netmask = print_in_addr_t(r->netmask, 0, &gc); #endif -#if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if !defined(TARGET_ANDROID) gateway = print_in_addr_t(r->gateway, 0, &gc); #endif +#endif is_local_route = local_route(r->network, r->netmask, r->gateway, rgi); if (is_local_route == LR_ERROR) @@ -2166,27 +2189,20 @@ delete_route(struct route_ipv4 *r, } #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s route del %s/%d", - iproute_path, - network, - netmask_to_netbits2(r->netmask)); -#else - argv_printf(&argv, "%s del -net %s netmask %s", - ROUTE_PATH, - network, - netmask); -#endif /*ENABLE_IPROUTE*/ + metric = -1; if (r->flags & RT_METRIC_DEFINED) { - argv_printf_cat(&argv, "metric %d", r->metric); + metric = r->metric; } - argv_msg(D_ROUTE, &argv); - openvpn_execve_check(&argv, es, 0, "ERROR: Linux route delete command failed"); + if (net_route_v4_del(ctx, &r->network, netmask_to_netbits2(r->netmask), + &r->gateway, NULL, 0, metric) < 0) + { + msg(M_WARN, "ERROR: Linux route delete command failed"); + } #elif defined (_WIN32) - argv_printf(&argv, "%s%sc DELETE %s MASK %s %s", + argv_printf(&argv, "%s%s DELETE %s MASK %s %s", get_win_sys_path(), WIN_ROUTE_PATH_SUFFIX, network, @@ -2314,17 +2330,25 @@ delete_route(struct route_ipv4 *r, done: r->flags &= ~RT_ADDED; - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route cleanup */ + net_ctx_reset(ctx); } void -delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es) +delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, + unsigned int flags, const struct env_set *es, + openvpn_net_ctx_t *ctx) { struct gc_arena gc; struct argv argv = argv_new(); const char *network; +#if !defined(TARGET_LINUX) const char *gateway; +#else + int metric; +#endif const char *device = tt->actual_name; bool gateway_needed = false; @@ -2344,7 +2368,9 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned gc_init(&gc); network = print_in6_addr( r6->network, 0, &gc); +#if !defined(TARGET_LINUX) gateway = print_in6_addr( r6->gateway, 0, &gc); +#endif #if defined(TARGET_DARWIN) \ || defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY) \ @@ -2375,35 +2401,19 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned gateway_needed = true; } - #if defined(TARGET_LINUX) -#ifdef ENABLE_IPROUTE - argv_printf(&argv, "%s -6 route del %s/%d dev %s", - iproute_path, - network, - r6->netbits, - device); - if (gateway_needed) - { - argv_printf_cat(&argv, "via %s", gateway); - } -#else /* ifdef ENABLE_IPROUTE */ - argv_printf(&argv, "%s -A inet6 del %s/%d dev %s", - ROUTE_PATH, - network, - r6->netbits, - device); - if (gateway_needed) + metric = -1; + if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0)) { - argv_printf_cat(&argv, "gw %s", gateway); + metric = r6->metric; } - if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0) + + if (net_route_v6_del(ctx, &r6->network, r6->netbits, + gateway_needed ? &r6->gateway : NULL, device, 0, + metric) < 0) { - argv_printf_cat(&argv, " metric %d", r6->metric); + msg(M_WARN, "ERROR: Linux route v6 delete command failed"); } -#endif /*ENABLE_IPROUTE*/ - argv_msg(D_ROUTE, &argv); - openvpn_execve_check(&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed"); #elif defined (_WIN32) @@ -2426,7 +2436,7 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned device = buf_bptr(&out); /* netsh interface ipv6 delete route 2001:db8::/32 MyTunDevice */ - argv_printf(&argv, "%s%sc interface ipv6 delete route %s/%d %s", + argv_printf(&argv, "%s%s interface ipv6 delete route %s/%d %s", get_win_sys_path(), NETSH_PATH_SUFFIX, network, @@ -2548,8 +2558,10 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned msg(M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system. Try putting your routes in a --route-down script"); #endif /* if defined(TARGET_LINUX) */ - argv_reset(&argv); + argv_free(&argv); gc_free(&gc); + /* release resources potentially allocated during route cleanup */ + net_ctx_reset(ctx); } /* @@ -2642,7 +2654,11 @@ test_routes(const struct route_list *rl, const struct tuntap *tt) ret = true; adapter_up = true; - if (rl) + /* we do this test only if we have IPv4 routes to install, and if + * the tun/tap interface has seen IPv4 ifconfig - because if we + * have no IPv4, the check will always fail, failing tun init + */ + if (rl && tt->did_ifconfig_setup) { struct route_ipv4 *r; for (r = rl->routes, len = 0; r; r = r->next, ++len) @@ -2711,7 +2727,7 @@ get_default_gateway_row(const MIB_IPFORWARDTABLE *routes) } void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); @@ -2798,7 +2814,7 @@ windows_route_find_if_index(const struct route_ipv4 *r, const struct tuntap *tt) */ void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); MIB_IPFORWARD_ROW2 BestRoute; @@ -2817,7 +2833,7 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, DestinationAddress.Ipv6.sin6_addr = *dest; } - status = GetBestInterfaceEx( &DestinationAddress, &BestIfIndex ); + status = GetBestInterfaceEx( (struct sockaddr *)&DestinationAddress, &BestIfIndex ); if (status != NO_ERROR) { @@ -2987,16 +3003,12 @@ del_route_ipapi(const struct route_ipv4 *r, const struct tuntap *tt) static bool do_route_service(const bool add, const route_message_t *rt, const size_t size, HANDLE pipe) { - DWORD len; bool ret = false; ack_message_t ack; struct gc_arena gc = gc_new(); - if (!WriteFile(pipe, rt, size, &len, NULL) - || !ReadFile(pipe, &ack, sizeof(ack), &len, NULL)) + if (!send_msg_iservice(pipe, rt, size, &ack, "ROUTE")) { - msg(M_WARN, "ROUTE: could not talk to service: %s [%lu]", - strerror_win32(GetLastError(), &gc), GetLastError()); goto out; } @@ -3074,7 +3086,7 @@ do_route_ipv6_service(const bool add, const struct route_ipv6 *r, const struct t * (only do this for routes actually using the tun/tap device) */ if (tt->type == DEV_TYPE_TUN - && msg.iface.index == tt->adapter_index ) + && msg.iface.index == tt->adapter_index) { inet_pton(AF_INET6, "fe80::8", &msg.gateway.ipv6); } @@ -3160,90 +3172,68 @@ show_routes(int msglev) gc_free(&gc); } -#elif defined(TARGET_LINUX) || defined(TARGET_ANDROID) +#elif defined(TARGET_ANDROID) void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) +{ + /* Android, set some pseudo GW, addr is in host byte order, + * Determining the default GW on Android 5.0+ is non trivial + * and serves almost no purpose since OpenVPN only uses the + * default GW address to add routes for networks that should + * NOT be routed over the VPN. Using a well known address + * (127.'d'.'g'.'w') for the default GW make detecting + * these routes easier from the controlling app. + */ + CLEAR(*rgi); + + rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w'; + rgi->flags = RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + strcpy(rgi->iface, "android-gw"); + + /* Skip scanning/fetching interface from loopback interface we do + * normally on Linux. + * It always fails and "ioctl(SIOCGIFCONF) failed" confuses users + */ + +} + +void +get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) +{ + /* Same for ipv6 */ + + CLEAR(*rgi6); + + /* Use a fake link-local address */ + ASSERT(inet_pton(AF_INET6, "fe80::ad", &rgi6->addrs->addr_ipv6) == 1); + rgi6->addrs->netbits_ipv6 = 64; + rgi6->flags = RGI_ADDR_DEFINED | RGI_IFACE_DEFINED; + strcpy(rgi6->iface, "android-gw"); +} + +#elif defined(TARGET_LINUX) + +void +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); int sd = -1; - char best_name[16]; - best_name[0] = 0; + char best_name[IFNAMSIZ]; CLEAR(*rgi); + CLEAR(best_name); -#ifndef TARGET_ANDROID /* get default gateway IP addr */ + if (net_route_v4_best_gw(ctx, NULL, &rgi->gateway.addr, best_name) == 0) { - FILE *fp = fopen("/proc/net/route", "r"); - if (fp) + rgi->flags |= RGI_ADDR_DEFINED; + if (!rgi->gateway.addr && best_name[0]) { - char line[256]; - int count = 0; - unsigned int lowest_metric = UINT_MAX; - in_addr_t best_gw = 0; - bool found = false; - while (fgets(line, sizeof(line), fp) != NULL) - { - if (count) - { - unsigned int net_x = 0; - unsigned int mask_x = 0; - unsigned int gw_x = 0; - unsigned int metric = 0; - unsigned int flags = 0; - char name[16]; - name[0] = 0; - const int np = sscanf(line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x", - name, - &net_x, - &gw_x, - &flags, - &metric, - &mask_x); - if (np == 6 && (flags & IFF_UP)) - { - const in_addr_t net = ntohl(net_x); - const in_addr_t mask = ntohl(mask_x); - const in_addr_t gw = ntohl(gw_x); - - if (!net && !mask && metric < lowest_metric) - { - found = true; - best_gw = gw; - strcpy(best_name, name); - lowest_metric = metric; - } - } - } - ++count; - } - fclose(fp); - - if (found) - { - rgi->gateway.addr = best_gw; - rgi->flags |= RGI_ADDR_DEFINED; - if (!rgi->gateway.addr && best_name[0]) - { - rgi->flags |= RGI_ON_LINK; - } - } + rgi->flags |= RGI_ON_LINK; } } -#else /* ifndef TARGET_ANDROID */ - /* Android, set some pseudo GW, addr is in host byte order, - * Determining the default GW on Android 5.0+ is non trivial - * and serves almost no purpose since OpenVPN only uses the - * default GW address to add routes for networks that should - * NOT be routed over the VPN. Using a well known address - * (127.'d'.'g'.'w') for the default GW make detecting - * these routes easier from the controlling app. - */ - rgi->gateway.addr = 127 << 24 | 'd' << 16 | 'g' << 8 | 'w'; - rgi->flags |= RGI_ADDR_DEFINED; - strcpy(best_name, "android-gw"); -#endif /* ifndef TARGET_ANDROID */ /* scan adapter list */ if (rgi->flags & RGI_ADDR_DEFINED) @@ -3292,7 +3282,7 @@ get_default_gateway(struct route_gateway_info *rgi) if (rgi->flags & RGI_ON_LINK) { /* check that interface name of current interface - * matches interface name of best default route */ + * matches interface name of best default route */ if (strcmp(ifreq.ifr_name, best_name)) { continue; @@ -3369,152 +3359,29 @@ struct rtreq { void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { - int nls = -1; - struct rtreq rtreq; - struct rtattr *rta; - - char rtbuf[2000]; - ssize_t ssize; + int flags; CLEAR(*rgi6); - nls = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE ); - if (nls < 0) - { - msg(M_WARN|M_ERRNO, "GDG6: socket() failed" ); goto done; - } - - /* bind() is not needed, no unsolicited msgs coming in */ - - /* request best matching route, see netlink(7) for explanations - */ - CLEAR(rtreq); - rtreq.nh.nlmsg_type = RTM_GETROUTE; - rtreq.nh.nlmsg_flags = NLM_F_REQUEST; /* best match only */ - rtreq.rtm.rtm_family = AF_INET6; - rtreq.rtm.rtm_src_len = 0; /* not source dependent */ - rtreq.rtm.rtm_dst_len = 128; /* exact dst */ - rtreq.rtm.rtm_table = RT_TABLE_MAIN; - rtreq.rtm.rtm_protocol = RTPROT_UNSPEC; - rtreq.nh.nlmsg_len = NLMSG_SPACE(sizeof(rtreq.rtm)); - - /* set RTA_DST for target IPv6 address we want */ - rta = (struct rtattr *)(((char *) &rtreq)+NLMSG_ALIGN(rtreq.nh.nlmsg_len)); - rta->rta_type = RTA_DST; - rta->rta_len = RTA_LENGTH(16); - rtreq.nh.nlmsg_len = NLMSG_ALIGN(rtreq.nh.nlmsg_len) - +RTA_LENGTH(16); - - if (dest == NULL) /* ::, unspecified */ - { - memset( RTA_DATA(rta), 0, 16 ); /* :: = all-zero */ - } - else - { - memcpy( RTA_DATA(rta), (void *)dest, 16 ); - } - - /* send and receive reply */ - if (send( nls, &rtreq, rtreq.nh.nlmsg_len, 0 ) < 0) - { - msg(M_WARN|M_ERRNO, "GDG6: send() failed" ); goto done; - } - - ssize = recv(nls, rtbuf, sizeof(rtbuf), MSG_TRUNC); - - if (ssize < 0) - { - msg(M_WARN|M_ERRNO, "GDG6: recv() failed" ); goto done; - } - - if (ssize > sizeof(rtbuf)) + if (net_route_v6_best_gw(ctx, dest, &rgi6->gateway.addr_ipv6, + rgi6->iface) == 0) { - msg(M_WARN, "get_default_gateway_ipv6: returned message too big for buffer (%d>%d)", (int)ssize, (int)sizeof(rtbuf) ); - goto done; - } - - struct nlmsghdr *nh; - - for (nh = (struct nlmsghdr *)rtbuf; - NLMSG_OK(nh, ssize); - nh = NLMSG_NEXT(nh, ssize)) - { - struct rtmsg *rtm; - int attrlen; - - if (nh->nlmsg_type == NLMSG_DONE) - { - break; - } - - if (nh->nlmsg_type == NLMSG_ERROR) - { - struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh); - - /* since linux-4.11 -ENETUNREACH is returned when no route can be - * found. Don't print any error message in this case */ - if (ne->error != -ENETUNREACH) - { - msg(M_WARN, "GDG6: NLMSG_ERROR: error %s\n", - strerror(-ne->error)); - } - break; - } - - if (nh->nlmsg_type != RTM_NEWROUTE) + if (!IN6_IS_ADDR_UNSPECIFIED(rgi6->gateway.addr_ipv6.s6_addr)) { - /* shouldn't happen */ - msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type ); - continue; + rgi6->flags |= RGI_ADDR_DEFINED; } - rtm = (struct rtmsg *)NLMSG_DATA(nh); - attrlen = RTM_PAYLOAD(nh); - - /* we're only looking for routes in the main table, as "we have - * no IPv6" will lead to a lookup result in "Local" (::/0 reject) - */ - if (rtm->rtm_family != AF_INET6 - || rtm->rtm_table != RT_TABLE_MAIN) + if (strlen(rgi6->iface) > 0) { - continue; - } /* we're not interested */ - - for (rta = RTM_RTA(rtm); - RTA_OK(rta, attrlen); - rta = RTA_NEXT(rta, attrlen)) - { - if (rta->rta_type == RTA_GATEWAY) - { - if (RTA_PAYLOAD(rta) != sizeof(struct in6_addr) ) - { - msg(M_WARN, "GDG6: RTA_GW size mismatch"); continue; - } - rgi6->gateway.addr_ipv6 = *(struct in6_addr *) RTA_DATA(rta); - rgi6->flags |= RGI_ADDR_DEFINED; - } - else if (rta->rta_type == RTA_OIF) - { - char ifname[IF_NAMESIZE+1]; - int oif; - if (RTA_PAYLOAD(rta) != sizeof(oif) ) - { - msg(M_WARN, "GDG6: oif size mismatch"); continue; - } - - memcpy(&oif, RTA_DATA(rta), sizeof(oif)); - if_indextoname(oif,ifname); - strncpy( rgi6->iface, ifname, sizeof(rgi6->iface)-1 ); - rgi6->flags |= RGI_IFACE_DEFINED; - } + rgi6->flags |= RGI_IFACE_DEFINED; } } /* if we have an interface but no gateway, the destination is on-link */ - if ( ( rgi6->flags & (RGI_IFACE_DEFINED|RGI_ADDR_DEFINED) ) == - RGI_IFACE_DEFINED) + flags = rgi6->flags & (RGI_IFACE_DEFINED | RGI_ADDR_DEFINED); + if (flags == RGI_IFACE_DEFINED) { rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK); if (dest) @@ -3522,12 +3389,6 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, rgi6->gateway.addr_ipv6 = *dest; } } - -done: - if (nls >= 0) - { - close(nls); - } } #elif defined(TARGET_DARWIN) || defined(TARGET_SOLARIS) \ @@ -3575,7 +3436,7 @@ struct rtmsg { #else /* if defined(TARGET_SOLARIS) */ #define NEXTADDR(w, u) \ if (rtm_addrs & (w)) { \ - l = ROUNDUP( ((struct sockaddr *)&(u))->sa_len); memmove(cp, &(u), l); cp += l; \ + l = ((struct sockaddr *)&(u))->sa_len; memmove(cp, &(u), l); cp += ROUNDUP(l); \ } #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) @@ -3584,7 +3445,7 @@ struct rtmsg { #define max(a,b) ((a) > (b) ? (a) : (b)) void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { struct gc_arena gc = gc_new(); struct rtmsg m_rtmsg; @@ -3804,7 +3665,7 @@ done: void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { struct rtmsg m_rtmsg; @@ -3984,13 +3845,13 @@ done: * may be disabled by missing items. */ void -get_default_gateway(struct route_gateway_info *rgi) +get_default_gateway(struct route_gateway_info *rgi, openvpn_net_ctx_t *ctx) { CLEAR(*rgi); } void get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6, - const struct in6_addr *dest) + const struct in6_addr *dest, openvpn_net_ctx_t *ctx) { msg(D_ROUTE, "no support for get_default_gateway_ipv6() on this system"); CLEAR(*rgi6); |