openvpnctrl: Pass external IP address to N2N daemons.
[people/teissler/ipfire-2.x.git] / src / misc-progs / openvpnctrl.c
1 #include <signal.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <fcntl.h>
10 #include "setuid.h"
11 #include "netutil.h"
12 #include "libsmooth.h"
13
14 #define noovpndebug
15
16 // global vars
17 struct keyvalue *kv = NULL;
18 FILE *ifacefile = NULL;
19
20 char redif[STRING_SIZE];
21 char blueif[STRING_SIZE];
22 char orangeif[STRING_SIZE];
23 char enablered[STRING_SIZE] = "off";
24 char enableblue[STRING_SIZE] = "off";
25 char enableorange[STRING_SIZE] = "off";
26
27 // consts
28 char OVPNINPUT[STRING_SIZE] = "OVPNINPUT";
29 char OVPNBLOCK[STRING_SIZE] = "OVPNBLOCK";
30 char OVPNNAT[STRING_SIZE] = "OVPNNAT";
31 char WRAPPERVERSION[STRING_SIZE] = "ipfire-2.2.4";
32
33 struct connection_struct {
34 char name[STRING_SIZE];
35 char type[STRING_SIZE];
36 char proto[STRING_SIZE];
37 char status[STRING_SIZE];
38 char local_subnet[STRING_SIZE];
39 char transfer_subnet[STRING_SIZE];
40 char role[STRING_SIZE];
41 char port[STRING_SIZE];
42 struct connection_struct *next;
43 };
44
45 typedef struct connection_struct connection;
46
47 void exithandler(void)
48 {
49 if(kv)
50 freekeyvalues(kv);
51 if (ifacefile)
52 fclose(ifacefile);
53 }
54
55 void usage(void)
56 {
57 #ifdef ovpndebug
58 printf("Wrapper for OpenVPN %s-debug\n", WRAPPERVERSION);
59 #else
60 printf("Wrapper for OpenVPN %s\n", WRAPPERVERSION);
61 #endif
62 printf("openvpnctrl <option>\n");
63 printf(" Valid options are:\n");
64 printf(" -s --start\n");
65 printf(" starts OpenVPN (implicitly creates chains and firewall rules)\n");
66 printf(" -k --kill\n");
67 printf(" kills/stops OpenVPN\n");
68 printf(" -r --restart\n");
69 printf(" restarts OpenVPN (implicitly creates chains and firewall rules)\n");
70 printf(" -sn2n --start-net-2-net\n");
71 printf(" starts all net2net connections\n");
72 printf(" you may pass a connection name to the switch to only start a specific one\n");
73 printf(" -kn2n --kill-net-2-net\n");
74 printf(" kills all net2net connections\n");
75 printf(" you may pass a connection name to the switch to only start a specific one\n");
76 printf(" -d --display\n");
77 printf(" displays OpenVPN status to syslog\n");
78 printf(" -fwr --firewall-rules\n");
79 printf(" removes current OpenVPN chains and rules and resets them according to the config\n");
80 printf(" -sdo --start-daemon-only\n");
81 printf(" starts OpenVPN daemon only\n");
82 exit(1);
83 }
84
85 connection *getConnections() {
86 FILE *fp = NULL;
87
88 if (!(fp = fopen(CONFIG_ROOT "/ovpn/ovpnconfig", "r"))) {
89 fprintf(stderr, "Could not open openvpn n2n configuration file.\n");
90 exit(1);
91 }
92
93 char line[STRING_SIZE] = "";
94 char result[STRING_SIZE] = "";
95 char *resultptr;
96 int count;
97 connection *conn_first = NULL;
98 connection *conn_last = NULL;
99 connection *conn_curr;
100
101 while ((fgets(line, STRING_SIZE, fp) != NULL)) {
102 if (line[strlen(line) - 1] == '\n')
103 line[strlen(line) - 1] = '\0';
104
105 conn_curr = (connection *)malloc(sizeof(connection));
106 memset(conn_curr, 0, sizeof(connection));
107
108 if (conn_first == NULL) {
109 conn_first = conn_curr;
110 } else {
111 conn_last->next = conn_curr;
112 }
113 conn_last = conn_curr;
114
115 count = 0;
116 char *lineptr = &line;
117 while (1) {
118 if (*lineptr == NULL)
119 break;
120
121 resultptr = result;
122 while (*lineptr != NULL) {
123 if (*lineptr == ',') {
124 lineptr++;
125 break;
126 }
127 *resultptr++ = *lineptr++;
128 }
129 *resultptr = '\0';
130
131 if (count == 1) {
132 strcpy(conn_curr->status, result);
133 } else if (count == 2) {
134 strcpy(conn_curr->name, result);
135 } else if (count == 4) {
136 strcpy(conn_curr->type, result);
137 } else if (count == 7) {
138 strcpy(conn_curr->role, result);
139 } else if (count == 9) {
140 strcpy(conn_curr->local_subnet, result);
141 } else if (count == 28) {
142 strcpy(conn_curr->transfer_subnet, result);
143 } else if (count == 29) {
144 strcpy(conn_curr->proto, result);
145 } else if (count == 30) {
146 strcpy(conn_curr->port, result);
147 }
148
149 count++;
150 }
151 }
152
153 fclose(fp);
154
155 return conn_first;
156 }
157
158 int readPidFile(const char *pidfile) {
159 FILE *fp = fopen(pidfile, "r");
160 if (fp == NULL) {
161 exit(1);
162 }
163
164 int pid = 0;
165 fscanf(fp, "%d", &pid);
166 fclose(fp);
167
168 return pid;
169 }
170
171 int readExternalAddress(char* address) {
172 FILE *fp = fopen("/var/ipfire/red/local-ipaddress", "r");
173 if (!fp)
174 goto ERROR;
175
176 int r = fscanf(fp, "%s", address);
177 fclose(fp);
178
179 if (r < 0)
180 goto ERROR;
181
182 /* In case the read IP address is not valid, we empty
183 * the content of address and return non-zero. */
184 if (!VALID_IP(address))
185 goto ERROR;
186
187 return 0;
188
189 ERROR:
190 address = NULL;
191 return 1;
192 }
193
194 void ovpnInit(void) {
195 // Read OpenVPN configuration
196 kv = initkeyvalues();
197 if (!readkeyvalues(kv, CONFIG_ROOT "/ovpn/settings")) {
198 fprintf(stderr, "Cannot read ovpn settings\n");
199 exit(1);
200 }
201
202 if (!findkey(kv, "ENABLED", enablered)) {
203 exit(1);
204 }
205
206 if (!findkey(kv, "ENABLED_BLUE", enableblue)){
207 exit(1);
208 }
209
210 if (!findkey(kv, "ENABLED_ORANGE", enableorange)){
211 exit(1);
212 }
213 freekeyvalues(kv);
214
215 // read interface settings
216
217 // details for the red int
218 memset(redif, 0, STRING_SIZE);
219 if ((ifacefile = fopen(CONFIG_ROOT "/red/iface", "r")))
220 {
221 if (fgets(redif, STRING_SIZE, ifacefile))
222 {
223 if (redif[strlen(redif) - 1] == '\n')
224 redif[strlen(redif) - 1] = '\0';
225 }
226 fclose (ifacefile);
227 ifacefile = NULL;
228
229 if (!VALID_DEVICE(redif))
230 {
231 memset(redif, 0, STRING_SIZE);
232 }
233 }
234
235 kv=initkeyvalues();
236 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) {
237 fprintf(stderr, "Cannot read ethernet settings\n");
238 exit(1);
239 }
240
241 if (strcmp(enableblue, "on") == 0) {
242 if (!findkey(kv, "BLUE_DEV", blueif)) {
243 exit(1);
244 }
245 }
246
247 if (strcmp(enableorange, "on") == 0) {
248 if (!findkey(kv, "ORANGE_DEV", orangeif)) {
249 exit(1);
250 }
251 }
252 freekeyvalues(kv);
253 }
254
255 void executeCommand(char *command) {
256 #ifdef ovpndebug
257 printf(strncat(command, "\n", 2));
258 #endif
259 safe_system(strncat(command, " >/dev/null 2>&1", 17));
260 }
261
262 void addRule(const char *chain, const char *interface, const char *protocol, const char *port) {
263 char command[STRING_SIZE];
264
265 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -i %s -p %s --dport %s -j ACCEPT",
266 chain, interface, protocol, port);
267 executeCommand(command);
268 }
269
270 void flushChain(char *chain) {
271 char str[STRING_SIZE];
272
273 snprintf(str, STRING_SIZE - 1, "/sbin/iptables -F %s", chain);
274 executeCommand(str);
275 }
276
277 void flushChainNAT(char *chain) {
278 char str[STRING_SIZE];
279
280 snprintf(str, STRING_SIZE - 1, "/sbin/iptables -t nat -F %s", chain);
281 executeCommand(str);
282 }
283
284 char* calcTransferNetAddress(const connection* conn) {
285 char *subnetmask = strdup(conn->transfer_subnet);
286 char *address = strsep(&subnetmask, "/");
287
288 if ((address == NULL) || (subnetmask == NULL)) {
289 goto ERROR;
290 }
291
292 in_addr_t _address = inet_addr(address);
293 in_addr_t _subnetmask = inet_addr(subnetmask);
294 _address &= _subnetmask;
295
296 if (strcmp(conn->role, "server") == 0) {
297 _address += 1 << 24;
298 } else if (strcmp(conn->role, "client") == 0) {
299 _address += 2 << 24;
300 } else {
301 goto ERROR;
302 }
303
304 struct in_addr address_info;
305 address_info.s_addr = _address;
306
307 return inet_ntoa(address_info);
308
309 ERROR:
310 fprintf(stderr, "Could not determine transfer net address: %s\n", conn->name);
311
312 free(address);
313 return NULL;
314 }
315
316 char* getLocalSubnetAddress(const connection* conn) {
317 kv = initkeyvalues();
318 if (!readkeyvalues(kv, CONFIG_ROOT "/ethernet/settings")) {
319 fprintf(stderr, "Cannot read ethernet settings\n");
320 exit(1);
321 }
322
323 const char *zones[] = {"GREEN", "BLUE", "ORANGE", NULL};
324 char *zone = NULL;
325
326 // Get net address of the local openvpn subnet.
327 char *subnetmask = strdup(conn->local_subnet);
328 char *address = strsep(&subnetmask, "/");
329
330 if ((address == NULL) || (subnetmask == NULL)) {
331 goto ERROR;
332 }
333
334 in_addr_t _address = inet_addr(address);
335 in_addr_t _subnetmask = inet_addr(subnetmask);
336
337 in_addr_t _netaddr = (_address & _subnetmask);
338 in_addr_t _broadcast = (_address | ~_subnetmask);
339
340 char zone_address_key[STRING_SIZE];
341 char zone_address[STRING_SIZE];
342 in_addr_t zone_addr;
343
344 int i = 0;
345 while (zones[i]) {
346 zone = zones[i++];
347 snprintf(zone_address_key, STRING_SIZE, "%s_ADDRESS", zone);
348
349 if (!findkey(kv, zone_address_key, zone_address))
350 continue;
351
352 zone_addr = inet_addr(zone_address);
353 if ((zone_addr > _netaddr) && (zone_addr < _broadcast)) {
354 freekeyvalues(kv);
355
356 return strdup(zone_address);
357 }
358 }
359
360 ERROR:
361 fprintf(stderr, "Could not determine local subnet address: %s\n", conn->name);
362
363 freekeyvalues(kv);
364 return NULL;
365 }
366
367 void setFirewallRules(void) {
368 char protocol[STRING_SIZE] = "";
369 char dport[STRING_SIZE] = "";
370 char dovpnip[STRING_SIZE] = "";
371
372 kv = initkeyvalues();
373 if (!readkeyvalues(kv, CONFIG_ROOT "/ovpn/settings"))
374 {
375 fprintf(stderr, "Cannot read ovpn settings\n");
376 exit(1);
377 }
378
379 /* we got one device, so lets proceed further */
380 if (!findkey(kv, "DDEST_PORT", dport)){
381 fprintf(stderr, "Cannot read DDEST_PORT\n");
382 exit(1);
383 }
384
385 if (!findkey(kv, "DPROTOCOL", protocol)){
386 fprintf(stderr, "Cannot read DPROTOCOL\n");
387 exit(1);
388 }
389
390 if (!findkey(kv, "VPN_IP", dovpnip)){
391 fprintf(stderr, "Cannot read VPN_IP\n");
392 }
393 freekeyvalues(kv);
394
395 // Flush all chains.
396 flushChain(OVPNINPUT);
397 flushChain(OVPNBLOCK);
398 flushChainNAT(OVPNNAT);
399
400 // set firewall rules
401 if (!strcmp(enablered, "on") && strlen(redif))
402 addRule(OVPNINPUT, redif, protocol, dport);
403 if (!strcmp(enableblue, "on") && strlen(blueif))
404 addRule(OVPNINPUT, blueif, protocol, dport);
405 if (!strcmp(enableorange, "on") && strlen(orangeif))
406 addRule(OVPNINPUT, orangeif, protocol, dport);
407
408 // read connection configuration
409 connection *conn = getConnections();
410
411 // set firewall rules for n2n connections
412 char command[STRING_SIZE];
413 char *local_subnet_address = NULL;
414 char *transfer_subnet_address = NULL;
415 while (conn != NULL) {
416 if (strcmp(conn->type, "net") == 0) {
417 addRule(OVPNINPUT, redif, conn->proto, conn->port);
418
419 /* Block all communication from the transfer nets. */
420 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -A %s -s %s -j DROP",
421 OVPNBLOCK, conn->transfer_subnet);
422 executeCommand(command);
423
424 local_subnet_address = getLocalSubnetAddress(conn);
425 transfer_subnet_address = calcTransferNetAddress(conn);
426
427 if ((local_subnet_address) && (transfer_subnet_address)) {
428 snprintf(command, STRING_SIZE - 1, "/sbin/iptables -t nat -A %s -s %s -j SNAT --to-source %s",
429 OVPNNAT, transfer_subnet_address, local_subnet_address);
430 executeCommand(command);
431 }
432 }
433
434 conn = conn->next;
435 }
436 }
437
438 void stopDaemon(void) {
439 char command[STRING_SIZE];
440
441 int pid = readPidFile("/var/run/openvpn.pid");
442 if (!pid > 0) {
443 exit(1);
444 }
445
446 fprintf(stderr, "Killing PID %d.\n", pid);
447 kill(pid, SIGTERM);
448
449 snprintf(command, STRING_SIZE - 1, "/bin/rm -f /var/run/openvpn.pid");
450 executeCommand(command);
451 }
452
453 void startDaemon(void) {
454 char command[STRING_SIZE];
455
456 if (!((strcmp(enablered, "on") == 0) || (strcmp(enableblue, "on") == 0) || (strcmp(enableorange, "on") == 0))) {
457 fprintf(stderr, "OpenVPN is not enabled on any interface\n");
458 exit(1);
459 } else {
460 snprintf(command, STRING_SIZE-1, "/sbin/modprobe tun");
461 executeCommand(command);
462 snprintf(command, STRING_SIZE-1, "/usr/sbin/openvpn --config /var/ipfire/ovpn/server.conf");
463 executeCommand(command);
464 }
465 }
466
467 int startNet2Net(char *name) {
468 connection *conn = NULL;
469 connection *conn_iter;
470
471 conn_iter = getConnections();
472
473 while (conn_iter) {
474 if ((strcmp(conn_iter->type, "net") == 0) && (strcmp(conn_iter->name, name) == 0)) {
475 conn = conn_iter;
476 break;
477 }
478 conn_iter = conn_iter->next;
479 }
480
481 if (conn == NULL) {
482 fprintf(stderr, "Connection not found.\n");
483 return 1;
484 }
485
486 if (strcmp(conn->status, "on") != 0) {
487 fprintf(stderr, "Connection '%s' is not enabled.\n", conn->name);
488 return 1;
489 }
490
491 fprintf(stderr, "Starting connection %s...\n", conn->name);
492
493 char configfile[STRING_SIZE];
494 snprintf(configfile, STRING_SIZE - 1, CONFIG_ROOT "/ovpn/n2nconf/%s/%s.conf",
495 conn->name, conn->name);
496
497 FILE *fp = fopen(configfile, "r");
498 if (fp == NULL) {
499 fprintf(stderr, "Could not find configuration file for connection '%s' at '%s'.\n",
500 conn->name, configfile);
501 return 2;
502 }
503 fclose(fp);
504
505 // Make sure all firewall rules are up to date.
506 setFirewallRules();
507
508 // Get the external IP address.
509 char address[STRING_SIZE] = "";
510 int r = readExternalAddress(address);
511 if (r) {
512 fprintf(stderr, "Could not read the external address\n");
513 exit(1);
514 }
515
516 char command[STRING_SIZE];
517 snprintf(command, STRING_SIZE-1, "/sbin/modprobe tun");
518 executeCommand(command);
519 snprintf(command, STRING_SIZE-1, "/usr/sbin/openvpn --local %s --config %s", address, configfile);
520 executeCommand(command);
521
522 return 0;
523 }
524
525 int killNet2Net(char *name) {
526 connection *conn = NULL;
527 connection *conn_iter;
528
529 conn_iter = getConnections();
530
531 while (conn_iter) {
532 if (strcmp(conn_iter->name, name) == 0) {
533 conn = conn_iter;
534 break;
535 }
536 conn_iter = conn_iter->next;
537 }
538
539 if (conn == NULL) {
540 fprintf(stderr, "Connection not found.\n");
541 return 1;
542 }
543
544 char pidfile[STRING_SIZE];
545 snprintf(pidfile, STRING_SIZE - 1, "/var/run/%sn2n.pid", conn->name);
546
547 int pid = readPidFile(pidfile);
548 if (!pid > 0) {
549 fprintf(stderr, "Could not read pid file of connection %s.", conn->name);
550 return 1;
551 }
552
553 fprintf(stderr, "Killing connection %s (PID %d)...\n", conn->name, pid);
554 kill(pid, SIGTERM);
555
556 char command[STRING_SIZE];
557 snprintf(command, STRING_SIZE - 1, "/bin/rm -f %s", pidfile);
558 executeCommand(command);
559
560 return 0;
561 }
562
563 void startAllNet2Net() {
564 int exitcode = 0, _exitcode = 0;
565
566 connection *conn = getConnections();
567
568 while(conn) {
569 /* Skip all connections that are not of type "net" or disabled. */
570 if ((strcmp(conn->type, "net") != 0) || (strcmp(conn->status, "on") != 0)) {
571 conn = conn->next;
572 continue;
573 }
574
575 _exitcode = startNet2Net(conn->name);
576 conn = conn->next;
577
578 if (_exitcode > exitcode) {
579 exitcode = _exitcode;
580 }
581 }
582
583 exit(exitcode);
584 }
585
586 void killAllNet2Net() {
587 int exitcode = 0, _exitcode = 0;
588
589 connection *conn = getConnections();
590
591 while(conn) {
592 /* Skip all connections that are not of type "net". */
593 if (strcmp(conn->type, "net") != 0) {
594 conn = conn->next;
595 continue;
596 }
597
598 _exitcode = killNet2Net(conn->name);
599 conn = conn->next;
600
601 if (_exitcode > exitcode) {
602 exitcode = _exitcode;
603 }
604 }
605
606 exit(exitcode);
607 }
608
609 void displayopenvpn(void) {
610 char command[STRING_SIZE];
611
612 snprintf(command, STRING_SIZE - 1, "/bin/killall -sSIGUSR2 openvpn");
613 executeCommand(command);
614 }
615
616 int main(int argc, char *argv[]) {
617 if (!(initsetuid()))
618 exit(1);
619 if(argc < 2)
620 usage();
621
622 if(argc == 3) {
623 ovpnInit();
624
625 if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) {
626 startNet2Net(argv[2]);
627 return 0;
628 }
629 else if( (strcmp(argv[1], "-kn2n") == 0) || (strcmp(argv[1], "--kill-net-2-net") == 0) ) {
630 killNet2Net(argv[2]);
631 return 0;
632 } else {
633 usage();
634 return 1;
635 }
636 }
637 else if(argc == 2) {
638 if( (strcmp(argv[1], "-k") == 0) || (strcmp(argv[1], "--kill") == 0) ) {
639 stopDaemon();
640 return 0;
641 }
642 else if( (strcmp(argv[1], "-d") == 0) || (strcmp(argv[1], "--display") == 0) ) {
643 displayopenvpn();
644 return 0;
645 }
646 else {
647 ovpnInit();
648
649 if( (strcmp(argv[1], "-s") == 0) || (strcmp(argv[1], "--start") == 0) ) {
650 setFirewallRules();
651 startDaemon();
652 return 0;
653 }
654 else if( (strcmp(argv[1], "-sn2n") == 0) || (strcmp(argv[1], "--start-net-2-net") == 0) ) {
655 startAllNet2Net();
656 return 0;
657 }
658 else if( (strcmp(argv[1], "-kn2n") == 0) || (strcmp(argv[1], "--kill-net-2-net") == 0) ) {
659 killAllNet2Net();
660 return 0;
661 }
662 else if( (strcmp(argv[1], "-sdo") == 0) || (strcmp(argv[1], "--start-daemon-only") == 0) ) {
663 startDaemon();
664 return 0;
665 }
666 else if( (strcmp(argv[1], "-r") == 0) || (strcmp(argv[1], "--restart") == 0) ) {
667 stopDaemon();
668 setFirewallRules();
669 startDaemon();
670 return 0;
671 }
672 else if( (strcmp(argv[1], "-fwr") == 0) || (strcmp(argv[1], "--firewall-rules") == 0) ) {
673 setFirewallRules();
674 return 0;
675 }
676 else {
677 usage();
678 return 0;
679 }
680 }
681 }
682 else {
683 usage();
684 return 0;
685 }
686 return 0;
687 }
688