📄 route.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-2004 James Yonan <jim@yonan.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 adding/deleting network routes. */#ifdef WIN32#include "config-win32.h"#else#include "config.h"#endif#include "syshead.h"#include "common.h"#include "error.h"#include "route.h"#include "misc.h"#include "socket.h"#include "tun.h"#include "memdbg.h"static void add_route (struct route *r);static void delete_route (const struct route *r);static bool get_default_gateway (in_addr_t *ret);struct route_option_list *new_route_option_list (struct gc_arena *a){ struct route_option_list *ret; ALLOC_OBJ_CLEAR_GC (ret, struct route_option_list, a); return ret;}struct route_list *new_route_list (struct gc_arena *a){ struct route_list *ret; ALLOC_OBJ_CLEAR_GC (ret, struct route_list, a); return ret;}static const char *route_string (const struct route *r, struct gc_arena *gc){ struct buffer out = alloc_buf_gc (256, gc); buf_printf (&out, "ROUTE network %s netmask %s gateway %s", print_in_addr_t (r->network, false, gc), print_in_addr_t (r->netmask, false, gc), print_in_addr_t (r->gateway, false, gc) ); if (r->metric_defined) buf_printf (&out, " metric %d", r->metric); return BSTR (&out);}static boolis_route_parm_defined (const char *parm){ if (!parm) return false; if (!strcmp (parm, "default")) return false; return true;}static voidsetenv_route_addr (const char *key, const in_addr_t addr, int i){ struct gc_arena gc = gc_new (); char name[128]; if (i >= 0) openvpn_snprintf (name, sizeof (name), "route_%s_%d", key, i); else openvpn_snprintf (name, sizeof (name), "route_%s", key); setenv_str (name, print_in_addr_t (addr, false, &gc)); gc_free (&gc);}static boolget_special_addr (const struct route_special_addr *spec, const char *string, in_addr_t *out, bool *status){ *status = true; if (!strcmp (string, "vpn_gateway")) { if (spec->remote_endpoint_defined) *out = spec->remote_endpoint; else { msg (M_INFO, PACKAGE_NAME " ROUTE: vpn_gateway undefined"); *status = false; } return true; } else if (!strcmp (string, "net_gateway")) { if (spec->net_gateway_defined) *out = spec->net_gateway; else { msg (M_INFO, PACKAGE_NAME " ROUTE: net_gateway undefined -- unable to get default gateway from system"); *status = false; } return true; } else if (!strcmp (string, "remote_host")) { if (spec->remote_host_defined) *out = spec->remote_host; else { msg (M_INFO, PACKAGE_NAME " ROUTE: remote_host undefined"); *status = false; } return true; } return false;}static boolinit_route (struct route *r, const struct route_option *ro, const struct route_special_addr *spec){ const in_addr_t default_netmask = ~0; bool status; r->option = ro; r->defined = false; /* network */ if (!is_route_parm_defined (ro->network)) { goto fail; } if (!get_special_addr (spec, ro->network, &r->network, &status)) { r->network = getaddr ( GETADDR_RESOLVE | GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL, ro->network, 0, &status, NULL); } if (!status) goto fail; /* netmask */ if (is_route_parm_defined (ro->netmask)) { r->netmask = getaddr ( GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL, ro->netmask, 0, &status, NULL); if (!status) goto fail; } else r->netmask = default_netmask; /* gateway */ if (is_route_parm_defined (ro->gateway)) { if (!get_special_addr (spec, ro->gateway, &r->gateway, &status)) { r->gateway = getaddr ( GETADDR_RESOLVE | GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL, ro->gateway, 0, &status, NULL); } if (!status) goto fail; } else { if (spec->remote_endpoint_defined) r->gateway = spec->remote_endpoint; else { msg (M_WARN, PACKAGE_NAME " ROUTE: " PACKAGE_NAME " needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options"); goto fail; } } /* metric */ r->metric_defined = false; r->metric = 0; if (is_route_parm_defined (ro->metric)) { r->metric = atoi (ro->metric); if (r->metric < 0) { msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0", ro->network, ro->metric); goto fail; } r->metric_defined = true; } else { r->metric = 0; r->metric_defined = false; } r->defined = true; return true; fail: msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s", ro->network); r->defined = false; return false;}voidadd_route_to_option_list (struct route_option_list *l, const char *network, const char *netmask, const char *gateway, const char *metric){ struct route_option *ro; if (l->n >= MAX_ROUTES) msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d routes", MAX_ROUTES); ro = &l->routes[l->n]; ro->network = network; ro->netmask = netmask; ro->gateway = gateway; ro->metric = metric; ++l->n;}voidclear_route_list (struct route_list *rl){ CLEAR (*rl);}boolinit_route_list (struct route_list *rl, const struct route_option_list *opt, const char *remote_endpoint, in_addr_t remote_host){ int i; bool ret = true; clear_route_list (rl); if (remote_host) { rl->spec.remote_host = remote_host; rl->spec.remote_host_defined = true; } rl->spec.net_gateway_defined = get_default_gateway (&rl->spec.net_gateway); if (rl->spec.net_gateway_defined) { setenv_route_addr ("net_gateway", rl->spec.net_gateway, -1); } rl->redirect_default_gateway = opt->redirect_default_gateway; rl->redirect_local = opt->redirect_local; if (is_route_parm_defined (remote_endpoint)) { rl->spec.remote_endpoint = getaddr ( GETADDR_RESOLVE | GETADDR_HOST_ORDER | GETADDR_FATAL_ON_SIGNAL, remote_endpoint, 0, &rl->spec.remote_endpoint_defined, NULL); if (rl->spec.remote_endpoint_defined) { setenv_route_addr ("vpn_gateway", rl->spec.remote_endpoint, -1); } else { msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s", remote_endpoint); ret = false; } } else rl->spec.remote_endpoint_defined = false; ASSERT (opt->n >= 0 && opt->n < MAX_ROUTES); for (i = 0; i < opt->n; ++i) { if (!init_route (&rl->routes[i], &opt->routes[i], &rl->spec)) ret = false; } rl->n = i; return ret;}static voidadd_route3 (in_addr_t network, in_addr_t netmask, in_addr_t gateway){ struct route r; CLEAR (r); r.defined = true; r.network = network; r.netmask = netmask; r.gateway = gateway; add_route (&r);}static voiddel_route3 (in_addr_t network, in_addr_t netmask, in_addr_t gateway){ struct route r; CLEAR (r); r.defined = true; r.network = network; r.netmask = netmask; r.gateway = gateway; delete_route (&r);}static voidredirect_default_route_to_vpn (struct route_list *rl){ const char err[] = "NOTE: unable to redirect default gateway --"; if (rl->redirect_default_gateway) { if (!rl->spec.remote_endpoint_defined) { msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err); } else if (!rl->spec.net_gateway_defined) { msg (M_WARN, "%s Cannot read current default gateway from system", err); } else if (!rl->spec.remote_host_defined) { msg (M_WARN, "%s Cannot obtain current remote host address", err); } else { /* route remote host to original default gateway */ if (!rl->redirect_local) add_route3 (rl->spec.remote_host, ~0, rl->spec.net_gateway); /* delete default route */ del_route3 (0, 0, rl->spec.net_gateway); /* add new default route */ add_route3 (0, 0, rl->spec.remote_endpoint); /* set a flag so we can undo later */ rl->did_redirect_default_gateway = true; } }}static voidundo_redirect_default_route_to_vpn (struct route_list *rl){ if (rl->did_redirect_default_gateway) { /* delete remote host route */ if (!rl->redirect_local) del_route3 (rl->spec.remote_host, ~0, rl->spec.net_gateway); /* delete default route */ del_route3 (0, 0, rl->spec.remote_endpoint); /* restore original default route */ add_route3 (0, 0, rl->spec.net_gateway); rl->did_redirect_default_gateway = false; }}voidadd_routes (struct route_list *rl, bool delete_first){ redirect_default_route_to_vpn (rl); if (!rl->routes_added) { int i; for (i = 0; i < rl->n; ++i) { if (delete_first) delete_route (&rl->routes[i]); add_route (&rl->routes[i]); } rl->routes_added = true; }}voiddelete_routes (struct route_list *rl){ if (rl->routes_added) { int i; for (i = rl->n - 1; i >= 0; --i) { const struct route *r = &rl->routes[i]; delete_route (r); } rl->routes_added = false; } undo_redirect_default_route_to_vpn (rl);}static const char *show_opt (const char *option){ if (!option) return "nil"; else return option;}static voidprint_route_option (const struct route_option *ro, int level){ msg (level, " route %s/%s/%s/%s", show_opt (ro->network), show_opt (ro->netmask), show_opt (ro->gateway), show_opt (ro->metric));}voidprint_route_options (const struct route_option_list *rol, int level){ int i; if (rol->redirect_default_gateway) msg (level, " [redirect_default_gateway local=%d]", rol->redirect_local); for (i = 0; i < rol->n; ++i) print_route_option (&rol->routes[i], level);}static voidprint_route (const struct route *r, int level){ struct gc_arena gc = gc_new (); if (r->defined) msg (level, "%s", route_string (r, &gc)); gc_free (&gc);}voidprint_routes (const struct route_list *rl, int level){ int i; for (i = 0; i < rl->n; ++i) print_route (&rl->routes[i], level);}static voidsetenv_route (const struct route *r, int i){ if (r->defined) { setenv_route_addr ("network", r->network, i); setenv_route_addr ("netmask", r->netmask, i); setenv_route_addr ("gateway", r->gateway, i); if (r->metric_defined) { char name[128]; openvpn_snprintf (name, sizeof (name), "route_metric_%d", i); setenv_int (name, r->metric); } }}voidsetenv_routes (const struct route_list *rl){ int i; for (i = 0; i < rl->n; ++i) setenv_route (&rl->routes[i], i + 1);}static voidadd_route (struct route *r){ struct gc_arena gc; struct buffer buf; const char *network; const char *netmask; const char *gateway; bool status = false; if (!r->defined) return; gc_init (&gc); buf = alloc_buf_gc (256, &gc); network = print_in_addr_t (r->network, false, &gc); netmask = print_in_addr_t (r->netmask, false, &gc); gateway = print_in_addr_t (r->gateway, false, &gc);#if defined(TARGET_LINUX)#ifdef CONFIG_FEATURE_IPROUTE buf_printf (&buf, IPROUTE_PATH " route add %s/%d via %s", network, count_netmask_bits(netmask), gateway); if (r->metric_defined) buf_printf (&buf, " metric %d", r->metric);#else buf_printf (&buf, ROUTE_PATH " add -net %s netmask %s gw %s", network, netmask, gateway); if (r->metric_defined) buf_printf (&buf, " metric %d", r->metric);#endif /*CONFIG_FEATURE_IPROUTE*/ msg (D_ROUTE, "%s", BSTR (&buf)); status = system_check (BSTR (&buf), "ERROR: Linux route add command failed", false);#elif defined (WIN32) buf_printf (&buf, ROUTE_PATH " ADD %s MASK %s %s", network, netmask, gateway); if (r->metric_defined) buf_printf (&buf, " METRIC %d", r->metric); netcmd_semaphore_lock (); msg (D_ROUTE, "%s", BSTR (&buf)); status = system_check (BSTR (&buf), "ERROR: Windows route add command failed", false); netcmd_semaphore_release ();#elif defined (TARGET_SOLARIS) /* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */ buf_printf (&buf, ROUTE_PATH " add");#if 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -