diff options
Diffstat (limited to 'src/openvpn/networking_iproute2.c')
-rw-r--r-- | src/openvpn/networking_iproute2.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c new file mode 100644 index 0000000..f3b9c61 --- /dev/null +++ b/src/openvpn/networking_iproute2.c @@ -0,0 +1,382 @@ +/* + * Networking API implementation for iproute2 + * + * Copyright (C) 2018 Antonio Quartulli <a@unstable.cc> + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#if defined(TARGET_LINUX) && defined(ENABLE_IPROUTE) + +#include "syshead.h" + +#include "argv.h" +#include "networking.h" +#include "misc.h" +#include "openvpn.h" +#include "run_command.h" +#include "socket.h" + +#include <stdbool.h> +#include <netinet/in.h> + +int +net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx) +{ + ctx->es = NULL; + if (c) + { + ctx->es = c->es; + } + ctx->gc = gc_new(); + + return 0; +} + +void +net_ctx_reset(openvpn_net_ctx_t *ctx) +{ + gc_reset(&ctx->gc); +} + +void +net_ctx_free(openvpn_net_ctx_t *ctx) +{ + gc_free(&ctx->gc); +} + +int +net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up) +{ + struct argv argv = argv_new(); + + argv_printf(&argv, "%s link set dev %s %s", iproute_path, iface, + up ? "up" : "down"); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed"); + + argv_free(&argv); + + return 0; +} + +int +net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, uint32_t mtu) +{ + struct argv argv = argv_new(); + + argv_printf(&argv, "%s link set dev %s up mtu %d", iproute_path, iface, + mtu); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed"); + + return 0; +} + +int +net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + struct argv argv = argv_new(); + + const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s addr add dev %s %s/%d", iproute_path, iface, + addr_str, prefixlen); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + struct argv argv = argv_new(); + char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 addr add %s/%d dev %s", iproute_path, addr_str, + prefixlen, iface); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, + "Linux ip -6 addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *addr, int prefixlen) +{ + struct argv argv = argv_new(); + const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s addr del dev %s %s/%d", iproute_path, iface, + addr_str, prefixlen); + + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface, + const struct in6_addr *addr, int prefixlen) +{ + struct argv argv = argv_new(); + char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 addr del %s/%d dev %s", iproute_path, + addr_str, prefixlen, iface); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip -6 addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + struct argv argv = argv_new(); + const char *local_str = print_in_addr_t(*local, 0, &ctx->gc); + const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc); + + argv_printf(&argv, "%s addr add dev %s local %s peer %s", iproute_path, + iface, local_str, remote_str); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed"); + + argv_free(&argv); + + return 0; +} + +int +net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface, + const in_addr_t *local, const in_addr_t *remote) +{ + struct argv argv = argv_new(); + const char *local_str = print_in_addr_t(*local, 0, &ctx->gc); + const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc); + + argv_printf(&argv, "%s addr del dev %s local %s peer %s", iproute_path, + iface, local_str, remote_str); + argv_msg(M_INFO, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric) +{ + struct argv argv = argv_new(); + const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s route add %s/%d", iproute_path, dst_str, prefixlen); + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + if (iface) + { + argv_printf_cat(&argv, "dev %s", iface); + } + + if (gw) + { + const char *gw_str = print_in_addr_t(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route add command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, const char *iface, + uint32_t table, int metric) +{ + struct argv argv = argv_new(); + char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 route add %s/%d dev %s", iproute_path, dst_str, + prefixlen, iface); + + if (gw) + { + char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 add command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen, + const in_addr_t *gw, const char *iface, uint32_t table, + int metric) +{ + struct argv argv = argv_new(); + const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s route del %s/%d", iproute_path, dst_str, prefixlen); + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route delete command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst, + int prefixlen, const struct in6_addr *gw, const char *iface, + uint32_t table, int metric) +{ + struct argv argv = argv_new(); + char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc); + + argv_printf(&argv, "%s -6 route del %s/%d dev %s", iproute_path, dst_str, + prefixlen, iface); + + if (gw) + { + char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc); + + argv_printf_cat(&argv, "via %s", gw_str); + } + + if (metric > 0) + { + argv_printf_cat(&argv, "metric %d", metric); + } + + argv_msg(D_ROUTE, &argv); + openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 del command failed"); + + argv_free(&argv); + + return 0; +} + +int +net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst, + in_addr_t *best_gw, char *best_iface) +{ + best_iface[0] = '\0'; + + FILE *fp = fopen("/proc/net/route", "r"); + if (!fp) + { + return -1; + } + + char line[256]; + int count = 0; + unsigned int lowest_metric = UINT_MAX; + 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) + { + *best_gw = gw; + strcpy(best_iface, name); + lowest_metric = metric; + } + } + } + ++count; + } + fclose(fp); + + return 0; +} + +/* + * The following function is not implemented in the iproute backend as it + * uses the sitnl implementation from networking_sitnl.c. + * + * int + * net_route_v6_best_gw(const struct in6_addr *dst, + * struct in6_addr *best_gw, char *best_iface) + */ + +#endif /* ENABLE_IPROUTE && TARGET_LINUX */ |