BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file NCDIfConfig.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <inttypes.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <sys/socket.h>
36 #include <net/if.h>
37 #include <net/if_arp.h>
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <linux/if_tun.h>
43 #include <pwd.h>
44  
45 #include <misc/debug.h>
46 #include <base/BLog.h>
47  
48 #include "NCDIfConfig.h"
49  
50 #include <generated/blog_channel_NCDIfConfig.h>
51  
52 #define IP_CMD "ip"
53 #define MODPROBE_CMD "modprobe"
54 #define RESOLVCONF_FILE "/etc/resolv.conf"
55 #define RESOLVCONF_TEMP_FILE "/etc/resolv.conf-ncd-temp"
56 #define TUN_DEVNODE "/dev/net/tun"
57  
58 static int run_command (const char *cmd)
59 {
60 BLog(BLOG_INFO, "run: %s", cmd);
61  
62 return system(cmd);
63 }
64  
65 static int write_to_file (uint8_t *data, size_t data_len, FILE *f)
66 {
67 while (data_len > 0) {
68 size_t bytes = fwrite(data, 1, data_len, f);
69 if (bytes == 0) {
70 return 0;
71 }
72 data += bytes;
73 data_len -= bytes;
74 }
75  
76 return 1;
77 }
78  
79 int NCDIfConfig_query (const char *ifname)
80 {
81 struct ifreq ifr;
82  
83 int flags = 0;
84  
85 int s = socket(AF_INET, SOCK_DGRAM, 0);
86 if (!s) {
87 BLog(BLOG_ERROR, "socket failed");
88 goto fail0;
89 }
90  
91 memset(&ifr, 0, sizeof(ifr));
92 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
93 if (ioctl(s, SIOCGIFFLAGS, &ifr)) {
94 BLog(BLOG_ERROR, "SIOCGIFFLAGS failed");
95 goto fail1;
96 }
97  
98 flags |= NCDIFCONFIG_FLAG_EXISTS;
99  
100 if ((ifr.ifr_flags&IFF_UP)) {
101 flags |= NCDIFCONFIG_FLAG_UP;
102  
103 if ((ifr.ifr_flags&IFF_RUNNING)) {
104 flags |= NCDIFCONFIG_FLAG_RUNNING;
105 }
106 }
107  
108 fail1:
109 close(s);
110 fail0:
111 return flags;
112 }
113  
114 int NCDIfConfig_set_up (const char *ifname)
115 {
116 if (strlen(ifname) >= IFNAMSIZ) {
117 BLog(BLOG_ERROR, "ifname too long");
118 return 0;
119 }
120  
121 char cmd[50 + IFNAMSIZ];
122 sprintf(cmd, IP_CMD" link set %s up", ifname);
123  
124 return !run_command(cmd);
125 }
126  
127 int NCDIfConfig_set_down (const char *ifname)
128 {
129 if (strlen(ifname) >= IFNAMSIZ) {
130 BLog(BLOG_ERROR, "ifname too long");
131 return 0;
132 }
133  
134 char cmd[50 + IFNAMSIZ];
135 sprintf(cmd, IP_CMD" link set %s down", ifname);
136  
137 return !run_command(cmd);
138 }
139  
140 int NCDIfConfig_add_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr)
141 {
142 ASSERT(ifaddr.prefix >= 0)
143 ASSERT(ifaddr.prefix <= 32)
144  
145 if (strlen(ifname) >= IFNAMSIZ) {
146 BLog(BLOG_ERROR, "ifname too long");
147 return 0;
148 }
149  
150 uint8_t *addr = (uint8_t *)&ifaddr.addr;
151  
152 char cmd[50 + IFNAMSIZ];
153 sprintf(cmd, IP_CMD" addr add %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname);
154  
155 return !run_command(cmd);
156 }
157  
158 int NCDIfConfig_remove_ipv4_addr (const char *ifname, struct ipv4_ifaddr ifaddr)
159 {
160 ASSERT(ifaddr.prefix >= 0)
161 ASSERT(ifaddr.prefix <= 32)
162  
163 if (strlen(ifname) >= IFNAMSIZ) {
164 BLog(BLOG_ERROR, "ifname too long");
165 return 0;
166 }
167  
168 uint8_t *addr = (uint8_t *)&ifaddr.addr;
169  
170 char cmd[50 + IFNAMSIZ];
171 sprintf(cmd, IP_CMD" addr del %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d dev %s", addr[0], addr[1], addr[2], addr[3], ifaddr.prefix, ifname);
172  
173 return !run_command(cmd);
174 }
175  
176 int NCDIfConfig_add_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr)
177 {
178 ASSERT(ifaddr.prefix >= 0)
179 ASSERT(ifaddr.prefix <= 128)
180  
181 if (strlen(ifname) >= IFNAMSIZ) {
182 BLog(BLOG_ERROR, "ifname too long");
183 return 0;
184 }
185  
186 char addr_str[IPADDR6_PRINT_MAX];
187 ipaddr6_print_addr(ifaddr.addr, addr_str);
188  
189 char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ];
190 sprintf(cmd, IP_CMD" addr add %s/%d dev %s", addr_str, ifaddr.prefix, ifname);
191  
192 return !run_command(cmd);
193 }
194  
195 int NCDIfConfig_remove_ipv6_addr (const char *ifname, struct ipv6_ifaddr ifaddr)
196 {
197 ASSERT(ifaddr.prefix >= 0)
198 ASSERT(ifaddr.prefix <= 128)
199  
200 if (strlen(ifname) >= IFNAMSIZ) {
201 BLog(BLOG_ERROR, "ifname too long");
202 return 0;
203 }
204  
205 char addr_str[IPADDR6_PRINT_MAX];
206 ipaddr6_print_addr(ifaddr.addr, addr_str);
207  
208 char cmd[40 + IPADDR6_PRINT_MAX + IFNAMSIZ];
209 sprintf(cmd, IP_CMD" addr del %s/%d dev %s", addr_str, ifaddr.prefix, ifname);
210  
211 return !run_command(cmd);
212 }
213  
214 static int route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *ifname)
215 {
216 ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del"))
217 ASSERT(dest.prefix >= 0)
218 ASSERT(dest.prefix <= 32)
219  
220 if (strlen(ifname) >= IFNAMSIZ) {
221 BLog(BLOG_ERROR, "ifname too long");
222 return 0;
223 }
224  
225 uint8_t *d_addr = (uint8_t *)&dest.addr;
226  
227 char gwstr[30];
228 if (gateway) {
229 const uint8_t *g_addr = (uint8_t *)gateway;
230 sprintf(gwstr, " via %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8, g_addr[0], g_addr[1], g_addr[2], g_addr[3]);
231 } else {
232 gwstr[0] = '\0';
233 }
234  
235 char cmd[120 + IFNAMSIZ];
236 sprintf(cmd, IP_CMD" route %s %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d%s metric %d dev %s",
237 cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, gwstr, metric, ifname);
238  
239 return !run_command(cmd);
240 }
241  
242 int NCDIfConfig_add_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device)
243 {
244 return route_cmd("add", dest, gateway, metric, device);
245 }
246  
247 int NCDIfConfig_remove_ipv4_route (struct ipv4_ifaddr dest, const uint32_t *gateway, int metric, const char *device)
248 {
249 return route_cmd("del", dest, gateway, metric, device);
250 }
251  
252 static int route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *ifname)
253 {
254 ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del"))
255 ASSERT(dest.prefix >= 0)
256 ASSERT(dest.prefix <= 128)
257  
258 if (strlen(ifname) >= IFNAMSIZ) {
259 BLog(BLOG_ERROR, "ifname too long");
260 return 0;
261 }
262  
263 char dest_str[IPADDR6_PRINT_MAX];
264 ipaddr6_print_addr(dest.addr, dest_str);
265  
266 char gwstr[10 + IPADDR6_PRINT_MAX];
267 if (gateway) {
268 strcpy(gwstr, " via ");
269 ipaddr6_print_addr(*gateway, gwstr + strlen(gwstr));
270 } else {
271 gwstr[0] = '\0';
272 }
273  
274 char cmd[70 + IPADDR6_PRINT_MAX + IPADDR6_PRINT_MAX + IFNAMSIZ];
275 sprintf(cmd, IP_CMD" route %s %s/%d%s metric %d dev %s",
276 cmdtype, dest_str, dest.prefix, gwstr, metric, ifname);
277  
278 return !run_command(cmd);
279 }
280  
281 int NCDIfConfig_add_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device)
282 {
283 return route_cmd6("add", dest, gateway, metric, device);
284 }
285  
286 int NCDIfConfig_remove_ipv6_route (struct ipv6_ifaddr dest, const struct ipv6_addr *gateway, int metric, const char *device)
287 {
288 return route_cmd6("del", dest, gateway, metric, device);
289 }
290  
291 static int blackhole_route_cmd (const char *cmdtype, struct ipv4_ifaddr dest, int metric)
292 {
293 ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del"))
294 ASSERT(dest.prefix >= 0)
295 ASSERT(dest.prefix <= 32)
296  
297 uint8_t *d_addr = (uint8_t *)&dest.addr;
298  
299 char cmd[120];
300 sprintf(cmd, IP_CMD" route %s blackhole %"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"/%d metric %d",
301 cmdtype, d_addr[0], d_addr[1], d_addr[2], d_addr[3], dest.prefix, metric);
302  
303 return !run_command(cmd);
304 }
305  
306 int NCDIfConfig_add_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric)
307 {
308 return blackhole_route_cmd("add", dest, metric);
309 }
310  
311 int NCDIfConfig_remove_ipv4_blackhole_route (struct ipv4_ifaddr dest, int metric)
312 {
313 return blackhole_route_cmd("del", dest, metric);
314 }
315  
316 static int blackhole_route_cmd6 (const char *cmdtype, struct ipv6_ifaddr dest, int metric)
317 {
318 ASSERT(!strcmp(cmdtype, "add") || !strcmp(cmdtype, "del"))
319 ASSERT(dest.prefix >= 0)
320 ASSERT(dest.prefix <= 128)
321  
322 char dest_str[IPADDR6_PRINT_MAX];
323 ipaddr6_print_addr(dest.addr, dest_str);
324  
325 char cmd[70 + IPADDR6_PRINT_MAX];
326 sprintf(cmd, IP_CMD" route %s blackhole %s/%d metric %d",
327 cmdtype, dest_str, dest.prefix, metric);
328  
329 return !run_command(cmd);
330 }
331  
332 int NCDIfConfig_add_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric)
333 {
334 return blackhole_route_cmd6("add", dest, metric);
335 }
336  
337 int NCDIfConfig_remove_ipv6_blackhole_route (struct ipv6_ifaddr dest, int metric)
338 {
339 return blackhole_route_cmd6("del", dest, metric);
340 }
341  
342 int NCDIfConfig_set_resolv_conf (const char *data, size_t data_len)
343 {
344 FILE *temp_file = fopen(RESOLVCONF_TEMP_FILE, "w");
345 if (!temp_file) {
346 BLog(BLOG_ERROR, "failed to open resolvconf temp file");
347 goto fail0;
348 }
349  
350 char line[] = "# generated by badvpn-ncd\n";
351 if (!write_to_file((uint8_t *)line, strlen(line), temp_file) ||
352 !write_to_file((uint8_t *)data, data_len, temp_file)
353 ) {
354 BLog(BLOG_ERROR, "failed to write to resolvconf temp file");
355 goto fail1;
356 }
357  
358 if (fclose(temp_file) != 0) {
359 BLog(BLOG_ERROR, "failed to close resolvconf temp file");
360 return 0;
361 }
362  
363 if (rename(RESOLVCONF_TEMP_FILE, RESOLVCONF_FILE) < 0) {
364 BLog(BLOG_ERROR, "failed to rename resolvconf temp file to resolvconf file");
365 return 0;
366 }
367  
368 return 1;
369  
370 fail1:
371 fclose(temp_file);
372 fail0:
373 return 0;
374 }
375  
376 static int open_tuntap (const char *ifname, int flags)
377 {
378 if (strlen(ifname) >= IFNAMSIZ) {
379 BLog(BLOG_ERROR, "ifname too long");
380 return -1;
381 }
382  
383 int fd = open(TUN_DEVNODE, O_RDWR);
384 if (fd < 0) {
385 BLog(BLOG_ERROR, "open tun failed");
386 return -1;
387 }
388  
389 struct ifreq ifr;
390 memset(&ifr, 0, sizeof(ifr));
391 ifr.ifr_flags = flags;
392 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname);
393  
394 if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
395 BLog(BLOG_ERROR, "TUNSETIFF failed");
396 close(fd);
397 return -1;
398 }
399  
400 return fd;
401 }
402  
403 int NCDIfConfig_make_tuntap (const char *ifname, const char *owner, int tun)
404 {
405 // load tun module if needed
406 if (access(TUN_DEVNODE, F_OK) < 0) {
407 if (run_command(MODPROBE_CMD" tun") != 0) {
408 BLog(BLOG_ERROR, "modprobe tun failed");
409 }
410 }
411  
412 int fd;
413 if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) {
414 goto fail0;
415 }
416  
417 if (owner) {
418 long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
419 if (bufsize < 0) {
420 bufsize = 16384;
421 }
422  
423 char *buf = malloc(bufsize);
424 if (!buf) {
425 BLog(BLOG_ERROR, "malloc failed");
426 goto fail1;
427 }
428  
429 struct passwd pwd;
430 struct passwd *res;
431 getpwnam_r(owner, &pwd, buf, bufsize, &res);
432 if (!res) {
433 BLog(BLOG_ERROR, "getpwnam_r failed");
434 free(buf);
435 goto fail1;
436 }
437  
438 int uid = pwd.pw_uid;
439  
440 free(buf);
441  
442 if (ioctl(fd, TUNSETOWNER, uid) < 0) {
443 BLog(BLOG_ERROR, "TUNSETOWNER failed");
444 goto fail1;
445 }
446 }
447  
448 if (ioctl(fd, TUNSETPERSIST, (void *)1) < 0) {
449 BLog(BLOG_ERROR, "TUNSETPERSIST failed");
450 goto fail1;
451 }
452  
453 close(fd);
454  
455 return 1;
456  
457 fail1:
458 close(fd);
459 fail0:
460 return 0;
461 }
462  
463 int NCDIfConfig_remove_tuntap (const char *ifname, int tun)
464 {
465 int fd;
466 if ((fd = open_tuntap(ifname, (tun ? IFF_TUN : IFF_TAP))) < 0) {
467 goto fail0;
468 }
469  
470 if (ioctl(fd, TUNSETPERSIST, (void *)0) < 0) {
471 BLog(BLOG_ERROR, "TUNSETPERSIST failed");
472 goto fail1;
473 }
474  
475 close(fd);
476  
477 return 1;
478  
479 fail1:
480 close(fd);
481 fail0:
482 return 0;
483 }