firewall3: ipset: Handle reload_set properly
[oweals/firewall3.git] / main.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.io>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <stdio.h>
20 #include <unistd.h>
21
22 #include "options.h"
23 #include "defaults.h"
24 #include "zones.h"
25 #include "rules.h"
26 #include "redirects.h"
27 #include "snats.h"
28 #include "forwards.h"
29 #include "ipsets.h"
30 #include "includes.h"
31 #include "ubus.h"
32 #include "iptables.h"
33 #include "helpers.h"
34
35
36 static enum fw3_family print_family = FW3_FAMILY_ANY;
37
38 static struct fw3_state *run_state = NULL;
39 static struct fw3_state *cfg_state = NULL;
40
41
42 static bool
43 build_state(bool runtime)
44 {
45         struct fw3_state *state = NULL;
46         struct uci_package *p = NULL;
47         FILE *sf;
48
49         state = calloc(1, sizeof(*state));
50         if (!state)
51                 error("Out of memory");
52
53         state->uci = uci_alloc_context();
54
55         if (!state->uci)
56                 error("Out of memory");
57
58         if (runtime)
59         {
60                 sf = fopen(FW3_STATEFILE, "r");
61
62                 if (sf)
63                 {
64                         uci_import(state->uci, sf, "fw3_state", &p, true);
65                         fclose(sf);
66                 }
67
68                 if (!p)
69                 {
70                         uci_free_context(state->uci);
71                         free(state);
72
73                         return false;
74                 }
75
76                 state->statefile = true;
77
78                 run_state = state;
79         }
80         else
81         {
82                 if (!fw3_ubus_connect())
83                         warn("Failed to connect to ubus");
84
85                 if (uci_load(state->uci, "firewall", &p))
86                 {
87                         uci_perror(state->uci, NULL);
88                         error("Failed to load /etc/config/firewall");
89                 }
90
91                 if (!fw3_find_command("ipset"))
92                 {
93                         warn("Unable to locate ipset utility, disabling ipset support");
94                         state->disable_ipsets = true;
95                 }
96
97                 cfg_state = state;
98         }
99
100
101         struct blob_buf b = {NULL, NULL, 0, NULL};
102         fw3_ubus_rules(&b);
103
104         fw3_load_defaults(state, p);
105         fw3_load_cthelpers(state, p);
106         fw3_load_ipsets(state, p, b.head);
107         fw3_load_zones(state, p);
108         fw3_load_rules(state, p, b.head);
109         fw3_load_redirects(state, p, b.head);
110         fw3_load_snats(state, p, b.head);
111         fw3_load_forwards(state, p, b.head);
112         fw3_load_includes(state, p, b.head);
113
114         return true;
115 }
116
117 static void
118 free_state(struct fw3_state *state)
119 {
120         struct list_head *cur, *tmp;
121
122         list_for_each_safe(cur, tmp, &state->zones)
123                 fw3_free_zone((struct fw3_zone *)cur);
124
125         list_for_each_safe(cur, tmp, &state->rules)
126                 fw3_free_rule((struct fw3_rule *)cur);
127
128         list_for_each_safe(cur, tmp, &state->redirects)
129                 fw3_free_redirect((struct fw3_redirect *)cur);
130
131         list_for_each_safe(cur, tmp, &state->snats)
132                 fw3_free_snat((struct fw3_snat *)cur);
133
134         list_for_each_safe(cur, tmp, &state->forwards)
135                 fw3_free_forward((struct fw3_forward *)cur);
136
137         list_for_each_safe(cur, tmp, &state->ipsets)
138                 fw3_free_ipset((struct fw3_ipset *)cur);
139
140         list_for_each_safe(cur, tmp, &state->includes)
141                 fw3_free_include((struct fw3_include *)cur);
142
143         list_for_each_safe(cur, tmp, &state->cthelpers)
144                 fw3_free_cthelper((struct fw3_cthelper *)cur);
145
146         uci_free_context(state->uci);
147
148         free(state);
149
150         fw3_ubus_disconnect();
151 }
152
153
154 static bool
155 family_running(enum fw3_family family)
156 {
157         return (run_state && has(run_state->defaults.flags, family, family));
158 }
159
160 static void
161 family_set(struct fw3_state *state, enum fw3_family family, bool set)
162 {
163         if (!state)
164                 return;
165
166         if (set)
167                 set(state->defaults.flags, family, family);
168         else
169                 del(state->defaults.flags, family, family);
170 }
171
172 static int
173 stop(bool complete)
174 {
175         int rv = 1;
176         enum fw3_family family;
177         enum fw3_table table;
178         struct fw3_ipt_handle *handle;
179
180         if (!complete && !run_state)
181         {
182                 warn("The firewall appears to be stopped. "
183                          "Use the 'flush' command to forcefully purge all rules.");
184
185                 return rv;
186         }
187
188         if (!print_family && run_state)
189                 fw3_hotplug_zones(run_state, false);
190
191         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
192         {
193                 if (!complete && !family_running(family))
194                         continue;
195
196                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
197                 {
198                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
199                                 continue;
200
201                         if (!(handle = fw3_ipt_open(family, table)))
202                                 continue;
203
204                         info(" * %sing %s %s table", complete ? "Flush" : "Clear",
205                              fw3_flag_names[family], fw3_flag_names[table]);
206
207                         if (complete)
208                         {
209                                 fw3_flush_all(handle);
210                         }
211                         else if (run_state)
212                         {
213                                 fw3_flush_rules(handle, run_state, false);
214                                 fw3_flush_zones(handle, run_state, false);
215                         }
216
217                         fw3_ipt_commit(handle);
218                         fw3_ipt_close(handle);
219                 }
220
221                 family_set(run_state, family, false);
222                 family_set(cfg_state, family, false);
223
224                 rv = 0;
225         }
226
227         if (run_state) {
228                 for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
229                         fw3_destroy_ipsets(run_state, family, false);
230         }
231
232         if (complete)
233                 fw3_flush_conntrack(NULL);
234
235         if (!rv && run_state)
236                 fw3_write_statefile(run_state);
237
238         return rv;
239 }
240
241 static int
242 start(void)
243 {
244         int rv = 1;
245         enum fw3_family family;
246         enum fw3_table table;
247         struct fw3_ipt_handle *handle;
248
249         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
250         {
251                 if (!print_family)
252                         fw3_create_ipsets(cfg_state, family, false);
253
254                 if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
255                         continue;
256
257                 if (print_family && family != print_family)
258                         continue;
259
260                 if (!print_family && family_running(family))
261                 {
262                         warn("The %s firewall appears to be started already. "
263                              "If it is indeed empty, remove the %s file and retry.",
264                              fw3_flag_names[family], FW3_STATEFILE);
265
266                         continue;
267                 }
268
269                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
270                 {
271                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
272                                 continue;
273
274                         if (!(handle = fw3_ipt_open(family, table)))
275                                 continue;
276
277                         info(" * Populating %s %s table",
278                              fw3_flag_names[family], fw3_flag_names[table]);
279
280                         fw3_print_default_chains(handle, cfg_state, false);
281                         fw3_print_zone_chains(handle, cfg_state, false);
282                         fw3_print_default_head_rules(handle, cfg_state, false);
283                         fw3_print_rules(handle, cfg_state);
284                         fw3_print_redirects(handle, cfg_state);
285                         fw3_print_snats(handle, cfg_state);
286                         fw3_print_forwards(handle, cfg_state);
287                         fw3_print_zone_rules(handle, cfg_state, false);
288                         fw3_print_default_tail_rules(handle, cfg_state, false);
289
290                         if (!print_family)
291                                 fw3_ipt_commit(handle);
292
293                         fw3_ipt_close(handle);
294                 }
295
296                 if (!print_family)
297                         fw3_print_includes(cfg_state, family, false);
298
299                 family_set(run_state, family, true);
300                 family_set(cfg_state, family, true);
301
302                 rv = 0;
303         }
304
305         if (!rv)
306         {
307                 fw3_flush_conntrack(run_state);
308                 fw3_set_defaults(cfg_state);
309
310                 if (!print_family)
311                 {
312                         fw3_run_includes(cfg_state, false);
313                         fw3_hotplug_zones(cfg_state, true);
314                         fw3_write_statefile(cfg_state);
315                 }
316         }
317
318         return rv;
319 }
320
321
322 static int
323 reload(void)
324 {
325         int rv = 1;
326         enum fw3_family family;
327         enum fw3_table table;
328         struct fw3_ipt_handle *handle;
329
330         if (!run_state)
331                 return start();
332
333         fw3_hotplug_zones(run_state, false);
334
335         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
336         {
337                 if (!family_running(family))
338                         goto start;
339
340                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
341                 {
342                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
343                                 continue;
344
345                         if (!(handle = fw3_ipt_open(family, table)))
346                                 continue;
347
348                         info(" * Clearing %s %s table",
349                              fw3_flag_names[family], fw3_flag_names[table]);
350
351                         fw3_flush_rules(handle, run_state, true);
352                         fw3_flush_zones(handle, run_state, true);
353                         fw3_ipt_commit(handle);
354                         fw3_ipt_close(handle);
355                 }
356
357                 fw3_ipsets_update_run_state(family, run_state, cfg_state);
358                 fw3_destroy_ipsets(run_state, family, true);
359
360                 family_set(run_state, family, false);
361                 family_set(cfg_state, family, false);
362
363 start:
364                 if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
365                         continue;
366
367                 fw3_create_ipsets(cfg_state, family, true);
368
369                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
370                 {
371                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
372                                 continue;
373
374                         if (!(handle = fw3_ipt_open(family, table)))
375                                 continue;
376
377                         info(" * Populating %s %s table",
378                              fw3_flag_names[family], fw3_flag_names[table]);
379
380                         fw3_print_default_chains(handle, cfg_state, true);
381                         fw3_print_zone_chains(handle, cfg_state, true);
382                         fw3_print_default_head_rules(handle, cfg_state, true);
383                         fw3_print_rules(handle, cfg_state);
384                         fw3_print_redirects(handle, cfg_state);
385                         fw3_print_snats(handle, cfg_state);
386                         fw3_print_forwards(handle, cfg_state);
387                         fw3_print_zone_rules(handle, cfg_state, true);
388                         fw3_print_default_tail_rules(handle, cfg_state, true);
389
390                         fw3_ipt_commit(handle);
391                         fw3_ipt_close(handle);
392                 }
393
394                 fw3_print_includes(cfg_state, family, true);
395
396                 family_set(run_state, family, true);
397                 family_set(cfg_state, family, true);
398
399                 rv = 0;
400         }
401
402         if (!rv)
403         {
404                 fw3_flush_conntrack(run_state);
405
406                 fw3_set_defaults(cfg_state);
407                 fw3_run_includes(cfg_state, true);
408                 fw3_hotplug_zones(cfg_state, true);
409                 fw3_write_statefile(cfg_state);
410         }
411
412         return rv;
413 }
414
415 static int
416 gc(void)
417 {
418         enum fw3_family family;
419         enum fw3_table table;
420         struct fw3_ipt_handle *handle;
421
422         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
423         {
424                 if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
425                         continue;
426
427                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
428                 {
429                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
430                                 continue;
431
432                         if (!(handle = fw3_ipt_open(family, table)))
433                                 continue;
434
435                         fw3_ipt_gc(handle);
436                         fw3_ipt_commit(handle);
437                         fw3_ipt_close(handle);
438                 }
439         }
440
441         return 0;
442 }
443
444 static int
445 lookup_network(const char *net)
446 {
447         struct fw3_zone *z;
448         struct fw3_device *d;
449
450         list_for_each_entry(z, &cfg_state->zones, list)
451         {
452                 list_for_each_entry(d, &z->networks, list)
453                 {
454                         if (!strcmp(d->name, net))
455                         {
456                                 printf("%s\n", z->name);
457                                 return 0;
458                         }
459                 }
460         }
461
462         return 1;
463 }
464
465 static int
466 lookup_device(const char *dev)
467 {
468         struct fw3_zone *z;
469         struct fw3_device *d;
470
471         list_for_each_entry(z, &cfg_state->zones, list)
472         {
473                 list_for_each_entry(d, &z->devices, list)
474                 {
475                         if (!strcmp(d->name, dev))
476                         {
477                                 printf("%s\n", z->name);
478                                 return 0;
479                         }
480                 }
481         }
482
483         return 1;
484 }
485
486 static int
487 lookup_zone(const char *zone, const char *device)
488 {
489         struct fw3_zone *z;
490         struct fw3_device *d;
491
492         list_for_each_entry(z, &cfg_state->zones, list)
493         {
494                 if (strcmp(z->name, zone))
495                         continue;
496
497                 list_for_each_entry(d, &z->devices, list)
498                 {
499                         if (device && strcmp(device, d->name))
500                                 continue;
501
502                         printf("%s\n", d->name);
503
504                         if (device)
505                                 return 0;
506                 }
507
508                 if (!device)
509                         return 0;
510         }
511
512         return 1;
513 }
514
515 static int
516 usage(void)
517 {
518         fprintf(stderr, "fw3 [-4] [-6] [-q] print\n");
519         fprintf(stderr, "fw3 [-q] {start|stop|flush|reload|restart}\n");
520         fprintf(stderr, "fw3 [-q] network {net}\n");
521         fprintf(stderr, "fw3 [-q] device {dev}\n");
522         fprintf(stderr, "fw3 [-q] zone {zone} [dev]\n");
523
524         return 1;
525 }
526
527
528 int main(int argc, char **argv)
529 {
530         int ch, rv = 1;
531         enum fw3_family family = FW3_FAMILY_ANY;
532         struct fw3_defaults *defs = NULL;
533
534         while ((ch = getopt(argc, argv, "46dqh")) != -1)
535         {
536                 switch (ch)
537                 {
538                 case '4':
539                         family = FW3_FAMILY_V4;
540                         break;
541
542                 case '6':
543                         family = FW3_FAMILY_V6;
544                         break;
545
546                 case 'd':
547                         fw3_pr_debug = true;
548                         break;
549
550                 case 'q':
551                         if (freopen("/dev/null", "w", stderr)) {}
552                         break;
553
554                 case 'h':
555                         rv = usage();
556                         goto out;
557                 }
558         }
559
560         build_state(false);
561         defs = &cfg_state->defaults;
562
563         if (optind >= argc)
564         {
565                 rv = usage();
566                 goto out;
567         }
568
569         if (!strcmp(argv[optind], "print"))
570         {
571                 if (family == FW3_FAMILY_ANY)
572                 {
573                         family = FW3_FAMILY_V4;
574                 }
575                 else if (family == FW3_FAMILY_V6)
576                 {
577                         if (defs->disable_ipv6)
578                                 warn("IPv6 rules globally disabled in configuration");
579 #ifdef DISABLE_IPV6
580                         else
581                                 warn("IPv6 support is not compiled in");
582 #endif
583                 }
584
585                 if (freopen("/dev/null", "w", stderr)) {};
586
587                 cfg_state->disable_ipsets = true;
588                 print_family = family;
589                 fw3_pr_debug = true;
590
591                 if (fw3_lock())
592                 {
593                         build_state(true);
594                         rv = start();
595                         fw3_unlock();
596                 }
597         }
598         else if (!strcmp(argv[optind], "start"))
599         {
600                 if (fw3_lock())
601                 {
602                         build_state(true);
603                         rv = start();
604                         fw3_unlock();
605                 }
606         }
607         else if (!strcmp(argv[optind], "stop"))
608         {
609                 if (fw3_lock())
610                 {
611                         build_state(true);
612                         rv = stop(false);
613                         fw3_unlock();
614                 }
615         }
616         else if (!strcmp(argv[optind], "flush"))
617         {
618                 if (fw3_lock())
619                 {
620                         build_state(true);
621                         rv = stop(true);
622                         fw3_unlock();
623                 }
624         }
625         else if (!strcmp(argv[optind], "restart"))
626         {
627                 if (fw3_lock())
628                 {
629                         build_state(true);
630                         stop(true);
631                         rv = start();
632                         fw3_unlock();
633                 }
634         }
635         else if (!strcmp(argv[optind], "reload"))
636         {
637                 if (fw3_lock())
638                 {
639                         build_state(true);
640                         rv = reload();
641                         fw3_unlock();
642                 }
643         }
644         else if (!strcmp(argv[optind], "gc"))
645         {
646                 if (fw3_lock())
647                 {
648                         rv = gc();
649                         fw3_unlock();
650                 }
651         }
652         else if (!strcmp(argv[optind], "network") && (optind + 1) < argc)
653         {
654                 rv = lookup_network(argv[optind + 1]);
655         }
656         else if (!strcmp(argv[optind], "device") && (optind + 1) < argc)
657         {
658                 rv = lookup_device(argv[optind + 1]);
659         }
660         else if (!strcmp(argv[optind], "zone") && (optind + 1) < argc)
661         {
662                 rv = lookup_zone(argv[optind + 1], argv[optind + 2]);
663         }
664         else
665         {
666                 rv = usage();
667         }
668
669 out:
670         if (cfg_state)
671                 free_state(cfg_state);
672
673         if (run_state)
674                 free_state(run_state);
675
676         return rv;
677 }