2dd1f86f02c13d52c001734ddbcd70926e6e1389
[project/firewall3.git] / main.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
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 "forwards.h"
28 #include "ipsets.h"
29 #include "ubus.h"
30
31
32 static bool print_rules = false;
33 static enum fw3_family use_family = FW3_FAMILY_ANY;
34
35 static const char *families[] = {
36         "(bug)",
37         "IPv4",
38         "IPv6",
39 };
40
41 static const char *tables[] = {
42         "filter",
43         "nat",
44         "mangle",
45         "raw",
46 };
47
48
49 static struct fw3_state *
50 build_state(void)
51 {
52         struct fw3_state *state = NULL;
53         struct uci_package *p = NULL;
54
55         state = malloc(sizeof(*state));
56
57         if (!state)
58                 error("Out of memory");
59
60         memset(state, 0, sizeof(*state));
61         state->uci = uci_alloc_context();
62
63         if (!state->uci)
64                 error("Out of memory");
65
66         if (uci_load(state->uci, "firewall", &p))
67         {
68                 uci_perror(state->uci, NULL);
69                 error("Failed to load /etc/config/firewall");
70         }
71
72         if (!fw3_find_command("ipset"))
73         {
74                 warn("Unable to locate ipset utility, disabling ipset support");
75                 state->disable_ipsets = true;
76         }
77
78         fw3_load_defaults(state, p);
79         fw3_load_ipsets(state, p);
80         fw3_load_zones(state, p);
81         fw3_load_rules(state, p);
82         fw3_load_redirects(state, p);
83         fw3_load_forwards(state, p);
84
85         return state;
86 }
87
88 static void
89 free_state(struct fw3_state *state)
90 {
91         struct list_head *cur, *tmp;
92
93         list_for_each_safe(cur, tmp, &state->zones)
94                 fw3_free_zone((struct fw3_zone *)cur);
95
96         list_for_each_safe(cur, tmp, &state->rules)
97                 fw3_free_rule((struct fw3_rule *)cur);
98
99         list_for_each_safe(cur, tmp, &state->redirects)
100                 fw3_free_redirect((struct fw3_redirect *)cur);
101
102         list_for_each_safe(cur, tmp, &state->forwards)
103                 fw3_free_forward((struct fw3_forward *)cur);
104
105         uci_free_context(state->uci);
106
107         free(state);
108
109         fw3_ubus_disconnect();
110 }
111
112
113 static bool
114 restore_pipe(enum fw3_family family, bool silent)
115 {
116         const char *cmd[] = {
117                 "(bug)",
118                 "iptables-restore",
119                 "ip6tables-restore",
120         };
121
122         if (print_rules)
123                 return fw3_stdout_pipe();
124
125         if (!fw3_command_pipe(silent, cmd[family], "--lenient", "--noflush"))
126         {
127                 warn("Unable to execute %s", cmd[family]);
128                 return false;
129         }
130
131         return true;
132 }
133
134 static bool
135 family_running(struct list_head *statefile, enum fw3_family family)
136 {
137         struct fw3_statefile_entry *e;
138
139         if (statefile)
140         {
141                 list_for_each_entry(e, statefile, list)
142                 {
143                         if (e->type != FW3_TYPE_DEFAULTS)
144                                 continue;
145
146                         return hasbit(e->flags[0], family);
147                 }
148         }
149
150         return false;
151 }
152
153 static bool
154 family_used(enum fw3_family family)
155 {
156         return (use_family == FW3_FAMILY_ANY) || (use_family == family);
157 }
158
159 static bool
160 family_loaded(struct fw3_state *state, enum fw3_family family)
161 {
162         return hasbit(state->defaults.flags, family);
163 }
164
165 static void
166 family_set(struct fw3_state *state, enum fw3_family family, bool set)
167 {
168         if (set)
169                 setbit(state->defaults.flags, family);
170         else
171                 delbit(state->defaults.flags, family);
172 }
173
174 static int
175 stop(struct fw3_state *state, bool complete, bool restart)
176 {
177         int rv = 1;
178         enum fw3_family family;
179         enum fw3_table table;
180
181         struct list_head *statefile = fw3_read_statefile();
182
183         if (!complete && !statefile)
184         {
185                 if (!restart)
186                         warn("The firewall appears to be stopped. "
187                                  "Use the 'flush' command to forcefully purge all rules.");
188
189                 return rv;
190         }
191
192         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
193         {
194                 if (!complete && !family_running(statefile, family))
195                         continue;
196
197                 if (!family_used(family) || !restore_pipe(family, true))
198                         continue;
199
200                 info("Removing %s rules ...", families[family]);
201
202                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
203                 {
204                         if (!fw3_has_table(family == FW3_FAMILY_V6, tables[table]))
205                                 continue;
206
207                         info(" * %sing %s table",
208                              complete ? "Flush" : "Clear", tables[table]);
209
210                         fw3_pr("*%s\n", tables[table]);
211
212                         if (complete)
213                         {
214                                 fw3_flush_all(table);
215                         }
216                         else
217                         {
218                                 /* pass 1 */
219                                 fw3_flush_rules(table, family, false, statefile);
220                                 fw3_flush_zones(table, family, false, statefile);
221
222                                 /* pass 2 */
223                                 fw3_flush_rules(table, family, true, statefile);
224                                 fw3_flush_zones(table, family, true, statefile);
225                         }
226
227                         fw3_pr("COMMIT\n");
228                 }
229
230                 fw3_command_close();
231
232                 if (!restart)
233                         family_set(state, family, false);
234
235                 rv = 0;
236         }
237
238         if (!restart &&
239             !family_loaded(state, FW3_FAMILY_V4) &&
240             !family_loaded(state, FW3_FAMILY_V6) &&
241             fw3_command_pipe(false, "ipset", "-exist", "-"))
242         {
243                 fw3_destroy_ipsets(statefile);
244                 fw3_command_close();
245         }
246
247         fw3_free_statefile(statefile);
248
249         if (!rv)
250                 fw3_write_statefile(state);
251
252         return rv;
253 }
254
255 static int
256 start(struct fw3_state *state, bool restart)
257 {
258         int rv = 1;
259         enum fw3_family family;
260         enum fw3_table table;
261
262         struct list_head *statefile = fw3_read_statefile();
263
264         if (!print_rules && !restart &&
265             fw3_command_pipe(false, "ipset", "-exist", "-"))
266         {
267                 fw3_create_ipsets(state);
268                 fw3_command_close();
269         }
270
271         for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
272         {
273                 if (!family_used(family))
274                         continue;
275
276                 if (!family_loaded(state, family) || !restore_pipe(family, false))
277                         continue;
278
279                 if (!restart && family_running(statefile, family))
280                 {
281                         warn("The %s firewall appears to be started already. "
282                              "If it is indeed empty, remove the %s file and retry.",
283                              families[family], FW3_STATEFILE);
284
285                         continue;
286                 }
287
288                 info("Constructing %s rules ...", families[family]);
289
290                 for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
291                 {
292                         if (!fw3_has_table(family == FW3_FAMILY_V6, tables[table]))
293                                 continue;
294
295                         info(" * Populating %s table", tables[table]);
296
297                         fw3_pr("*%s\n", tables[table]);
298                         fw3_print_default_chains(table, family, state);
299                         fw3_print_zone_chains(table, family, state);
300                         fw3_print_default_head_rules(table, family, state);
301                         fw3_print_rules(table, family, state);
302                         fw3_print_redirects(table, family, state);
303                         fw3_print_forwards(table, family, state);
304                         fw3_print_zone_rules(table, family, state);
305                         fw3_print_default_tail_rules(table, family, state);
306                         fw3_pr("COMMIT\n");
307                 }
308
309                 fw3_command_close();
310                 family_set(state, family, true);
311
312                 rv = 0;
313         }
314
315         fw3_free_statefile(statefile);
316
317         if (!rv)
318                 fw3_write_statefile(state);
319
320         return rv;
321 }
322
323 static int
324 lookup_network(struct fw3_state *state, const char *net)
325 {
326         struct fw3_zone *z;
327         struct fw3_device *d;
328
329         list_for_each_entry(z, &state->zones, list)
330         {
331                 list_for_each_entry(d, &z->networks, list)
332                 {
333                         if (!strcmp(d->name, net))
334                         {
335                                 printf("%s\n", z->name);
336                                 return 0;
337                         }
338                 }
339         }
340
341         return 1;
342 }
343
344 static int
345 lookup_device(struct fw3_state *state, const char *dev)
346 {
347         struct fw3_zone *z;
348         struct fw3_device *d;
349
350         list_for_each_entry(z, &state->zones, list)
351         {
352                 list_for_each_entry(d, &z->devices, list)
353                 {
354                         if (!strcmp(d->name, dev))
355                         {
356                                 printf("%s\n", z->name);
357                                 return 0;
358                         }
359                 }
360         }
361
362         return 1;
363 }
364
365 static int
366 usage(void)
367 {
368         fprintf(stderr, "fw3 [-4] [-6] [-q] {start|stop|flush|restart|print}\n");
369         fprintf(stderr, "fw3 [-q] network {net}\n");
370         fprintf(stderr, "fw3 [-q] device {dev}\n");
371
372         return 1;
373 }
374
375
376 int main(int argc, char **argv)
377 {
378         int ch, rv = 1;
379         struct fw3_state *state = NULL;
380         struct fw3_defaults *defs = NULL;
381
382         while ((ch = getopt(argc, argv, "46qh")) != -1)
383         {
384                 switch (ch)
385                 {
386                 case '4':
387                         use_family = FW3_FAMILY_V4;
388                         break;
389
390                 case '6':
391                         use_family = FW3_FAMILY_V6;
392                         break;
393
394                 case 'q':
395                         freopen("/dev/null", "w", stderr);
396                         break;
397
398                 case 'h':
399                         rv = usage();
400                         goto out;
401                 }
402         }
403
404         if (!fw3_ubus_connect())
405                 error("Failed to connect to ubus");
406
407         state = build_state();
408         defs = &state->defaults;
409
410         if (!fw3_lock())
411                 goto out;
412
413         if (optind >= argc)
414         {
415                 rv = usage();
416                 goto out;
417         }
418
419         if (use_family == FW3_FAMILY_V6 && defs->disable_ipv6)
420                 warn("IPv6 rules globally disabled in configuration");
421
422         if (!strcmp(argv[optind], "print"))
423         {
424                 freopen("/dev/null", "w", stderr);
425
426                 state->disable_ipsets = true;
427                 print_rules = true;
428
429                 rv = start(state, false);
430         }
431         else if (!strcmp(argv[optind], "start"))
432         {
433                 rv = start(state, false);
434         }
435         else if (!strcmp(argv[optind], "stop"))
436         {
437                 rv = stop(state, false, false);
438         }
439         else if (!strcmp(argv[optind], "flush"))
440         {
441                 rv = stop(state, true, false);
442         }
443         else if (!strcmp(argv[optind], "restart"))
444         {
445                 rv = stop(state, false, true);
446                 rv = start(state, !rv);
447         }
448         else if (!strcmp(argv[optind], "network") && (optind + 1) < argc)
449         {
450                 rv = lookup_network(state, argv[optind + 1]);
451         }
452         else if (!strcmp(argv[optind], "device") && (optind + 1) < argc)
453         {
454                 rv = lookup_device(state, argv[optind + 1]);
455         }
456         else
457         {
458                 rv = usage();
459         }
460
461 out:
462         if (state)
463                 free_state(state);
464
465         fw3_unlock();
466
467         return rv;
468 }