zones: add interface/subnet bound LOG rules
[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, "Custom %s %s rule chain", zone->name, 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, "Custom %s %s rule chain", zone->name, 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 (t > FW3_FLAG_ACCEPT && zone->log & FW3_ZONE_LOG_FILTER)
431                         {
432                                 if (has(zone->flags, handle->family, fw3_to_src_target(t)))
433                                 {
434                                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
435
436                                         snprintf(buf, sizeof(buf) - 1, "%s %s in: ",
437                                                  fw3_flag_names[t], zone->name);
438
439                                         fw3_ipt_rule_limit(r, &zone->log_limit);
440                                         fw3_ipt_rule_target(r, "LOG");
441                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
442                                         fw3_ipt_rule_replace(r, "zone_%s_src_%s",
443                                                              zone->name, fw3_flag_names[t]);
444                                 }
445
446                                 if (has(zone->flags, handle->family, t))
447                                 {
448                                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
449
450                                         snprintf(buf, sizeof(buf) - 1, "%s %s out: ",
451                                                  fw3_flag_names[t], zone->name);
452
453                                         fw3_ipt_rule_limit(r, &zone->log_limit);
454                                         fw3_ipt_rule_target(r, "LOG");
455                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
456                                         fw3_ipt_rule_replace(r, "zone_%s_dest_%s",
457                                                              zone->name, fw3_flag_names[t]);
458                                 }
459                         }
460
461                         if (has(zone->flags, handle->family, fw3_to_src_target(t)))
462                         {
463                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
464                                 fw3_ipt_rule_target(r, jump_target(t));
465                                 fw3_ipt_rule_extra(r, zone->extra_src);
466
467                                 if (t == FW3_FLAG_ACCEPT && !state->defaults.drop_invalid)
468                                         fw3_ipt_rule_extra(r,
469                                                            "-m conntrack --ctstate NEW,UNTRACKED");
470
471                                 fw3_ipt_rule_replace(r, "zone_%s_src_%s", zone->name,
472                                                      fw3_flag_names[t]);
473                         }
474
475                         if (has(zone->flags, handle->family, t))
476                         {
477                                 if (t == FW3_FLAG_ACCEPT &&
478                                     zone->masq && !zone->masq_allow_invalid)
479                                 {
480                                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
481                                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate INVALID");
482                                         fw3_ipt_rule_comment(r, "Prevent NAT leakage");
483                                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_DROP]);
484                                         fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
485                                                              fw3_flag_names[t]);
486                                 }
487
488                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
489                                 fw3_ipt_rule_target(r, jump_target(t));
490                                 fw3_ipt_rule_extra(r, zone->extra_dest);
491                                 fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
492                                                      fw3_flag_names[t]);
493                         }
494                 }
495
496                 for (i = 0; i < sizeof(chains)/sizeof(chains[0]); i += 2)
497                 {
498                         if (*chains[i] == 'o')
499                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
500                         else
501                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
502
503                         fw3_ipt_rule_target(r, "zone_%s_%s", zone->name, chains[i]);
504
505                         if (*chains[i] == 'o')
506                                 fw3_ipt_rule_extra(r, zone->extra_dest);
507                         else
508                                 fw3_ipt_rule_extra(r, zone->extra_src);
509
510                         fw3_ipt_rule_replace(r, chains[i + 1]);
511                 }
512         }
513         else if (handle->table == FW3_TABLE_NAT)
514         {
515                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
516                 {
517                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
518                         fw3_ipt_rule_target(r, "zone_%s_prerouting", zone->name);
519                         fw3_ipt_rule_extra(r, zone->extra_src);
520                         fw3_ipt_rule_replace(r, "PREROUTING");
521                 }
522
523                 if (has(zone->flags, handle->family, FW3_FLAG_SNAT))
524                 {
525                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
526                         fw3_ipt_rule_target(r, "zone_%s_postrouting", zone->name);
527                         fw3_ipt_rule_extra(r, zone->extra_dest);
528                         fw3_ipt_rule_replace(r, "POSTROUTING");
529                 }
530         }
531         else if (handle->table == FW3_TABLE_MANGLE)
532         {
533                 if (zone->mtu_fix)
534                 {
535                         if (zone->log & FW3_ZONE_LOG_MANGLE)
536                         {
537                                 snprintf(buf, sizeof(buf) - 1, "MSSFIX %s out: ", zone->name);
538
539                                 r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
540                                 fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
541                                 fw3_ipt_rule_addarg(r, false, "SYN", NULL);
542                                 fw3_ipt_rule_limit(r, &zone->log_limit);
543                                 fw3_ipt_rule_comment(r, "Zone %s MTU fix logging", zone->name);
544                                 fw3_ipt_rule_target(r, "LOG");
545                                 fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
546                                 fw3_ipt_rule_replace(r, "FORWARD");
547                         }
548
549                         r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
550                         fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
551                         fw3_ipt_rule_addarg(r, false, "SYN", NULL);
552                         fw3_ipt_rule_comment(r, "Zone %s MTU fixing", zone->name);
553                         fw3_ipt_rule_target(r, "TCPMSS");
554                         fw3_ipt_rule_addarg(r, false, "--clamp-mss-to-pmtu", NULL);
555                         fw3_ipt_rule_replace(r, "FORWARD");
556                 }
557         }
558         else if (handle->table == FW3_TABLE_RAW)
559         {
560                 if (has(zone->flags, handle->family, FW3_FLAG_HELPER))
561                 {
562                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
563                         fw3_ipt_rule_comment(r, "%s CT helper assignment", zone->name);
564                         fw3_ipt_rule_target(r, "zone_%s_helper", zone->name);
565                         fw3_ipt_rule_extra(r, zone->extra_src);
566                         fw3_ipt_rule_replace(r, "PREROUTING");
567                 }
568
569                 if (has(zone->flags, handle->family, FW3_FLAG_NOTRACK))
570                 {
571                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
572                         fw3_ipt_rule_comment(r, "%s CT bypass", zone->name);
573                         fw3_ipt_rule_target(r, "zone_%s_notrack", zone->name);
574                         fw3_ipt_rule_extra(r, zone->extra_src);
575                         fw3_ipt_rule_replace(r, "PREROUTING");
576                 }
577         }
578 }
579
580 static void
581 print_interface_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
582                       bool reload, struct fw3_zone *zone)
583 {
584         struct fw3_device *dev;
585         struct fw3_address *sub;
586
587         fw3_foreach(dev, &zone->devices)
588         fw3_foreach(sub, &zone->subnets)
589         {
590                 if (!fw3_is_family(sub, handle->family))
591                         continue;
592
593                 if (!dev && !sub)
594                         continue;
595
596                 print_interface_rule(handle, state, reload, zone, dev, sub);
597         }
598 }
599
600 static struct fw3_address *
601 next_addr(struct fw3_address *addr, struct list_head *list,
602                 enum fw3_family family, bool invert)
603 {
604         struct list_head *p;
605         struct fw3_address *rv;
606
607         for (p = addr ? addr->list.next : list->next; p != list; p = p->next)
608         {
609                 rv = list_entry(p, struct fw3_address, list);
610
611                 if (fw3_is_family(rv, family) && rv->set && rv->invert == invert)
612                         return rv;
613         }
614
615         return NULL;
616 }
617
618 static void
619 print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
620                 bool reload, struct fw3_zone *zone)
621 {
622         bool first_src, first_dest;
623         struct fw3_address *msrc;
624         struct fw3_address *mdest;
625         struct fw3_ipt_rule *r;
626
627         if (!fw3_is_family(zone, handle->family))
628                 return;
629
630         info("   * Zone '%s'", zone->name);
631
632         switch (handle->table)
633         {
634         case FW3_TABLE_FILTER:
635                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
636                 {
637                         r = fw3_ipt_rule_new(handle);
638                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
639                         fw3_ipt_rule_comment(r, "Accept port redirections");
640                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
641                         fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
642
643                         r = fw3_ipt_rule_new(handle);
644                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
645                         fw3_ipt_rule_comment(r, "Accept port forwards");
646                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
647                         fw3_ipt_rule_append(r, "zone_%s_forward", zone->name);
648                 }
649
650                 r = fw3_ipt_rule_new(handle);
651                 fw3_ipt_rule_target(r, "zone_%s_src_%s", zone->name,
652                                      fw3_flag_names[zone->policy_input]);
653                 fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
654
655                 r = fw3_ipt_rule_new(handle);
656                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name,
657                                      fw3_flag_names[zone->policy_forward]);
658                 fw3_ipt_rule_append(r, "zone_%s_forward", zone->name);
659
660                 r = fw3_ipt_rule_new(handle);
661                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name,
662                                      fw3_flag_names[zone->policy_output]);
663                 fw3_ipt_rule_append(r, "zone_%s_output", zone->name);
664
665                 break;
666
667         case FW3_TABLE_NAT:
668                 if (zone->masq && handle->family == FW3_FAMILY_V4)
669                 {
670                         /* for any negated masq_src ip, emit -s addr -j RETURN rules */
671                         for (msrc = NULL;
672                              (msrc = next_addr(msrc, &zone->masq_src,
673                                                handle->family, true)) != NULL; )
674                         {
675                                 msrc->invert = false;
676                                 r = fw3_ipt_rule_new(handle);
677                                 fw3_ipt_rule_src_dest(r, msrc, NULL);
678                                 fw3_ipt_rule_target(r, "RETURN");
679                                 fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
680                                 msrc->invert = true;
681                         }
682
683                         /* for any negated masq_dest ip, emit -d addr -j RETURN rules */
684                         for (mdest = NULL;
685                              (mdest = next_addr(mdest, &zone->masq_dest,
686                                                 handle->family, true)) != NULL; )
687                         {
688                                 mdest->invert = false;
689                                 r = fw3_ipt_rule_new(handle);
690                                 fw3_ipt_rule_src_dest(r, NULL, mdest);
691                                 fw3_ipt_rule_target(r, "RETURN");
692                                 fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
693                                 mdest->invert = true;
694                         }
695
696                         /* emit masquerading entries for non-negated addresses
697                            and ensure that both src and dest loops run at least once,
698                            even if there are no relevant addresses */
699                         for (first_src = true, msrc = NULL;
700                              (msrc = next_addr(msrc, &zone->masq_src,
701                                                    handle->family, false)) || first_src;
702                              first_src = false)
703                         {
704                                 for (first_dest = true, mdest = NULL;
705                                      (mdest = next_addr(mdest, &zone->masq_dest,
706                                                             handle->family, false)) || first_dest;
707                                      first_dest = false)
708                                 {
709                                         r = fw3_ipt_rule_new(handle);
710                                         fw3_ipt_rule_src_dest(r, msrc, mdest);
711                                         fw3_ipt_rule_target(r, "MASQUERADE");
712                                         fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name);
713                                 }
714                         }
715                 }
716                 break;
717
718         case FW3_TABLE_RAW:
719                 fw3_print_cthelpers(handle, state, zone);
720                 break;
721
722         case FW3_TABLE_MANGLE:
723                 break;
724         }
725
726         print_interface_rules(handle, state, reload, zone);
727 }
728
729 void
730 fw3_print_zone_chains(struct fw3_ipt_handle *handle, struct fw3_state *state,
731                       bool reload)
732 {
733         struct fw3_zone *zone;
734
735         list_for_each_entry(zone, &state->zones, list)
736                 print_zone_chain(handle, state, reload, zone);
737 }
738
739 void
740 fw3_print_zone_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
741                      bool reload)
742 {
743         struct fw3_zone *zone;
744
745         list_for_each_entry(zone, &state->zones, list)
746                 print_zone_rule(handle, state, reload, zone);
747 }
748
749 void
750 fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state,
751                 bool reload)
752 {
753         struct fw3_zone *z, *tmp;
754         const struct fw3_chain_spec *c;
755         char chain[32];
756
757         list_for_each_entry_safe(z, tmp, &state->zones, list)
758         {
759                 if (!has(z->flags, handle->family, handle->table))
760                         continue;
761
762                 for (c = zone_chains; c->format; c++)
763                 {
764                         /* don't touch user chains on selective stop */
765                         if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
766                                 continue;
767
768                         if (!fw3_is_family(c, handle->family))
769                                 continue;
770
771                         if (c->table != handle->table)
772                                 continue;
773
774                         if (c->flag && !has(z->flags, handle->family, c->flag))
775                                 continue;
776
777                         snprintf(chain, sizeof(chain), c->format, z->name);
778                         fw3_ipt_flush_chain(handle, chain);
779
780                         /* keep certain basic chains that do not depend on any settings to
781                            avoid purging unrelated user rules pointing to them */
782                         if (reload && !c->flag)
783                                 continue;
784
785                         fw3_ipt_delete_chain(handle, chain);
786                 }
787
788                 del(z->flags, handle->family, handle->table);
789         }
790 }
791
792 void
793 fw3_hotplug_zones(struct fw3_state *state, bool add)
794 {
795         struct fw3_zone *z;
796         struct fw3_device *d;
797
798         list_for_each_entry(z, &state->zones, list)
799         {
800                 if (add != fw3_hasbit(z->flags[0], FW3_FLAG_HOTPLUG))
801                 {
802                         list_for_each_entry(d, &z->devices, list)
803                                 fw3_hotplug(add, z, d);
804
805                         if (add)
806                                 fw3_setbit(z->flags[0], FW3_FLAG_HOTPLUG);
807                         else
808                                 fw3_delbit(z->flags[0], FW3_FLAG_HOTPLUG);
809                 }
810         }
811 }
812
813 struct fw3_zone *
814 fw3_lookup_zone(struct fw3_state *state, const char *name)
815 {
816         struct fw3_zone *z;
817
818         if (list_empty(&state->zones))
819                 return NULL;
820
821         list_for_each_entry(z, &state->zones, list)
822         {
823                 if (strcmp(z->name, name))
824                         continue;
825
826                 return z;
827         }
828
829         return NULL;
830 }
831
832 struct list_head *
833 fw3_resolve_zone_addresses(struct fw3_zone *zone, struct fw3_address *addr)
834 {
835         struct fw3_device *net;
836         struct fw3_address *cur, *tmp;
837         struct list_head *all;
838
839         all = calloc(1, sizeof(*all));
840         if (!all)
841                 return NULL;
842
843         INIT_LIST_HEAD(all);
844
845         if (addr && addr->set)
846         {
847                 tmp = malloc(sizeof(*tmp));
848
849                 if (tmp)
850                 {
851                         *tmp = *addr;
852                         list_add_tail(&tmp->list, all);
853                 }
854         }
855         else
856         {
857                 list_for_each_entry(net, &zone->networks, list)
858                         fw3_ubus_address(all, net->name);
859
860                 list_for_each_entry(cur, &zone->subnets, list)
861                 {
862                         tmp = malloc(sizeof(*tmp));
863
864                         if (!tmp)
865                                 continue;
866
867                         *tmp = *cur;
868                         list_add_tail(&tmp->list, all);
869                 }
870         }
871
872         return all;
873 }