helpers: implement explicit CT helper assignment support
[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 const struct fw3_option fw3_zone_opts[] = {
57         FW3_OPT("enabled",             bool,     zone,     enabled),
58
59         FW3_OPT("name",                string,   zone,     name),
60         FW3_OPT("family",              family,   zone,     family),
61
62         FW3_LIST("network",            device,   zone,     networks),
63         FW3_LIST("device",             device,   zone,     devices),
64         FW3_LIST("subnet",             network,  zone,     subnets),
65
66         FW3_OPT("input",               target,   zone,     policy_input),
67         FW3_OPT("forward",             target,   zone,     policy_forward),
68         FW3_OPT("output",              target,   zone,     policy_output),
69
70         FW3_OPT("masq",                bool,     zone,     masq),
71         FW3_OPT("masq_allow_invalid",  bool,     zone,     masq_allow_invalid),
72         FW3_LIST("masq_src",           network,  zone,     masq_src),
73         FW3_LIST("masq_dest",          network,  zone,     masq_dest),
74
75         FW3_OPT("extra",               string,   zone,     extra_src),
76         FW3_OPT("extra_src",           string,   zone,     extra_src),
77         FW3_OPT("extra_dest",          string,   zone,     extra_dest),
78
79         FW3_OPT("mtu_fix",             bool,     zone,     mtu_fix),
80         FW3_OPT("custom_chains",       bool,     zone,     custom_chains),
81
82         FW3_OPT("log",                 bool,     zone,     log),
83         FW3_OPT("log_limit",           limit,    zone,     log_limit),
84
85         FW3_OPT("auto_helper",         bool,     zone,     auto_helper),
86         FW3_LIST("helper",             cthelper, zone,     cthelpers),
87
88         FW3_OPT("__flags_v4",          int,      zone,     flags[0]),
89         FW3_OPT("__flags_v6",          int,      zone,     flags[1]),
90
91         FW3_LIST("__addrs",            address,  zone,     old_addrs),
92
93         { }
94 };
95
96
97 static void
98 check_policy(struct uci_element *e, enum fw3_flag *pol, enum fw3_flag def,
99              const char *name)
100 {
101         if (*pol == FW3_FLAG_UNSPEC)
102         {
103                 warn_elem(e, "has no %s policy specified, using default", name);
104                 *pol = def;
105         }
106         else if (*pol > FW3_FLAG_DROP)
107         {
108                 warn_elem(e, "has invalid %s policy, using default", name);
109                 *pol = def;
110         }
111 }
112
113 static bool
114 check_masq_addrs(struct list_head *head)
115 {
116         struct fw3_address *addr;
117         int n_addr = 0, n_failed = 0;
118
119         list_for_each_entry(addr, head, list)
120         {
121                 if (addr->invert)
122                         continue;
123
124                 n_addr++;
125
126                 if (!addr->set && addr->resolved)
127                         n_failed++;
128         }
129
130         return (n_addr == 0 || n_failed < n_addr);
131 }
132
133 static void
134 resolve_networks(struct uci_element *e, struct fw3_zone *zone)
135 {
136         struct fw3_device *net, *tmp;
137
138         list_for_each_entry(net, &zone->networks, list)
139         {
140                 tmp = fw3_ubus_device(net->name);
141
142                 if (!tmp)
143                 {
144                         warn_elem(e, "cannot resolve device of network '%s'", net->name);
145                         continue;
146                 }
147
148                 snprintf(tmp->network, sizeof(tmp->network), "%s", net->name);
149                 list_add_tail(&tmp->list, &zone->devices);
150         }
151 }
152
153 static void
154 resolve_cthelpers(struct fw3_state *s, struct uci_element *e, struct fw3_zone *zone)
155 {
156         struct fw3_cthelpermatch *match;
157
158         if (list_empty(&zone->cthelpers))
159         {
160                 if (!zone->masq && zone->auto_helper)
161                 {
162                         fw3_setbit(zone->flags[0], FW3_FLAG_HELPER);
163                         fw3_setbit(zone->flags[1], FW3_FLAG_HELPER);
164                 }
165
166                 return;
167         }
168
169         list_for_each_entry(match, &zone->cthelpers, list)
170         {
171                 if (match->invert)
172                 {
173                         warn_elem(e, "must not use a negated helper match");
174                         continue;
175                 }
176
177                 match->ptr = fw3_lookup_cthelper(s, match->name);
178
179                 if (!match->ptr)
180                 {
181                         warn_elem(e, "refers to not existing helper '%s'", match->name);
182                         continue;
183                 }
184
185                 if (fw3_is_family(match->ptr, FW3_FAMILY_V4))
186                         fw3_setbit(zone->flags[0], FW3_FLAG_HELPER);
187
188                 if (fw3_is_family(match->ptr, FW3_FAMILY_V6))
189                         fw3_setbit(zone->flags[1], FW3_FLAG_HELPER);
190         }
191 }
192
193 struct fw3_zone *
194 fw3_alloc_zone(void)
195 {
196         struct fw3_zone *zone;
197
198         zone = calloc(1, sizeof(*zone));
199         if (!zone)
200                 return NULL;
201
202         INIT_LIST_HEAD(&zone->networks);
203         INIT_LIST_HEAD(&zone->devices);
204         INIT_LIST_HEAD(&zone->subnets);
205         INIT_LIST_HEAD(&zone->masq_src);
206         INIT_LIST_HEAD(&zone->masq_dest);
207         INIT_LIST_HEAD(&zone->cthelpers);
208
209         INIT_LIST_HEAD(&zone->old_addrs);
210
211         zone->enabled = true;
212         zone->auto_helper = true;
213         zone->custom_chains = true;
214         zone->log_limit.rate = 10;
215
216         return zone;
217 }
218
219 void
220 fw3_load_zones(struct fw3_state *state, struct uci_package *p)
221 {
222         struct uci_section *s;
223         struct uci_element *e;
224         struct fw3_zone *zone;
225         struct fw3_defaults *defs = &state->defaults;
226
227         INIT_LIST_HEAD(&state->zones);
228
229         uci_foreach_element(&p->sections, e)
230         {
231                 s = uci_to_section(e);
232
233                 if (strcmp(s->type, "zone"))
234                         continue;
235
236                 zone = fw3_alloc_zone();
237
238                 if (!zone)
239                         continue;
240
241                 if (!fw3_parse_options(zone, fw3_zone_opts, s))
242                         warn_elem(e, "has invalid options");
243
244                 if (!zone->enabled)
245                 {
246                         fw3_free_zone(zone);
247                         continue;
248                 }
249
250                 if (!zone->extra_dest)
251                         zone->extra_dest = zone->extra_src;
252
253                 if (!defs->custom_chains && zone->custom_chains)
254                         zone->custom_chains = false;
255
256                 if (!defs->auto_helper && zone->auto_helper)
257                         zone->auto_helper = false;
258
259                 if (!zone->name || !*zone->name)
260                 {
261                         warn_elem(e, "has no name - ignoring");
262                         fw3_free_zone(zone);
263                         continue;
264                 }
265
266                 if (strlen(zone->name) > FW3_ZONE_MAXNAMELEN)
267                 {
268                         warn_elem(e, "must not have a name longer than %u characters",
269                                      FW3_ZONE_MAXNAMELEN);
270                         fw3_free_zone(zone);
271                         continue;
272                 }
273
274                 fw3_ubus_zone_devices(zone);
275
276                 if (list_empty(&zone->networks) && list_empty(&zone->devices) &&
277                     list_empty(&zone->subnets) && !zone->extra_src)
278                 {
279                         warn_elem(e, "has no device, network, subnet or extra options");
280                 }
281
282                 if (!check_masq_addrs(&zone->masq_src))
283                 {
284                         warn_elem(e, "has unresolved masq_src, disabling masq");
285                         zone->masq = false;
286                 }
287
288                 if (!check_masq_addrs(&zone->masq_dest))
289                 {
290                         warn_elem(e, "has unresolved masq_dest, disabling masq");
291                         zone->masq = false;
292                 }
293
294                 check_policy(e, &zone->policy_input, defs->policy_input, "input");
295                 check_policy(e, &zone->policy_output, defs->policy_output, "output");
296                 check_policy(e, &zone->policy_forward, defs->policy_forward, "forward");
297
298                 resolve_networks(e, zone);
299
300                 if (zone->masq)
301                 {
302                         fw3_setbit(zone->flags[0], FW3_FLAG_SNAT);
303                 }
304
305                 if (zone->custom_chains)
306                 {
307                         fw3_setbit(zone->flags[0], FW3_FLAG_SNAT);
308                         fw3_setbit(zone->flags[0], FW3_FLAG_DNAT);
309                 }
310
311                 resolve_cthelpers(state, e, zone);
312
313                 fw3_setbit(zone->flags[0], fw3_to_src_target(zone->policy_input));
314                 fw3_setbit(zone->flags[0], zone->policy_forward);
315                 fw3_setbit(zone->flags[0], zone->policy_output);
316
317                 fw3_setbit(zone->flags[1], fw3_to_src_target(zone->policy_input));
318                 fw3_setbit(zone->flags[1], zone->policy_forward);
319                 fw3_setbit(zone->flags[1], zone->policy_output);
320
321                 list_add_tail(&zone->list, &state->zones);
322         }
323 }
324
325
326 static void
327 print_zone_chain(struct fw3_ipt_handle *handle, struct fw3_state *state,
328                  bool reload, struct fw3_zone *zone)
329 {
330         int i;
331         struct fw3_ipt_rule *r;
332         const struct fw3_chain_spec *c;
333
334         const char *flt_chains[] = {
335                 "input",   "input",
336                 "output",  "output",
337                 "forward", "forwarding",
338         };
339
340         const char *nat_chains[] = {
341                 "prerouting",  "prerouting",
342                 "postrouting", "postrouting",
343         };
344
345         if (!fw3_is_family(zone, handle->family))
346                 return;
347
348         set(zone->flags, handle->family, handle->table);
349
350         if (zone->custom_chains)
351                 set(zone->flags, handle->family, FW3_FLAG_CUSTOM_CHAINS);
352
353         for (c = zone_chains; c->format; c++)
354         {
355                 /* don't touch user chains on selective stop */
356                 if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS)
357                         continue;
358
359                 if (!fw3_is_family(c, handle->family))
360                         continue;
361
362                 if (c->table != handle->table)
363                         continue;
364
365                 if (c->flag &&
366                     !fw3_hasbit(zone->flags[handle->family == FW3_FAMILY_V6], c->flag))
367                         continue;
368
369                 fw3_ipt_create_chain(handle, c->format, zone->name);
370         }
371
372         if (zone->custom_chains)
373         {
374                 if (handle->table == FW3_TABLE_FILTER)
375                 {
376                         for (i = 0; i < sizeof(flt_chains)/sizeof(flt_chains[0]); i += 2)
377                         {
378                                 r = fw3_ipt_rule_new(handle);
379                                 fw3_ipt_rule_comment(r, "user chain for %s", flt_chains[i+1]);
380                                 fw3_ipt_rule_target(r, "%s_%s_rule", flt_chains[i+1], zone->name);
381                                 fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, flt_chains[i]);
382                         }
383                 }
384                 else if (handle->table == FW3_TABLE_NAT)
385                 {
386                         for (i = 0; i < sizeof(nat_chains)/sizeof(nat_chains[0]); i += 2)
387                         {
388                                 r = fw3_ipt_rule_new(handle);
389                                 fw3_ipt_rule_comment(r, "user chain for %s", nat_chains[i+1]);
390                                 fw3_ipt_rule_target(r, "%s_%s_rule", nat_chains[i+1], zone->name);
391                                 fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, nat_chains[i]);
392                         }
393                 }
394         }
395
396         set(zone->flags, handle->family, handle->table);
397 }
398
399 static void
400 print_interface_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
401                                          bool reload, struct fw3_zone *zone,
402                      struct fw3_device *dev, struct fw3_address *sub)
403 {
404         struct fw3_protocol tcp = { .protocol = 6 };
405         struct fw3_ipt_rule *r;
406         enum fw3_flag t;
407
408         char buf[32];
409
410         int i;
411
412         const char *chains[] = {
413                 "input", "INPUT",
414                 "output", "OUTPUT",
415                 "forward", "FORWARD",
416         };
417
418 #define jump_target(t) \
419         ((t == FW3_FLAG_REJECT) ? "reject" : fw3_flag_names[t])
420
421         if (handle->table == FW3_TABLE_FILTER)
422         {
423                 for (t = FW3_FLAG_ACCEPT; t <= FW3_FLAG_DROP; t++)
424                 {
425                         if (has(zone->flags, handle->family, fw3_to_src_target(t)))
426                         {
427                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
428                                 fw3_ipt_rule_target(r, jump_target(t));
429                                 fw3_ipt_rule_extra(r, zone->extra_src);
430
431                                 if (t == FW3_FLAG_ACCEPT && !state->defaults.drop_invalid)
432                                         fw3_ipt_rule_extra(r,
433                                                            "-m conntrack --ctstate NEW,UNTRACKED");
434
435                                 fw3_ipt_rule_replace(r, "zone_%s_src_%s", zone->name,
436                                                      fw3_flag_names[t]);
437                         }
438
439                         if (has(zone->flags, handle->family, t))
440                         {
441                                 if (t == FW3_FLAG_ACCEPT &&
442                                     zone->masq && !zone->masq_allow_invalid)
443                                 {
444                                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
445                                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate INVALID");
446                                         fw3_ipt_rule_comment(r, "Prevent NAT leakage");
447                                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_DROP]);
448                                         fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
449                                                              fw3_flag_names[t]);
450                                 }
451
452                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
453                                 fw3_ipt_rule_target(r, jump_target(t));
454                                 fw3_ipt_rule_extra(r, zone->extra_dest);
455                                 fw3_ipt_rule_replace(r, "zone_%s_dest_%s", zone->name,
456                                                      fw3_flag_names[t]);
457                         }
458                 }
459
460                 for (i = 0; i < sizeof(chains)/sizeof(chains[0]); i += 2)
461                 {
462                         if (*chains[i] == 'o')
463                                 r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
464                         else
465                                 r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
466
467                         fw3_ipt_rule_target(r, "zone_%s_%s", zone->name, chains[i]);
468
469                         if (*chains[i] == 'o')
470                                 fw3_ipt_rule_extra(r, zone->extra_dest);
471                         else
472                                 fw3_ipt_rule_extra(r, zone->extra_src);
473
474                         fw3_ipt_rule_replace(r, chains[i + 1]);
475                 }
476         }
477         else if (handle->table == FW3_TABLE_NAT)
478         {
479                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
480                 {
481                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
482                         fw3_ipt_rule_target(r, "zone_%s_prerouting", zone->name);
483                         fw3_ipt_rule_extra(r, zone->extra_src);
484                         fw3_ipt_rule_replace(r, "PREROUTING");
485                 }
486
487                 if (has(zone->flags, handle->family, FW3_FLAG_SNAT))
488                 {
489                         r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub);
490                         fw3_ipt_rule_target(r, "zone_%s_postrouting", zone->name);
491                         fw3_ipt_rule_extra(r, zone->extra_dest);
492                         fw3_ipt_rule_replace(r, "POSTROUTING");
493                 }
494         }
495         else if (handle->table == FW3_TABLE_MANGLE)
496         {
497                 if (zone->mtu_fix)
498                 {
499                         if (zone->log)
500                         {
501                                 snprintf(buf, sizeof(buf) - 1, "MSSFIX(%s): ", zone->name);
502
503                                 r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
504                                 fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
505                                 fw3_ipt_rule_addarg(r, false, "SYN", NULL);
506                                 fw3_ipt_rule_limit(r, &zone->log_limit);
507                                 fw3_ipt_rule_comment(r, "%s (mtu_fix logging)", zone->name);
508                                 fw3_ipt_rule_target(r, "LOG");
509                                 fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
510                                 fw3_ipt_rule_replace(r, "FORWARD");
511                         }
512
513                         r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub);
514                         fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST");
515                         fw3_ipt_rule_addarg(r, false, "SYN", NULL);
516                         fw3_ipt_rule_comment(r, "%s (mtu_fix)", zone->name);
517                         fw3_ipt_rule_target(r, "TCPMSS");
518                         fw3_ipt_rule_addarg(r, false, "--clamp-mss-to-pmtu", NULL);
519                         fw3_ipt_rule_replace(r, "FORWARD");
520                 }
521         }
522         else if (handle->table == FW3_TABLE_RAW)
523         {
524                 if (has(zone->flags, handle->family, FW3_FLAG_HELPER))
525                 {
526                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
527                         fw3_ipt_rule_comment(r, "%s CT helper assignment", zone->name);
528                         fw3_ipt_rule_target(r, "zone_%s_helper", zone->name);
529                         fw3_ipt_rule_extra(r, zone->extra_src);
530                         fw3_ipt_rule_replace(r, "PREROUTING");
531                 }
532
533                 if (has(zone->flags, handle->family, FW3_FLAG_NOTRACK))
534                 {
535                         r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL);
536                         fw3_ipt_rule_comment(r, "%s CT bypass", zone->name);
537                         fw3_ipt_rule_target(r, "zone_%s_notrack", zone->name);
538                         fw3_ipt_rule_extra(r, zone->extra_src);
539                         fw3_ipt_rule_replace(r, "PREROUTING");
540                 }
541         }
542 }
543
544 static void
545 print_interface_rules(struct fw3_ipt_handle *handle, struct fw3_state *state,
546                       bool reload, struct fw3_zone *zone)
547 {
548         struct fw3_device *dev;
549         struct fw3_address *sub;
550
551         fw3_foreach(dev, &zone->devices)
552         fw3_foreach(sub, &zone->subnets)
553         {
554                 if (!fw3_is_family(sub, handle->family))
555                         continue;
556
557                 if (!dev && !sub)
558                         continue;
559
560                 print_interface_rule(handle, state, reload, zone, dev, sub);
561         }
562 }
563
564 static struct fw3_address *
565 next_addr(struct fw3_address *addr, struct list_head *list,
566                 enum fw3_family family, bool invert)
567 {
568         struct list_head *p;
569         struct fw3_address *rv;
570
571         for (p = addr ? addr->list.next : list->next; p != list; p = p->next)
572         {
573                 rv = list_entry(p, struct fw3_address, list);
574
575                 if (fw3_is_family(rv, family) && rv->set && rv->invert == invert)
576                         return rv;
577         }
578
579         return NULL;
580 }
581
582 static void
583 print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state,
584                 bool reload, struct fw3_zone *zone)
585 {
586         bool first_src, first_dest;
587         struct fw3_address *msrc;
588         struct fw3_address *mdest;
589         struct fw3_ipt_rule *r;
590
591         enum fw3_flag t;
592         char buf[32];
593
594         if (!fw3_is_family(zone, handle->family))
595                 return;
596
597         info("   * Zone '%s'", zone->name);
598
599         switch (handle->table)
600         {
601         case FW3_TABLE_FILTER:
602                 if (has(zone->flags, handle->family, FW3_FLAG_DNAT))
603                 {
604                         r = fw3_ipt_rule_new(handle);
605                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
606                         fw3_ipt_rule_comment(r, "Accept port redirections");
607                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
608                         fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
609
610                         r = fw3_ipt_rule_new(handle);
611                         fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT");
612                         fw3_ipt_rule_comment(r, "Accept port forwards");
613                         fw3_ipt_rule_target(r, fw3_flag_names[FW3_FLAG_ACCEPT]);
614                         fw3_ipt_rule_append(r, "zone_%s_forward", zone->name);
615                 }
616
617                 r = fw3_ipt_rule_new(handle);
618                 fw3_ipt_rule_target(r, "zone_%s_src_%s", zone->name,
619                                      fw3_flag_names[zone->policy_input]);
620                 fw3_ipt_rule_append(r, "zone_%s_input", zone->name);
621
622                 r = fw3_ipt_rule_new(handle);
623                 fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name,
624                                      fw3_flag_names[zone->policy_forward]);
625                 fw3_ipt_rule_append(r, "zone_%s_forward", 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_output]);
630                 fw3_ipt_rule_append(r, "zone_%s_output", zone->name);
631
632                 if (zone->log)
633                 {
634                         for (t = FW3_FLAG_REJECT; t <= FW3_FLAG_DROP; t++)
635                         {
636                                 if (has(zone->flags, handle->family, fw3_to_src_target(t)))
637                                 {
638                                         r = fw3_ipt_rule_new(handle);
639
640                                         snprintf(buf, sizeof(buf) - 1, "%s(src %s)",
641                                                  fw3_flag_names[t], zone->name);
642
643                                         fw3_ipt_rule_limit(r, &zone->log_limit);
644                                         fw3_ipt_rule_target(r, "LOG");
645                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
646                                         fw3_ipt_rule_append(r, "zone_%s_src_%s",
647                                                             zone->name, fw3_flag_names[t]);
648                                 }
649
650                                 if (has(zone->flags, handle->family, t))
651                                 {
652                                         r = fw3_ipt_rule_new(handle);
653
654                                         snprintf(buf, sizeof(buf) - 1, "%s(dest %s)",
655                                                  fw3_flag_names[t], zone->name);
656
657                                         fw3_ipt_rule_limit(r, &zone->log_limit);
658                                         fw3_ipt_rule_target(r, "LOG");
659                                         fw3_ipt_rule_addarg(r, false, "--log-prefix", buf);
660                                         fw3_ipt_rule_append(r, "zone_%s_dest_%s",
661                                                             zone->name, fw3_flag_names[t]);
662                                 }
663                         }
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 }