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