firewall3: Improve ipset support
[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_destroy_ipsets(run_state, family, true);
358
359                 family_set(run_state, family, false);
360                 family_set(cfg_state, family, false);
361
362 start:
363                 if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
364                         continue;
365
366                 fw3_create_ipsets(cfg_state, family, true);
367
368                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
369                 {
370                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
371                                 continue;
372
373                         if (!(handle = fw3_ipt_open(family, table)))
374                                 continue;
375
376                         info(" * Populating %s %s table",
377                              fw3_flag_names[family], fw3_flag_names[table]);
378
379                         fw3_print_default_chains(handle, cfg_state, true);
380                         fw3_print_zone_chains(handle, cfg_state, true);
381                         fw3_print_default_head_rules(handle, cfg_state, true);
382                         fw3_print_rules(handle, cfg_state);
383                         fw3_print_redirects(handle, cfg_state);
384                         fw3_print_snats(handle, cfg_state);
385                         fw3_print_forwards(handle, cfg_state);
386                         fw3_print_zone_rules(handle, cfg_state, true);
387                         fw3_print_default_tail_rules(handle, cfg_state, true);
388
389                         fw3_ipt_commit(handle);
390                         fw3_ipt_close(handle);
391                 }
392
393                 fw3_print_includes(cfg_state, family, true);
394
395                 family_set(run_state, family, true);
396                 family_set(cfg_state, family, true);
397
398                 rv = 0;
399         }
400
401         if (!rv)
402         {
403                 fw3_flush_conntrack(run_state);
404
405                 fw3_set_defaults(cfg_state);
406                 fw3_run_includes(cfg_state, true);
407                 fw3_hotplug_zones(cfg_state, true);
408                 fw3_write_statefile(cfg_state);
409         }
410
411         return rv;
412 }
413
414 static int
415 gc(void)
416 {
417         enum fw3_family family;
418         enum fw3_table table;
419         struct fw3_ipt_handle *handle;
420
421         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
422         {
423                 if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
424                         continue;
425
426                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
427                 {
428                         if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
429                                 continue;
430
431                         if (!(handle = fw3_ipt_open(family, table)))
432                                 continue;
433
434                         fw3_ipt_gc(handle);
435                         fw3_ipt_commit(handle);
436                         fw3_ipt_close(handle);
437                 }
438         }
439
440         return 0;
441 }
442
443 static int
444 lookup_network(const char *net)
445 {
446         struct fw3_zone *z;
447         struct fw3_device *d;
448
449         list_for_each_entry(z, &cfg_state->zones, list)
450         {
451                 list_for_each_entry(d, &z->networks, list)
452                 {
453                         if (!strcmp(d->name, net))
454                         {
455                                 printf("%s\n", z->name);
456                                 return 0;
457                         }
458                 }
459         }
460
461         return 1;
462 }
463
464 static int
465 lookup_device(const char *dev)
466 {
467         struct fw3_zone *z;
468         struct fw3_device *d;
469
470         list_for_each_entry(z, &cfg_state->zones, list)
471         {
472                 list_for_each_entry(d, &z->devices, list)
473                 {
474                         if (!strcmp(d->name, dev))
475                         {
476                                 printf("%s\n", z->name);
477                                 return 0;
478                         }
479                 }
480         }
481
482         return 1;
483 }
484
485 static int
486 lookup_zone(const char *zone, const char *device)
487 {
488         struct fw3_zone *z;
489         struct fw3_device *d;
490
491         list_for_each_entry(z, &cfg_state->zones, list)
492         {
493                 if (strcmp(z->name, zone))
494                         continue;
495
496                 list_for_each_entry(d, &z->devices, list)
497                 {
498                         if (device && strcmp(device, d->name))
499                                 continue;
500
501                         printf("%s\n", d->name);
502
503                         if (device)
504                                 return 0;
505                 }
506
507                 if (!device)
508                         return 0;
509         }
510
511         return 1;
512 }
513
514 static int
515 usage(void)
516 {
517         fprintf(stderr, "fw3 [-4] [-6] [-q] print\n");
518         fprintf(stderr, "fw3 [-q] {start|stop|flush|reload|restart}\n");
519         fprintf(stderr, "fw3 [-q] network {net}\n");
520         fprintf(stderr, "fw3 [-q] device {dev}\n");
521         fprintf(stderr, "fw3 [-q] zone {zone} [dev]\n");
522
523         return 1;
524 }
525
526
527 int main(int argc, char **argv)
528 {
529         int ch, rv = 1;
530         enum fw3_family family = FW3_FAMILY_ANY;
531         struct fw3_defaults *defs = NULL;
532
533         while ((ch = getopt(argc, argv, "46dqh")) != -1)
534         {
535                 switch (ch)
536                 {
537                 case '4':
538                         family = FW3_FAMILY_V4;
539                         break;
540
541                 case '6':
542                         family = FW3_FAMILY_V6;
543                         break;
544
545                 case 'd':
546                         fw3_pr_debug = true;
547                         break;
548
549                 case 'q':
550                         if (freopen("/dev/null", "w", stderr)) {}
551                         break;
552
553                 case 'h':
554                         rv = usage();
555                         goto out;
556                 }
557         }
558
559         build_state(false);
560         defs = &cfg_state->defaults;
561
562         if (optind >= argc)
563         {
564                 rv = usage();
565                 goto out;
566         }
567
568         if (!strcmp(argv[optind], "print"))
569         {
570                 if (family == FW3_FAMILY_ANY)
571                 {
572                         family = FW3_FAMILY_V4;
573                 }
574                 else if (family == FW3_FAMILY_V6)
575                 {
576                         if (defs->disable_ipv6)
577                                 warn("IPv6 rules globally disabled in configuration");
578 #ifdef DISABLE_IPV6
579                         else
580                                 warn("IPv6 support is not compiled in");
581 #endif
582                 }
583
584                 if (freopen("/dev/null", "w", stderr)) {};
585
586                 cfg_state->disable_ipsets = true;
587                 print_family = family;
588                 fw3_pr_debug = true;
589
590                 if (fw3_lock())
591                 {
592                         build_state(true);
593                         rv = start();
594                         fw3_unlock();
595                 }
596         }
597         else if (!strcmp(argv[optind], "start"))
598         {
599                 if (fw3_lock())
600                 {
601                         build_state(true);
602                         rv = start();
603                         fw3_unlock();
604                 }
605         }
606         else if (!strcmp(argv[optind], "stop"))
607         {
608                 if (fw3_lock())
609                 {
610                         build_state(true);
611                         rv = stop(false);
612                         fw3_unlock();
613                 }
614         }
615         else if (!strcmp(argv[optind], "flush"))
616         {
617                 if (fw3_lock())
618                 {
619                         build_state(true);
620                         rv = stop(true);
621                         fw3_unlock();
622                 }
623         }
624         else if (!strcmp(argv[optind], "restart"))
625         {
626                 if (fw3_lock())
627                 {
628                         build_state(true);
629                         stop(true);
630                         rv = start();
631                         fw3_unlock();
632                 }
633         }
634         else if (!strcmp(argv[optind], "reload"))
635         {
636                 if (fw3_lock())
637                 {
638                         build_state(true);
639                         rv = reload();
640                         fw3_unlock();
641                 }
642         }
643         else if (!strcmp(argv[optind], "gc"))
644         {
645                 if (fw3_lock())
646                 {
647                         rv = gc();
648                         fw3_unlock();
649                 }
650         }
651         else if (!strcmp(argv[optind], "network") && (optind + 1) < argc)
652         {
653                 rv = lookup_network(argv[optind + 1]);
654         }
655         else if (!strcmp(argv[optind], "device") && (optind + 1) < argc)
656         {
657                 rv = lookup_device(argv[optind + 1]);
658         }
659         else if (!strcmp(argv[optind], "zone") && (optind + 1) < argc)
660         {
661                 rv = lookup_zone(argv[optind + 1], argv[optind + 2]);
662         }
663         else
664         {
665                 rv = usage();
666         }
667
668 out:
669         if (cfg_state)
670                 free_state(cfg_state);
671
672         if (run_state)
673                 free_state(run_state);
674
675         return rv;
676 }