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