c81125f0eb71f8dd66636f64f689bd125ce21953
[project/firewall3.git] / zones.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 "zones.h"
20 #include "ubus.h"
21
22
23 #define C(f, tbl, tgt, name) \
24         { FW3_FAMILY_##f, FW3_TABLE_##tbl, FW3_TARGET_##tgt, name }
25
26 struct chain {
27         enum fw3_family family;
28         enum fw3_table table;
29         enum fw3_target target;
30         const char *name;
31 };
32
33 static const struct chain src_chains[] = {
34         C(ANY, FILTER, UNSPEC,  "zone_%s_input"),
35         C(ANY, FILTER, UNSPEC,  "zone_%s_output"),
36         C(ANY, FILTER, UNSPEC,  "zone_%s_forward"),
37
38         C(ANY, FILTER, ACCEPT,  "zone_%s_src_ACCEPT"),
39         C(ANY, FILTER, REJECT,  "zone_%s_src_REJECT"),
40         C(ANY, FILTER, DROP,    "zone_%s_src_DROP"),
41 };
42
43 static const struct chain dst_chains[] = {
44         C(ANY, FILTER, ACCEPT,  "zone_%s_dest_ACCEPT"),
45         C(ANY, FILTER, REJECT,  "zone_%s_dest_REJECT"),
46         C(ANY, FILTER, DROP,    "zone_%s_dest_DROP"),
47
48         C(V4,  NAT,    SNAT,    "zone_%s_postrouting"),
49         C(V4,  NAT,    DNAT,    "zone_%s_prerouting"),
50
51         C(ANY, FILTER, CUSTOM_CHAINS, "input_%s_rule"),
52         C(ANY, FILTER, CUSTOM_CHAINS, "output_%s_rule"),
53         C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_%s_rule"),
54
55         C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
56         C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),
57 };
58
59 const struct fw3_option fw3_zone_opts[] = {
60         FW3_OPT("enabled",             bool,     zone,     enabled),
61
62         FW3_OPT("name",                string,   zone,     name),
63         FW3_OPT("family",              family,   zone,     family),
64
65         FW3_LIST("network",            device,   zone,     networks),
66         FW3_LIST("device",             device,   zone,     devices),
67         FW3_LIST("subnet",             address,  zone,     subnets),
68
69         FW3_OPT("input",               target,   zone,     policy_input),
70         FW3_OPT("forward",             target,   zone,     policy_forward),
71         FW3_OPT("output",              target,   zone,     policy_output),
72
73         FW3_OPT("masq",                bool,     zone,     masq),
74         FW3_LIST("masq_src",           address,  zone,     masq_src),
75         FW3_LIST("masq_dest",          address,  zone,     masq_dest),
76
77         FW3_OPT("extra",               string,   zone,     extra_src),
78         FW3_OPT("extra_src",           string,   zone,     extra_src),
79         FW3_OPT("extra_dest",          string,   zone,     extra_dest),
80
81         FW3_OPT("conntrack",           bool,     zone,     conntrack),
82         FW3_OPT("mtu_fix",             bool,     zone,     mtu_fix),
83         FW3_OPT("custom_chains",       bool,     zone,     custom_chains),
84
85         FW3_OPT("log",                 bool,     zone,     log),
86         FW3_OPT("log_limit",           limit,    zone,     log_limit),
87
88         { }
89 };
90
91
92 static bool
93 print_chains(enum fw3_table table, enum fw3_family family,
94              const char *fmt, const char *name, uint16_t targets,
95              const struct chain *chains, int n)
96 {
97         bool rv = false;
98         char cn[128] = { 0 };
99         const struct chain *c;
100
101         for (c = chains; n > 0; c++, n--)
102         {
103                 if (!fw3_is_family(c, family))
104                         continue;
105
106                 if (c->table != table)
107                         continue;
108
109                 if ((c->target != FW3_TARGET_UNSPEC) && !hasbit(targets, c->target))
110                         continue;
111
112                 snprintf(cn, sizeof(cn), c->name, name);
113                 fw3_pr(fmt, cn);
114
115                 rv = true;
116         }
117
118         return rv;
119 }
120
121 static void
122 check_policy(struct uci_element *e, enum fw3_target *pol, enum fw3_target def,
123              const char *name)
124 {
125         if (*pol == FW3_TARGET_UNSPEC)
126         {
127                 warn_elem(e, "has no %s policy specified, using default", name);
128                 *pol = def;
129         }
130         else if (*pol > FW3_TARGET_DROP)
131         {
132                 warn_elem(e, "has invalid %s policy, using default", name);
133                 *pol = def;
134         }
135 }
136
137 static void
138 resolve_networks(struct uci_element *e, struct fw3_zone *zone)
139 {
140         struct fw3_device *net, *tmp;
141
142         list_for_each_entry(net, &zone->networks, list)
143         {
144                 tmp = fw3_ubus_device(net->name);
145
146                 if (!tmp)
147                 {
148                         warn_elem(e, "cannot resolve device of network '%s'", net->name);
149                         continue;
150                 }
151
152                 list_add_tail(&tmp->list, &zone->devices);
153         }
154 }
155
156 struct fw3_zone *
157 fw3_alloc_zone(void)
158 {
159         struct fw3_zone *zone;
160
161         zone = malloc(sizeof(*zone));
162
163         if (!zone)
164                 return NULL;
165
166         memset(zone, 0, sizeof(*zone));
167
168         INIT_LIST_HEAD(&zone->networks);
169         INIT_LIST_HEAD(&zone->devices);
170         INIT_LIST_HEAD(&zone->subnets);
171         INIT_LIST_HEAD(&zone->masq_src);
172         INIT_LIST_HEAD(&zone->masq_dest);
173
174         zone->enabled = true;
175         zone->custom_chains = true;
176         zone->log_limit.rate = 10;
177
178         return zone;
179 }
180
181 void
182 fw3_load_zones(struct fw3_state *state, struct uci_package *p)
183 {
184         struct uci_section *s;
185         struct uci_element *e;
186         struct fw3_zone *zone;
187         struct fw3_defaults *defs = &state->defaults;
188
189         INIT_LIST_HEAD(&state->zones);
190
191         uci_foreach_element(&p->sections, e)
192         {
193                 s = uci_to_section(e);
194
195                 if (strcmp(s->type, "zone"))
196                         continue;
197
198                 zone = fw3_alloc_zone();
199
200                 if (!zone)
201                         continue;
202
203                 fw3_parse_options(zone, fw3_zone_opts, s);
204
205                 if (!zone->enabled)
206                 {
207                         fw3_free_zone(zone);
208                         continue;
209                 }
210
211                 if (!zone->extra_dest)
212                         zone->extra_dest = zone->extra_src;
213
214                 if (!defs->custom_chains && zone->custom_chains)
215                         zone->custom_chains = false;
216
217                 if (!zone->name || !*zone->name)
218                 {
219                         warn_elem(e, "has no name - ignoring");
220                         fw3_free_zone(zone);
221                         continue;
222                 }
223
224                 if (list_empty(&zone->networks) && list_empty(&zone->devices) &&
225                     list_empty(&zone->subnets) && !zone->extra_src)
226                 {
227                         warn_elem(e, "has no device, network, subnet or extra options");
228                 }
229
230                 check_policy(e, &zone->policy_input, defs->policy_input, "input");
231                 check_policy(e, &zone->policy_output, defs->policy_output, "output");
232                 check_policy(e, &zone->policy_forward, defs->policy_forward, "forward");
233
234                 resolve_networks(e, zone);
235
236                 if (zone->masq)
237                 {
238                         setbit(zone->dst_flags, FW3_TARGET_SNAT);
239                         zone->conntrack = true;
240                 }
241
242                 if (zone->custom_chains)
243                 {
244                         setbit(zone->dst_flags, FW3_TARGET_SNAT);
245                         setbit(zone->dst_flags, FW3_TARGET_DNAT);
246                 }
247
248                 setbit(zone->src_flags, zone->policy_input);
249                 setbit(zone->dst_flags, zone->policy_output);
250                 setbit(zone->dst_flags, zone->policy_forward);
251
252                 list_add_tail(&zone->list, &state->zones);
253         }
254 }
255
256
257 static void
258 print_zone_chain(enum fw3_table table, enum fw3_family family,
259                  struct fw3_zone *zone, struct fw3_state *state)
260 {
261         bool s, d;
262         uint16_t mask = ~0;
263
264         if (!fw3_is_family(zone, family))
265                 return;
266
267         setbit(zone->dst_flags, family);
268
269         /* user chains already loaded, don't create again */
270         if (hasbit(zone->dst_flags, FW3_TARGET_CUSTOM_CHAINS))
271                 delbit(mask, FW3_TARGET_CUSTOM_CHAINS);
272
273         if (zone->custom_chains)
274                 setbit(zone->dst_flags, FW3_TARGET_CUSTOM_CHAINS);
275
276         if (!zone->conntrack && !state->defaults.drop_invalid)
277                 setbit(zone->dst_flags, FW3_TARGET_NOTRACK);
278
279         s = print_chains(table, family, ":%s - [0:0]\n", zone->name,
280                          zone->src_flags & mask,
281                          src_chains, ARRAY_SIZE(src_chains));
282
283         d = print_chains(table, family, ":%s - [0:0]\n", zone->name,
284                          zone->dst_flags & mask,
285                          dst_chains, ARRAY_SIZE(dst_chains));
286
287         if (zone->custom_chains)
288         {
289                 if (table == FW3_TABLE_FILTER)
290                 {
291                         fw3_pr("-A zone_%s_input -j input_%s_rule "
292                                    "-m comment --comment \"user chain for %s input\"\n",
293                                zone->name, zone->name, zone->name);
294
295                         fw3_pr("-A zone_%s_output -j output_%s_rule "
296                                    "-m comment --comment \"user chain for %s output\"\n",
297                                zone->name, zone->name, zone->name);
298
299                         fw3_pr("-A zone_%s_forward -j forwarding_%s_rule "
300                                    "-m comment --comment \"user chain for %s forwarding\"\n",
301                                zone->name, zone->name, zone->name);
302                 }
303                 else if (table == FW3_TABLE_NAT)
304                 {
305                         fw3_pr("-A zone_%s_prerouting -j prerouting_%s_rule "
306                                "-m comment --comment \"user chain for %s prerouting\"\n",
307                                zone->name, zone->name, zone->name);
308
309                         fw3_pr("-A zone_%s_postrouting -j postrouting_%s_rule "
310                                "-m comment --comment \"user chain for %s postrouting\"\n",
311                                zone->name, zone->name, zone->name);
312                 }
313         }
314
315         if (s || d)
316         {
317                 info("   * Zone '%s'", zone->name);
318                 fw3_set_running(zone, &state->running_zones);
319         }
320 }
321
322 static void
323 print_interface_rule(enum fw3_table table, enum fw3_family family,
324                      struct fw3_zone *zone, struct fw3_device *dev,
325                      struct fw3_address *sub, bool disable_notrack)
326 {
327         enum fw3_target t;
328
329 #define jump_target(t) \
330         ((t == FW3_TARGET_REJECT) ? "reject" : fw3_flag_names[t])
331
332         if (table == FW3_TABLE_FILTER)
333         {
334                 for (t = FW3_TARGET_ACCEPT; t <= FW3_TARGET_DROP; t++)
335                 {
336                         if (hasbit(zone->src_flags, t))
337                         {
338                                 fw3_pr("-A zone_%s_src_%s", zone->name, fw3_flag_names[t]);
339                                 fw3_format_in_out(dev, NULL);
340                                 fw3_format_src_dest(sub, NULL);
341                                 fw3_format_extra(zone->extra_src);
342                                 fw3_pr(" -j %s\n", jump_target(t));
343                         }
344
345                         if (hasbit(zone->dst_flags, t))
346                         {
347                                 fw3_pr("-A zone_%s_dest_%s", zone->name, fw3_flag_names[t]);
348                                 fw3_format_in_out(NULL, dev);
349                                 fw3_format_src_dest(NULL, sub);
350                                 fw3_format_extra(zone->extra_dest);
351                                 fw3_pr(" -j %s\n", jump_target(t));
352                         }
353                 }
354
355                 fw3_pr("-A delegate_input");
356                 fw3_format_in_out(dev, NULL);
357                 fw3_format_src_dest(sub, NULL);
358                 fw3_format_extra(zone->extra_src);
359                 fw3_pr(" -j zone_%s_input\n", zone->name);
360
361                 fw3_pr("-A delegate_forward");
362                 fw3_format_in_out(dev, NULL);
363                 fw3_format_src_dest(sub, NULL);
364                 fw3_format_extra(zone->extra_src);
365                 fw3_pr(" -j zone_%s_forward\n", zone->name);
366
367                 fw3_pr("-A delegate_output");
368                 fw3_format_in_out(NULL, dev);
369                 fw3_format_src_dest(NULL, sub);
370                 fw3_format_extra(zone->extra_dest);
371                 fw3_pr(" -j zone_%s_output\n", zone->name);
372         }
373         else if (table == FW3_TABLE_NAT)
374         {
375                 if (hasbit(zone->dst_flags, FW3_TARGET_DNAT))
376                 {
377                         fw3_pr("-A delegate_prerouting");
378                         fw3_format_in_out(dev, NULL);
379                         fw3_format_src_dest(sub, NULL);
380                         fw3_format_extra(zone->extra_src);
381                         fw3_pr(" -j zone_%s_prerouting\n", zone->name);
382                 }
383
384                 if (hasbit(zone->dst_flags, FW3_TARGET_SNAT))
385                 {
386                         fw3_pr("-A delegate_postrouting");
387                         fw3_format_in_out(NULL, dev);
388                         fw3_format_src_dest(NULL, sub);
389                         fw3_format_extra(zone->extra_dest);
390                         fw3_pr(" -j zone_%s_postrouting\n", zone->name);
391                 }
392         }
393         else if (table == FW3_TABLE_MANGLE)
394         {
395                 if (zone->mtu_fix)
396                 {
397                         if (zone->log)
398                         {
399                                 fw3_pr("-A mssfix");
400                                 fw3_format_in_out(NULL, dev);
401                                 fw3_format_src_dest(NULL, sub);
402                                 fw3_pr(" -p tcp --tcp-flags SYN,RST SYN");
403                                 fw3_format_limit(&zone->log_limit);
404                                 fw3_format_comment(zone->name, " (mtu_fix logging)");
405                                 fw3_pr(" -j LOG --log-prefix \"MSSFIX(%s): \"\n", zone->name);
406                         }
407
408                         fw3_pr("-A mssfix");
409                         fw3_format_in_out(NULL, dev);
410                         fw3_format_src_dest(NULL, sub);
411                         fw3_pr(" -p tcp --tcp-flags SYN,RST SYN");
412                         fw3_format_comment(zone->name, " (mtu_fix)");
413                         fw3_pr(" -j TCPMSS --clamp-mss-to-pmtu\n");
414                 }
415         }
416         else if (table == FW3_TABLE_RAW)
417         {
418                 if (!zone->conntrack && !disable_notrack)
419                 {
420                         fw3_pr("-A notrack");
421                         fw3_format_in_out(dev, NULL);
422                         fw3_format_src_dest(sub, NULL);
423                         fw3_format_extra(zone->extra_src);
424                         fw3_format_comment(zone->name, " (notrack)");
425                         fw3_pr(" -j CT --notrack\n", zone->name);
426                 }
427         }
428 }
429
430 static void
431 print_interface_rules(enum fw3_table table, enum fw3_family family,
432                       struct fw3_zone *zone, bool disable_notrack)
433 {
434         struct fw3_device *dev;
435         struct fw3_address *sub;
436
437         fw3_foreach(dev, &zone->devices)
438         fw3_foreach(sub, &zone->subnets)
439         {
440                 if (!fw3_is_family(sub, family))
441                         continue;
442
443                 if (!dev && !sub)
444                         continue;
445
446                 print_interface_rule(table, family, zone, dev, sub, disable_notrack);
447         }
448 }
449
450 static void
451 print_zone_rule(enum fw3_table table, enum fw3_family family,
452                 struct fw3_zone *zone, bool disable_notrack)
453 {
454         struct fw3_address *msrc;
455         struct fw3_address *mdest;
456
457         enum fw3_target t;
458
459         if (!fw3_is_family(zone, family))
460                 return;
461
462         switch (table)
463         {
464         case FW3_TABLE_FILTER:
465                 fw3_pr("-A zone_%s_input -j zone_%s_src_%s\n",
466                            zone->name, zone->name, fw3_flag_names[zone->policy_input]);
467
468                 fw3_pr("-A zone_%s_forward -j zone_%s_dest_%s\n",
469                            zone->name, zone->name, fw3_flag_names[zone->policy_forward]);
470
471                 fw3_pr("-A zone_%s_output -j zone_%s_dest_%s\n",
472                            zone->name, zone->name, fw3_flag_names[zone->policy_output]);
473
474                 if (zone->log)
475                 {
476                         for (t = FW3_TARGET_REJECT; t <= FW3_TARGET_DROP; t++)
477                         {
478                                 if (hasbit(zone->src_flags, t))
479                                 {
480                                         fw3_pr("-A zone_%s_src_%s", zone->name, fw3_flag_names[t]);
481                                         fw3_format_limit(&zone->log_limit);
482                                         fw3_pr(" -j LOG --log-prefix \"%s(src %s)\"\n",
483                                                    fw3_flag_names[t], zone->name);
484                                 }
485
486                                 if (hasbit(zone->dst_flags, t))
487                                 {
488                                         fw3_pr("-A zone_%s_dest_%s", zone->name, fw3_flag_names[t]);
489                                         fw3_format_limit(&zone->log_limit);
490                                         fw3_pr(" -j LOG --log-prefix \"%s(dest %s)\"\n",
491                                                    fw3_flag_names[t], zone->name);
492                                 }
493                         }
494                 }
495                 break;
496
497         case FW3_TABLE_NAT:
498                 if (zone->masq && family == FW3_FAMILY_V4)
499                 {
500                         fw3_foreach(msrc, &zone->masq_src)
501                         fw3_foreach(mdest, &zone->masq_dest)
502                         {
503                                 fw3_pr("-A zone_%s_postrouting ", zone->name);
504                                 fw3_format_src_dest(msrc, mdest);
505                                 fw3_pr("-j MASQUERADE\n");
506                         }
507                 }
508                 break;
509
510         case FW3_TABLE_RAW:
511         case FW3_TABLE_MANGLE:
512                 break;
513         }
514
515         print_interface_rules(table, family, zone, disable_notrack);
516 }
517
518 void
519 fw3_print_zone_chains(enum fw3_table table, enum fw3_family family,
520                       struct fw3_state *state)
521 {
522         struct fw3_zone *zone;
523
524         list_for_each_entry(zone, &state->zones, list)
525                 print_zone_chain(table, family, zone, state);
526 }
527
528 void
529 fw3_print_zone_rules(enum fw3_table table, enum fw3_family family,
530                      struct fw3_state *state)
531 {
532         struct fw3_zone *zone;
533
534         list_for_each_entry(zone, &state->zones, list)
535                 print_zone_rule(table, family, zone, state->defaults.drop_invalid);
536 }
537
538 void
539 fw3_flush_zones(enum fw3_table table, enum fw3_family family,
540                             bool pass2, bool reload, struct fw3_state *state)
541 {
542         struct fw3_zone *z, *tmp;
543         uint16_t mask = ~0;
544         uint16_t families = (1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6);
545
546         /* don't touch user chains on selective stop */
547         if (reload)
548                 delbit(mask, FW3_DEFAULT_CUSTOM_CHAINS);
549
550         list_for_each_entry_safe(z, tmp, &state->running_zones, running_list)
551         {
552                 if (!hasbit(z->dst_flags, family))
553                         continue;
554
555                 print_chains(table, family, pass2 ? "-X %s\n" : "-F %s\n",
556                              z->name, z->src_flags & mask,
557                              src_chains, ARRAY_SIZE(src_chains));
558
559                 print_chains(table, family, pass2 ? "-X %s\n" : "-F %s\n",
560                              z->name, z->dst_flags & mask,
561                              dst_chains, ARRAY_SIZE(dst_chains));
562
563                 if (pass2)
564                 {
565                         delbit(z->dst_flags, family);
566
567                         if (!(z->dst_flags & families))
568                                 fw3_set_running(z, NULL);
569                 }
570         }
571 }
572
573 struct fw3_zone *
574 fw3_lookup_zone(struct fw3_state *state, const char *name, bool running)
575 {
576         struct fw3_zone *z;
577
578         if (list_empty(&state->zones))
579                 return NULL;
580
581         list_for_each_entry(z, &state->zones, list)
582         {
583                 if (strcmp(z->name, name))
584                         continue;
585
586                 if (!running || z->running_list.next)
587                         return z;
588
589                 break;
590         }
591
592         return NULL;
593 }