defaults: add support for xt_FLOWOFFLOAD rule
[project/firewall3.git] / zones.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013 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 "zones.h"
20 #include "ubus.h"
21 #include "helpers.h"
22
23
24 #define C(f, tbl, tgt, fmt) \
25         { FW3_FAMILY_##f, FW3_TABLE_##tbl, FW3_FLAG_##tgt, fmt }
26
27 static const struct fw3_chain_spec zone_chains[] = {
28         C(ANY, FILTER, UNSPEC,        "zone_%s_input"),
29         C(ANY, FILTER, UNSPEC,        "zone_%s_output"),
30         C(ANY, FILTER, UNSPEC,        "zone_%s_forward"),
31
32         C(ANY, FILTER, SRC_ACCEPT,    "zone_%s_src_ACCEPT"),
33         C(ANY, FILTER, SRC_REJECT,    "zone_%s_src_REJECT"),
34         C(ANY, FILTER, SRC_DROP,      "zone_%s_src_DROP"),
35
36         C(ANY, FILTER, ACCEPT,        "zone_%s_dest_ACCEPT"),
37         C(ANY, FILTER, REJECT,        "zone_%s_dest_REJECT"),
38         C(ANY, FILTER, DROP,          "zone_%s_dest_DROP"),
39
40         C(V4,  NAT,    SNAT,          "zone_%s_postrouting"),
41         C(V4,  NAT,    DNAT,          "zone_%s_prerouting"),
42
43         C(ANY, RAW,    HELPER,        "zone_%s_helper"),
44         C(ANY, RAW,    NOTRACK,       "zone_%s_notrack"),
45
46         C(ANY, FILTER, CUSTOM_CHAINS, "input_%s_rule"),
47         C(ANY, FILTER, CUSTOM_CHAINS, "output_%s_rule"),
48         C(ANY, FILTER, CUSTOM_CHAINS, "forwarding_%s_rule"),
49
50         C(V4,  NAT,    CUSTOM_CHAINS, "prerouting_%s_rule"),
51         C(V4,  NAT,    CUSTOM_CHAINS, "postrouting_%s_rule"),
52
53         { }
54 };
55
56 enum fw3_zone_logmask {
57         FW3_ZONE_LOG_FILTER = (1 << 0),
58         FW3_ZONE_LOG_MANGLE = (1 << 1),
59 };
60
61 const struct fw3_option fw3_zone_opts[] = {
62         FW3_OPT("enabled",             bool,     zone,     enabled),
63
64         FW3_OPT("name",                string,   zone,     name),
65         FW3_OPT("family",              family,   zone,     family),
66
67         FW3_LIST("network",            device,   zone,     networks),
68         FW3_LIST("device",             device,   zone,     devices),
69         FW3_LIST("subnet",             network,  zone,     subnets),
70
71         FW3_OPT("input",               target,   zone,     policy_input),
72         FW3_OPT("forward",             target,   zone,     policy_forward),
73         FW3_OPT("output",              target,   zone,     policy_output),
74
75         FW3_OPT("masq",                bool,     zone,     masq),
76         FW3_OPT("masq_allow_invalid",  bool,     zone,     masq_allow_invalid),
77         FW3_LIST("masq_src",           network,  zone,     masq_src),
78         FW3_LIST("masq_dest",          network,  zone,     masq_dest),
79
80         FW3_OPT("extra",               string,   zone,     extra_src),
81         FW3_OPT("extra_src",           string,   zone,     extra_src),
82         FW3_OPT("extra_dest",          string,   zone,     extra_dest),
83
84         FW3_OPT("mtu_fix",             bool,     zone,     mtu_fix),
85         FW3_OPT("custom_chains",       bool,     zone,     custom_chains),
86
87         FW3_OPT("log",                 int,      zone,     log),
88         FW3_OPT("log_limit",           limit,    zone,     log_limit),
89
90         FW3_OPT("auto_helper",         bool,     zone,     auto_helper),
91         FW3_LIST("helper",             cthelper, zone,     cthelpers),
92
93         FW3_OPT("__flags_v4",          int,      zone,     flags[0]),
94         FW3_OPT("__flags_v6",          int,      zone,     flags[1]),
95
96         FW3_LIST("__addrs",            address,  zone,     old_addrs),
97
98         { }
99 };
100
101
102 static void
103 check_policy(struct uci_element *e, enum fw3_flag *pol, enum fw3_flag def,
104              const char *name)
105 {
106         if (*pol == FW3_FLAG_UNSPEC)
107         {
108                 warn_elem(e, "has no %s policy specified, using default", name);
109                 *pol = def;
110         }
111         else if (*pol > FW3_FLAG_DROP)
112         {
113                 warn_elem(e, "has invalid %s policy, using default", name);
114                 *pol = def;
115         }
116 }
117
118 static bool
119 check_masq_addrs(struct list_head *head)
120 {
121         struct fw3_address *addr;
122         int n_addr = 0, n_failed = 0;
123
124         list_for_each_entry(addr, head, list)
125         {
126                 if (addr->invert)
127                         continue;
128
129                 n_addr++;
130
131                 if (!addr->set && addr->resolved)
132                         n_failed++;
133         }
134
135         return (n_addr == 0 || n_failed < n_addr);
136 }
137
138 static void
139 resolve_networks(struct uci_element *e, struct fw3_zone *zone)
140 {
141         struct fw3_device *net, *tmp;
142
143         list_for_each_entry(net, &zone->networks, list)
144         {
145                 tmp = fw3_ubus_device(net->name);
146
147                 if (!tmp)
148                 {
149                         warn_elem(e, "cannot resolve device of network '%s'", net->name);
150                         continue;
151                 }
152
153                 snprintf(tmp->network, sizeof(tmp->network), "%s", net->name);
154                 list_add_tail(&tmp->list, &zone->devices);
155         }
156 }
157
158 static void
159 resolve_cthelpers(struct fw3_state *s, struct uci_element *e, struct fw3_zone *zone)
160 {
161         struct fw3_cthelpermatch *match;
162
163         if (list_empty(&zone->cthelpers))
164         {
165                 if (!zone->masq && zone->auto_helper)
166                 {
167                         fw3_setbit(zone->flags[0], FW3_FLAG_HELPER);
168                         fw3_setbit(zone->flags[1], FW3_FLAG_HELPER);
169                 }
170
171                 return;
172         }
173
174         list_for_each_entry(match, &zone->cthelpers, list)
175         {
176                 if (match->invert)
177                 {
178                         warn_elem(e, "must not use a negated helper match");
179                         continue;
180                 }
181
182                 match->ptr = fw3_lookup_cthelper(s, match->name);
183
184                 if (!match->ptr)
185                 {
186                         warn_elem(e, "refers to not existing helper '%s'", match->name);
187                         continue;
188                 }
189
190                 if (fw3_is_family(match->ptr, FW3_FAMILY_V4))
191                         fw3_setbit(zone->flags[0], FW3_FLAG_HELPER);
192
193                 if (fw3_is_family(match->ptr, FW3_FAMILY_V6))
194                         fw3_setbit(zone->flags[1], FW3_FLAG_HELPER);
195         }
196 }
197
198 struct fw3_zone *
199 fw3_alloc_zone(void)
200 {
201         struct fw3_zone *zone;
202
203         zone = calloc(1, sizeof(*zone));
204         if (!zone)
205                 return NULL;
206
207         INIT_LIST_HEAD(&zone->networks);
208         INIT_LIST_HEAD(&zone->devices);
209         INIT_LIST_HEAD(&zone->subnets);
210         INIT_LIST_HEAD(&zone->masq_src);
211         INIT_LIST_HEAD(&zone->masq_dest);
212         INIT_LIST_HEAD(&zone->cthelpers);
213
214         INIT_LIST_HEAD(&zone->old_addrs);
215
216         zone->enabled = true;
217         zone->auto_helper = true;
218         zone->custom_chains = true;
219         zone->log_limit.rate = 10;
220
221         return zone;
222 }
223
224 void
225 fw3_load_zones(struct fw3_state *state, struct uci_package *p)
226 {
227         struct uci_section *s;
228         struct uci_element *e;
229         struct fw3_zone *zone;
230         struct fw3_defaults *defs = &state->defaults;
231
232         INIT_LIST_HEAD(&state->zones);
233
234         uci_foreach_element(&p->sections, e)
235         {
236                 s = uci_to_section(e);
237
238                 if (strcmp(s->type, "zone"))
239                         continue;
240
241                 zone = fw3_alloc_zone();
242
243                 if (!zone)
244                         continue;
245
246                 if (!fw3_parse_options(zone, fw3_zone_opts, s))
247                         warn_elem(e, "has invalid options");
248
249                 if (!zone->enabled)
250                 {
251                         fw3_free_zone(zone);
252                         continue;
253                 }
254
255                 if (!zone->extra_dest)
256                         zone->extra_dest = zone->extra_src;
257
258                 if (!defs->custom_chains && zone->custom_chains)
259                         zone->custom_chains = false;
260
261                 if (!defs->auto_helper && zone->auto_helper)
262                         zone->auto_helper = false;
263
264                 if (!zone->name || !*zone->name)
265                 {
266                         warn_elem(e, "has no name - ignoring");
267                         fw3_free_zone(zone);
268                         continue;
269                 }
270
271                 if (strlen(zone->name) > FW3_ZONE_MAXNAMELEN)
272                 {
273                         warn_elem(e, "must not have a name longer than %u characters",
274                                      FW3_ZONE_MAXNAMELEN);
275                         fw3_free_zone(zone);
276                         continue;
277                 }
278
279                 fw3_ubus_zone_devices(zone);
280
281                 if (list_empty(&zone->networks) && list_empty(&zone->devices) &&
282                     list_empty(&zone->subnets) && !zone->extra_src)
283                 {
284                         warn_elem(e, "has no device, network, subnet or extra options");
285                 }
286
287                 if (!check_masq_addrs(&zone->masq_src))
288                 {
289                         warn_elem(e, "has unresolved masq_src, disabling masq");
290                         zone->masq = false;
291                 }
292
293                 if (!check_masq_addrs(&zone->masq_dest))
294                 {
295                         warn_elem(e, "has unresolved masq_dest, disabling masq");
296                         zone->masq = false;
297                 }
298
299                 check_policy(e, &zone->policy_input, defs->policy_input, "input");
300                 check_policy(e, &zone->policy_output, defs->policy_output, "output");
301                 check_policy(e, &zone->policy_forward, defs->policy_forward, "forward");
302
303                 resolve_networks(e, zone);
304
305                 if (zone->masq)
306                 {
307                         fw3_setbit(zone->flags[0], FW3_FLAG_SNAT);
308                 }
309
310                 if (zone->custom_chains)
311                 {
312                         fw3_setbit(zone->flags[0], FW3_FLAG_SNAT);
313                         fw3_setbit(zone->flags[0], FW3_FLAG_DNAT);
314                 }
315
316                 resolve_cthelpers(state, e, zone);
317
318                 fw3_setbit(zone->flags[0], fw3_to_src_target(zone->policy_input));
319                 fw3_setbit(zone->flags[0], zone->policy_forward);
320                 fw3_setbit(zone->flags[0], zone->policy_output);
321
322                 fw3_setbit(zone->flags[1], fw3_to_src_target(zone->policy_input));
323                 fw3_setbit(zone->flags[1], zone->policy_forward);
324                 fw3_setbit(zone->flags[1], zone->policy_output);
325
326                 list_add_tail(&zone->list, &state->zones);
327         }
328 }
329
330
331 static void
332 print_zone_chain(struct fw3_ipt_handle *handle, struct fw3_state *state,
333                  bool reload, struct fw3_zone *zone)
334 {
335         int i;
336         struct fw3_ipt_rule *r;
337         const struct fw3_chain_spec *c;
338
339         const char *flt_chains[] = {
340                 "input",   "input",
341                 "output",  "output",
342                 "forward", "forwarding",
343         };
344
345         const char *nat_chains[] = {
346                 "prerouting",  "prerouting",
347                 "postrouting", "postrouting",
348         };
349
350         if (!fw3_is_family(zone, handle->family))
351                 return;
352
353         set(zone->flags, handle->family, handle->table);
354
355         if (zone->custom_chains)
356                 set(zone->flags, handle->family, FW3_FLAG_CUSTOM_CHAINS);
357
358         for (c = zone_chains; c->format; c++)
359         {
360                 /* don't touch user chains on selective stop */
361                 if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
362                         continue;
363
364                 if (!fw3_is_family(c, handle->family))
365                         continue;
366
367                 if (c->table != handle->table)
368                         continue;
369
370                 if (c->flag &&
371                     !fw3_hasbit(zone->flags[handle->family == FW3_FAMILY_V6], c->flag))
372                         continue;
373
374                 fw3_ipt_create_chain(handle, c->format, zone->name);
375         }
376
377         if (zone->custom_chains)
378         {
379                 if (handle->table == FW3_TABLE_FILTER)
380                 {
381                         for (i = 0; i < sizeof(flt_chains)/sizeof(flt_chains[0]); i += 2)
382                         {
383                                 r = fw3_ipt_rule_new(handle);
384                                 fw3_ipt_rule_comment(r, "user chain for %s", flt_chains[i+1]);
385                                 fw3_ipt_rule_target(r, "%s_%s_rule", flt_chains[i+1], zone->name);
386                                 fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, flt_chains[i]);
387                         }
388                 }
389                 else if (handle->table == FW3_TABLE_NAT)
390                 {
391                         for (i = 0; i < sizeof(nat_chains)/sizeof(nat_chains[0]); i += 2)
392                         {
393                                 r = fw3_ipt_rule_new(handle);
394                                 fw3_ipt_rule_comment(r, "user chain for %s", nat_chains[i+1]);
395                                 fw3_ipt_rule_target(r, "%s_%s_rule", nat_chains[i+1], zone->name);
396                                 fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, nat_chains[i]);
397                         }
398                 }
399         }
400
401         set(zone->flags, handle->family, handle->table);
402 }
403
404 static void
405 print_interface_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
406                                          bool reload, struct fw3_zone *zone,
407                      struct fw3_device *dev, struct fw3_address *sub)
408 {
409         struct fw3_protocol tcp = { .protocol = 6 };
410         struct fw3_ipt_rule *r;
411         enum fw3_flag t;
412
413         char buf[32];
414
415         int i;
416
417         const char *chains[] = {
418                 "input", "INPUT",
419                 "output", "OUTPUT",
420                 "forward", "FORWARD",
421         };
422
423 #define jump_target(t) \
424         ((t == FW3_FLAG_REJECT) ? "reject" : fw3_flag_names[t])
425
426         if (handle->table == FW3_TABLE_FILTER)
427         {
428                 for (t = FW3_FLAG_ACCEPT; t <= FW3_FLAG_DROP; t++)
429                 {
430                         if (has(zone->flags, handle->family, fw3_to_src_target(t)))
431                         {
432                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
433                                 fw3_ipt_rule_target(r, jump_target(t));
434                                 fw3_ipt_rule_extra(r, zone->extra_src);
435
436                                 if (t == FW3_FLAG_ACCEPT && !state->defaults.drop_invalid)
437                                         fw3_ipt_rule_extra(r,
438                                                            "-m conntrack --ctstate NEW,UNTRACKED");
439
440                                 fw3_ipt_rule_replace(r, "zone_%s_src_%s", zone->name,
441                                                      fw3_flag_names[t]);
442                         }
443
444                         if (has(zone->flags, handle->family, t))
445                         {
446                                 if (t == FW3_FLAG_ACCEPT &&
447                                     zone->masq && !zone->masq_allow_invalid)
448                                 {
449                                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
450                                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate INVALID");
451                                         fw3_ipt_rule_comment(r, "Prevent NAT leakage");
452                                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_DROP]);
453                                         fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
454                                                              fw3_flag_names[t]);
455                                 }
456
457                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
458                                 fw3_ipt_rule_target(r, jump_target(t));
459                                 fw3_ipt_rule_extra(r, zone->extra_dest);
460                                 fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
461                                                      fw3_flag_names[t]);
462                         }
463                 }
464
465                 for (i = 0; i < sizeof(chains)/sizeof(chains[0]); i += 2)
466                 {
467                         if (*chains[i] == 'o')
468                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
469                         else
470                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
471
472                         fw3_ipt_rule_target(r, "zone_%s_%s", zone->name, chains[i]);
473
474                         if (*chains[i] == 'o')
475                                 fw3_ipt_rule_extra(r, zone->extra_dest);
476                         else
477                                 fw3_ipt_rule_extra(r, zone->extra_src);
478
479                         fw3_ipt_rule_replace(r, chains[i + 1]);
480                 }
481         }
482         else if (handle->table == FW3_TABLE_NAT)
483         {
484                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
485                 {
486                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
487                         fw3_ipt_rule_target(r, "zone_%s_prerouting", zone->name);
488                         fw3_ipt_rule_extra(r, zone->extra_src);
489                         fw3_ipt_rule_replace(r, "PREROUTING");
490                 }
491
492                 if (has(zone->flags, handle->family, FW3_FLAG_SNAT))
493                 {
494                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
495                         fw3_ipt_rule_target(r, "zone_%s_postrouting", zone->name);
496                         fw3_ipt_rule_extra(r, zone->extra_dest);
497                         fw3_ipt_rule_replace(r, "POSTROUTING");
498                 }
499         }
500         else if (handle->table == FW3_TABLE_MANGLE)
501         {
502                 if (zone->mtu_fix)
503                 {
504                         if (zone->log & FW3_ZONE_LOG_MANGLE)
505                         {
506                                 snprintf(buf, sizeof(buf) - 1, "MSSFIX(%s): ", zone->name);
507
508                                 r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
509                                 fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
510                                 fw3_ipt_rule_addarg(r, false, "SYN", NULL);
511                                 fw3_ipt_rule_limit(r, &zone->log_limit);
512                                 fw3_ipt_rule_comment(r, "%s (mtu_fix logging)", zone->name);
513                                 fw3_ipt_rule_target(r, "LOG");
514                                 fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
515                                 fw3_ipt_rule_replace(r, "FORWARD");
516                         }
517
518                         r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
519                         fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
520                         fw3_ipt_rule_addarg(r, false, "SYN", NULL);
521                         fw3_ipt_rule_comment(r, "%s (mtu_fix)", zone->name);
522                         fw3_ipt_rule_target(r, "TCPMSS");
523                         fw3_ipt_rule_addarg(r, false, "--clamp-mss-to-pmtu", NULL);
524                         fw3_ipt_rule_replace(r, "FORWARD");
525                 }
526         }
527         else if (handle->table == FW3_TABLE_RAW)
528         {
529                 if (has(zone->flags, handle->family, FW3_FLAG_HELPER))
530                 {
531                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
532                         fw3_ipt_rule_comment(r, "%s CT helper assignment", zone->name);
533                         fw3_ipt_rule_target(r, "zone_%s_helper", zone->name);
534                         fw3_ipt_rule_extra(r, zone->extra_src);
535                         fw3_ipt_rule_replace(r, "PREROUTING");
536                 }
537
538                 if (has(zone->flags, handle->family, FW3_FLAG_NOTRACK))
539                 {
540                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
541                         fw3_ipt_rule_comment(r, "%s CT bypass", zone->name);
542                         fw3_ipt_rule_target(r, "zone_%s_notrack", zone->name);
543                         fw3_ipt_rule_extra(r, zone->extra_src);
544                         fw3_ipt_rule_replace(r, "PREROUTING");
545                 }
546         }
547 }
548
549 static void
550 print_interface_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
551                       bool reload, struct fw3_zone *zone)
552 {
553         struct fw3_device *dev;
554         struct fw3_address *sub;
555
556         fw3_foreach(dev, &zone->devices)
557         fw3_foreach(sub, &zone->subnets)
558         {
559                 if (!fw3_is_family(sub, handle->family))
560                         continue;
561
562                 if (!dev && !sub)
563                         continue;
564
565                 print_interface_rule(handle, state, reload, zone, dev, sub);
566         }
567 }
568
569 static struct fw3_address *
570 next_addr(struct fw3_address *addr, struct list_head *list,
571                 enum fw3_family family, bool invert)
572 {
573         struct list_head *p;
574         struct fw3_address *rv;
575
576         for (p = addr ? addr->list.next : list->next; p != list; p = p->next)
577         {
578                 rv = list_entry(p, struct fw3_address, list);
579
580                 if (fw3_is_family(rv, family) && rv->set && rv->invert == invert)
581                         return rv;
582         }
583
584         return NULL;
585 }
586
587 static void
588 print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
589                 bool reload, struct fw3_zone *zone)
590 {
591         bool first_src, first_dest;
592         struct fw3_address *msrc;
593         struct fw3_address *mdest;
594         struct fw3_ipt_rule *r;
595
596         enum fw3_flag t;
597         char buf[32];
598
599         if (!fw3_is_family(zone, handle->family))
600                 return;
601
602         info("   * Zone '%s'", zone->name);
603
604         switch (handle->table)
605         {
606         case FW3_TABLE_FILTER:
607                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
608                 {
609                         r = fw3_ipt_rule_new(handle);
610                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
611                         fw3_ipt_rule_comment(r, "Accept port redirections");
612                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
613                         fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
614
615                         r = fw3_ipt_rule_new(handle);
616                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
617                         fw3_ipt_rule_comment(r, "Accept port forwards");
618                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
619                         fw3_ipt_rule_append(r, "zone_%s_forward", zone->name);
620                 }
621
622                 r = fw3_ipt_rule_new(handle);
623                 fw3_ipt_rule_target(r, "zone_%s_src_%s", zone->name,
624                                      fw3_flag_names[zone->policy_input]);
625                 fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
626
627                 r = fw3_ipt_rule_new(handle);
628                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name,
629                                      fw3_flag_names[zone->policy_forward]);
630                 fw3_ipt_rule_append(r, "zone_%s_forward", zone->name);
631
632                 r = fw3_ipt_rule_new(handle);
633                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name,
634                                      fw3_flag_names[zone->policy_output]);
635                 fw3_ipt_rule_append(r, "zone_%s_output", zone->name);
636
637                 if (zone->log & FW3_ZONE_LOG_FILTER)
638                 {
639                         for (t = FW3_FLAG_REJECT; t <= FW3_FLAG_DROP; t++)
640                         {
641                                 if (has(zone->flags, handle->family, fw3_to_src_target(t)))
642                                 {
643                                         r = fw3_ipt_rule_new(handle);
644
645                                         snprintf(buf, sizeof(buf) - 1, "%s(src %s)",
646                                                  fw3_flag_names[t], zone->name);
647
648                                         fw3_ipt_rule_limit(r, &zone->log_limit);
649                                         fw3_ipt_rule_target(r, "LOG");
650                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
651                                         fw3_ipt_rule_append(r, "zone_%s_src_%s",
652                                                             zone->name, fw3_flag_names[t]);
653                                 }
654
655                                 if (has(zone->flags, handle->family, t))
656                                 {
657                                         r = fw3_ipt_rule_new(handle);
658
659                                         snprintf(buf, sizeof(buf) - 1, "%s(dest %s)",
660                                                  fw3_flag_names[t], zone->name);
661
662                                         fw3_ipt_rule_limit(r, &zone->log_limit);
663                                         fw3_ipt_rule_target(r, "LOG");
664                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
665                                         fw3_ipt_rule_append(r, "zone_%s_dest_%s",
666                                                             zone->name, fw3_flag_names[t]);
667                                 }
668                         }
669                 }
670                 break;
671
672         case FW3_TABLE_NAT:
673                 if (zone->masq && handle->family == FW3_FAMILY_V4)
674                 {
675                         /* for any negated masq_src ip, emit -s addr -j RETURN rules */
676                         for (msrc = NULL;
677                              (msrc = next_addr(msrc, &zone->masq_src,
678                                                handle->family, true)) != NULL; )
679                         {
680                                 msrc->invert = false;
681                                 r = fw3_ipt_rule_new(handle);
682                                 fw3_ipt_rule_src_dest(r, msrc, NULL);
683                                 fw3_ipt_rule_target(r, "RETURN");
684                                 fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
685                                 msrc->invert = true;
686                         }
687
688                         /* for any negated masq_dest ip, emit -d addr -j RETURN rules */
689                         for (mdest = NULL;
690                              (mdest = next_addr(mdest, &zone->masq_dest,
691                                                 handle->family, true)) != NULL; )
692                         {
693                                 mdest->invert = false;
694                                 r = fw3_ipt_rule_new(handle);
695                                 fw3_ipt_rule_src_dest(r, NULL, mdest);
696                                 fw3_ipt_rule_target(r, "RETURN");
697                                 fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
698                                 mdest->invert = true;
699                         }
700
701                         /* emit masquerading entries for non-negated addresses
702                            and ensure that both src and dest loops run at least once,
703                            even if there are no relevant addresses */
704                         for (first_src = true, msrc = NULL;
705                              (msrc = next_addr(msrc, &zone->masq_src,
706                                                    handle->family, false)) || first_src;
707                              first_src = false)
708                         {
709                                 for (first_dest = true, mdest = NULL;
710                                      (mdest = next_addr(mdest, &zone->masq_dest,
711                                                             handle->family, false)) || first_dest;
712                                      first_dest = false)
713                                 {
714                                         r = fw3_ipt_rule_new(handle);
715                                         fw3_ipt_rule_src_dest(r, msrc, mdest);
716                                         fw3_ipt_rule_target(r, "MASQUERADE");
717                                         fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
718                                 }
719                         }
720                 }
721                 break;
722
723         case FW3_TABLE_RAW:
724                 fw3_print_cthelpers(handle, state, zone);
725                 break;
726
727         case FW3_TABLE_MANGLE:
728                 break;
729         }
730
731         print_interface_rules(handle, state, reload, zone);
732 }
733
734 void
735 fw3_print_zone_chains(struct fw3_ipt_handle *handle, struct fw3_state *state,
736                       bool reload)
737 {
738         struct fw3_zone *zone;
739
740         list_for_each_entry(zone, &state->zones, list)
741                 print_zone_chain(handle, state, reload, zone);
742 }
743
744 void
745 fw3_print_zone_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
746                      bool reload)
747 {
748         struct fw3_zone *zone;
749
750         list_for_each_entry(zone, &state->zones, list)
751                 print_zone_rule(handle, state, reload, zone);
752 }
753
754 void
755 fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state,
756                 bool reload)
757 {
758         struct fw3_zone *z, *tmp;
759         const struct fw3_chain_spec *c;
760         char chain[32];
761
762         list_for_each_entry_safe(z, tmp, &state->zones, list)
763         {
764                 if (!has(z->flags, handle->family, handle->table))
765                         continue;
766
767                 for (c = zone_chains; c->format; c++)
768                 {
769                         /* don't touch user chains on selective stop */
770                         if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
771                                 continue;
772
773                         if (!fw3_is_family(c, handle->family))
774                                 continue;
775
776                         if (c->table != handle->table)
777                                 continue;
778
779                         if (c->flag && !has(z->flags, handle->family, c->flag))
780                                 continue;
781
782                         snprintf(chain, sizeof(chain), c->format, z->name);
783                         fw3_ipt_flush_chain(handle, chain);
784
785                         /* keep certain basic chains that do not depend on any settings to
786                            avoid purging unrelated user rules pointing to them */
787                         if (reload && !c->flag)
788                                 continue;
789
790                         fw3_ipt_delete_chain(handle, chain);
791                 }
792
793                 del(z->flags, handle->family, handle->table);
794         }
795 }
796
797 void
798 fw3_hotplug_zones(struct fw3_state *state, bool add)
799 {
800         struct fw3_zone *z;
801         struct fw3_device *d;
802
803         list_for_each_entry(z, &state->zones, list)
804         {
805                 if (add != fw3_hasbit(z->flags[0], FW3_FLAG_HOTPLUG))
806                 {
807                         list_for_each_entry(d, &z->devices, list)
808                                 fw3_hotplug(add, z, d);
809
810                         if (add)
811                                 fw3_setbit(z->flags[0], FW3_FLAG_HOTPLUG);
812                         else
813                                 fw3_delbit(z->flags[0], FW3_FLAG_HOTPLUG);
814                 }
815         }
816 }
817
818 struct fw3_zone *
819 fw3_lookup_zone(struct fw3_state *state, const char *name)
820 {
821         struct fw3_zone *z;
822
823         if (list_empty(&state->zones))
824                 return NULL;
825
826         list_for_each_entry(z, &state->zones, list)
827         {
828                 if (strcmp(z->name, name))
829                         continue;
830
831                 return z;
832         }
833
834         return NULL;
835 }
836
837 struct list_head *
838 fw3_resolve_zone_addresses(struct fw3_zone *zone, struct fw3_address *addr)
839 {
840         struct fw3_device *net;
841         struct fw3_address *cur, *tmp;
842         struct list_head *all;
843
844         all = calloc(1, sizeof(*all));
845         if (!all)
846                 return NULL;
847
848         INIT_LIST_HEAD(all);
849
850         if (addr && addr->set)
851         {
852                 tmp = malloc(sizeof(*tmp));
853
854                 if (tmp)
855                 {
856                         *tmp = *addr;
857                         list_add_tail(&tmp->list, all);
858                 }
859         }
860         else
861         {
862                 list_for_each_entry(net, &zone->networks, list)
863                         fw3_ubus_address(all, net->name);
864
865                 list_for_each_entry(cur, &zone->subnets, list)
866                 {
867                         tmp = malloc(sizeof(*tmp));
868
869                         if (!tmp)
870                                 continue;
871
872                         *tmp = *cur;
873                         list_add_tail(&tmp->list, all);
874                 }
875         }
876
877         return all;
878 }