1 /*
2  *  Networking API implementation for iproute2
3  *
4  *  Copyright (C) 2018 Antonio Quartulli <a@unstable.cc>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2
8  *  as published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program (see the file COPYING included with this
17  *  distribution); if not, write to the Free Software Foundation, Inc.,
18  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #elif defined(_MSC_VER)
24 #include "config-msvc.h"
25 #endif
26 
27 #if defined(TARGET_LINUX) && defined(ENABLE_IPROUTE)
28 
29 #include "syshead.h"
30 
31 #include "argv.h"
32 #include "networking.h"
33 #include "misc.h"
34 #include "openvpn.h"
35 #include "run_command.h"
36 #include "socket.h"
37 
38 #include <stdbool.h>
39 #include <netinet/in.h>
40 
41 int
net_ctx_init(struct context * c,openvpn_net_ctx_t * ctx)42 net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx)
43 {
44     ctx->es = NULL;
45     if (c)
46     {
47         ctx->es = c->es;
48     }
49     ctx->gc = gc_new();
50 
51     return 0;
52 }
53 
54 void
net_ctx_reset(openvpn_net_ctx_t * ctx)55 net_ctx_reset(openvpn_net_ctx_t *ctx)
56 {
57     gc_reset(&ctx->gc);
58 }
59 
60 void
net_ctx_free(openvpn_net_ctx_t * ctx)61 net_ctx_free(openvpn_net_ctx_t *ctx)
62 {
63     gc_free(&ctx->gc);
64 }
65 
66 int
net_iface_up(openvpn_net_ctx_t * ctx,const char * iface,bool up)67 net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up)
68 {
69     struct argv argv = argv_new();
70 
71     argv_printf(&argv, "%s link set dev %s %s", iproute_path, iface,
72                 up ? "up" : "down");
73     argv_msg(M_INFO, &argv);
74     openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed");
75 
76     argv_free(&argv);
77 
78     return 0;
79 }
80 
81 int
net_iface_mtu_set(openvpn_net_ctx_t * ctx,const char * iface,uint32_t mtu)82 net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, uint32_t mtu)
83 {
84     struct argv argv = argv_new();
85 
86     argv_printf(&argv, "%s link set dev %s up mtu %d", iproute_path, iface,
87                 mtu);
88     argv_msg(M_INFO, &argv);
89     openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed");
90 
91     argv_free(&argv);
92 
93     return 0;
94 }
95 
96 int
net_addr_v4_add(openvpn_net_ctx_t * ctx,const char * iface,const in_addr_t * addr,int prefixlen)97 net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface,
98                 const in_addr_t *addr, int prefixlen)
99 {
100     struct argv argv = argv_new();
101 
102     const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc);
103 
104     argv_printf(&argv, "%s addr add dev %s %s/%d", iproute_path, iface,
105                 addr_str, prefixlen);
106     argv_msg(M_INFO, &argv);
107     openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed");
108 
109     argv_free(&argv);
110 
111     return 0;
112 }
113 
114 int
net_addr_v6_add(openvpn_net_ctx_t * ctx,const char * iface,const struct in6_addr * addr,int prefixlen)115 net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface,
116                 const struct in6_addr *addr, int prefixlen)
117 {
118     struct argv argv = argv_new();
119     char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc);
120 
121     argv_printf(&argv, "%s -6 addr add %s/%d dev %s", iproute_path, addr_str,
122                 prefixlen, iface);
123     argv_msg(M_INFO, &argv);
124     openvpn_execve_check(&argv, ctx->es, S_FATAL,
125                          "Linux ip -6 addr add failed");
126 
127     argv_free(&argv);
128 
129     return 0;
130 }
131 
132 int
net_addr_v4_del(openvpn_net_ctx_t * ctx,const char * iface,const in_addr_t * addr,int prefixlen)133 net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface,
134                 const in_addr_t *addr, int prefixlen)
135 {
136     struct argv argv = argv_new();
137     const char *addr_str = print_in_addr_t(*addr, 0, &ctx->gc);
138 
139     argv_printf(&argv, "%s addr del dev %s %s/%d", iproute_path, iface,
140                 addr_str, prefixlen);
141 
142     argv_msg(M_INFO, &argv);
143     openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed");
144 
145     argv_free(&argv);
146 
147     return 0;
148 }
149 
150 int
net_addr_v6_del(openvpn_net_ctx_t * ctx,const char * iface,const struct in6_addr * addr,int prefixlen)151 net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface,
152                 const struct in6_addr *addr, int prefixlen)
153 {
154     struct argv argv = argv_new();
155     char *addr_str = (char *)print_in6_addr(*addr, 0, &ctx->gc);
156 
157     argv_printf(&argv, "%s -6 addr del %s/%d dev %s", iproute_path,
158                 addr_str, prefixlen, iface);
159     argv_msg(M_INFO, &argv);
160     openvpn_execve_check(&argv, ctx->es, 0, "Linux ip -6 addr del failed");
161 
162     argv_free(&argv);
163 
164     return 0;
165 }
166 
167 int
net_addr_ptp_v4_add(openvpn_net_ctx_t * ctx,const char * iface,const in_addr_t * local,const in_addr_t * remote)168 net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface,
169                     const in_addr_t *local, const in_addr_t *remote)
170 {
171     struct argv argv = argv_new();
172     const char *local_str = print_in_addr_t(*local, 0, &ctx->gc);
173     const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc);
174 
175     argv_printf(&argv, "%s addr add dev %s local %s peer %s", iproute_path,
176                 iface, local_str, remote_str);
177     argv_msg(M_INFO, &argv);
178     openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed");
179 
180     argv_free(&argv);
181 
182     return 0;
183 }
184 
185 int
net_addr_ptp_v4_del(openvpn_net_ctx_t * ctx,const char * iface,const in_addr_t * local,const in_addr_t * remote)186 net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface,
187                     const in_addr_t *local, const in_addr_t *remote)
188 {
189     struct argv argv = argv_new();
190     const char *local_str = print_in_addr_t(*local, 0, &ctx->gc);
191     const char *remote_str = print_in_addr_t(*remote, 0, &ctx->gc);
192 
193     argv_printf(&argv, "%s addr del dev %s local %s peer %s", iproute_path,
194                 iface, local_str, remote_str);
195     argv_msg(M_INFO, &argv);
196     openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed");
197 
198     argv_free(&argv);
199 
200     return 0;
201 }
202 
203 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)204 net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen,
205                  const in_addr_t *gw, const char *iface, uint32_t table,
206                  int metric)
207 {
208     struct argv argv = argv_new();
209     const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc);
210 
211     argv_printf(&argv, "%s route add %s/%d", iproute_path, dst_str, prefixlen);
212 
213     if (metric > 0)
214     {
215         argv_printf_cat(&argv, "metric %d", metric);
216     }
217 
218     if (iface)
219     {
220         argv_printf_cat(&argv, "dev %s", iface);
221     }
222 
223     if (gw)
224     {
225         const char *gw_str = print_in_addr_t(*gw, 0, &ctx->gc);
226 
227         argv_printf_cat(&argv, "via %s", gw_str);
228     }
229 
230     argv_msg(D_ROUTE, &argv);
231     openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route add command failed");
232 
233     argv_free(&argv);
234 
235     return 0;
236 }
237 
238 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)239 net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst,
240                  int prefixlen, const struct in6_addr *gw, const char *iface,
241                  uint32_t table, int metric)
242 {
243     struct argv argv = argv_new();
244     char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc);
245 
246     argv_printf(&argv, "%s -6 route add %s/%d dev %s", iproute_path, dst_str,
247                 prefixlen, iface);
248 
249     if (gw)
250     {
251         char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc);
252 
253         argv_printf_cat(&argv, "via %s", gw_str);
254     }
255 
256     if (metric > 0)
257     {
258         argv_printf_cat(&argv, "metric %d", metric);
259     }
260 
261     argv_msg(D_ROUTE, &argv);
262     openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 add command failed");
263 
264     argv_free(&argv);
265 
266     return 0;
267 }
268 
269 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)270 net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen,
271                  const in_addr_t *gw, const char *iface, uint32_t table,
272                  int metric)
273 {
274     struct argv argv = argv_new();
275     const char *dst_str = print_in_addr_t(*dst, 0, &ctx->gc);
276 
277     argv_printf(&argv, "%s route del %s/%d", iproute_path, dst_str, prefixlen);
278 
279     if (metric > 0)
280     {
281         argv_printf_cat(&argv, "metric %d", metric);
282     }
283 
284     argv_msg(D_ROUTE, &argv);
285     openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route delete command failed");
286 
287     argv_free(&argv);
288 
289     return 0;
290 }
291 
292 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)293 net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst,
294                  int prefixlen, const struct in6_addr *gw, const char *iface,
295                  uint32_t table, int metric)
296 {
297     struct argv argv = argv_new();
298     char *dst_str = (char *)print_in6_addr(*dst, 0, &ctx->gc);
299 
300     argv_printf(&argv, "%s -6 route del %s/%d dev %s", iproute_path, dst_str,
301                 prefixlen, iface);
302 
303     if (gw)
304     {
305         char *gw_str = (char *)print_in6_addr(*gw, 0, &ctx->gc);
306 
307         argv_printf_cat(&argv, "via %s", gw_str);
308     }
309 
310     if (metric > 0)
311     {
312         argv_printf_cat(&argv, "metric %d", metric);
313     }
314 
315     argv_msg(D_ROUTE, &argv);
316     openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 del command failed");
317 
318     argv_free(&argv);
319 
320     return 0;
321 }
322 
323 int
net_route_v4_best_gw(openvpn_net_ctx_t * ctx,const in_addr_t * dst,in_addr_t * best_gw,char * best_iface)324 net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst,
325                      in_addr_t *best_gw, char *best_iface)
326 {
327     best_iface[0] = '\0';
328 
329     FILE *fp = fopen("/proc/net/route", "r");
330     if (!fp)
331     {
332         return -1;
333     }
334 
335     char line[256];
336     int count = 0;
337     unsigned int lowest_metric = UINT_MAX;
338     while (fgets(line, sizeof(line), fp) != NULL)
339     {
340         if (count)
341         {
342             unsigned int net_x = 0;
343             unsigned int mask_x = 0;
344             unsigned int gw_x = 0;
345             unsigned int metric = 0;
346             unsigned int flags = 0;
347             char name[16];
348             name[0] = '\0';
349 
350             const int np = sscanf(line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
351                                   name, &net_x, &gw_x, &flags, &metric,
352                                   &mask_x);
353 
354             if (np == 6 && (flags & IFF_UP))
355             {
356                 const in_addr_t net = ntohl(net_x);
357                 const in_addr_t mask = ntohl(mask_x);
358                 const in_addr_t gw = ntohl(gw_x);
359 
360                 if (!net && !mask && metric < lowest_metric)
361                 {
362                     *best_gw = gw;
363                     strcpy(best_iface, name);
364                     lowest_metric = metric;
365                 }
366             }
367         }
368         ++count;
369     }
370     fclose(fp);
371 
372     return 0;
373 }
374 
375 /*
376  * The following function is not implemented in the iproute backend as it
377  * uses the sitnl implementation from networking_sitnl.c.
378  *
379  * int
380  * net_route_v6_best_gw(const struct in6_addr *dst,
381  *                      struct in6_addr *best_gw, char *best_iface)
382  */
383 
384 #endif /* ENABLE_IPROUTE && TARGET_LINUX */
385