BadVPN – Blame information for rev 1
?pathlinks?
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 | } |