defaults: robustify flow table detection.
[oweals/firewall3.git] / redirects.c
1 /*
2  * firewall3 - 3rd OpenWrt UCI firewall implementation
3  *
4  *   Copyright (C) 2013-2018 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 "redirects.h"
20
21
22 const struct fw3_option fw3_redirect_opts[] = {
23         FW3_OPT("enabled",             bool,      redirect,     enabled),
24
25         FW3_OPT("name",                string,    redirect,     name),
26         FW3_OPT("family",              family,    redirect,     family),
27
28         FW3_OPT("src",                 device,    redirect,     src),
29         FW3_OPT("dest",                device,    redirect,     dest),
30
31         FW3_OPT("ipset",               setmatch,  redirect,     ipset),
32         FW3_OPT("helper",              cthelper,  redirect,     helper),
33
34         FW3_LIST("proto",              protocol,  redirect,     proto),
35
36         FW3_OPT("src_ip",              network,   redirect,     ip_src),
37         FW3_LIST("src_mac",            mac,       redirect,     mac_src),
38         FW3_OPT("src_port",            port,      redirect,     port_src),
39
40         FW3_OPT("src_dip",             network,   redirect,     ip_dest),
41         FW3_OPT("src_dport",           port,      redirect,     port_dest),
42
43         FW3_OPT("dest_ip",             network,   redirect,     ip_redir),
44         FW3_OPT("dest_port",           port,      redirect,     port_redir),
45
46         FW3_OPT("extra",               string,    redirect,     extra),
47
48         FW3_OPT("limit",               limit,     redirect,     limit),
49         FW3_OPT("limit_burst",         int,       redirect,     limit.burst),
50
51         FW3_OPT("utc_time",            bool,      redirect,     time.utc),
52         FW3_OPT("start_date",          date,      redirect,     time.datestart),
53         FW3_OPT("stop_date",           date,      redirect,     time.datestop),
54         FW3_OPT("start_time",          time,      redirect,     time.timestart),
55         FW3_OPT("stop_time",           time,      redirect,     time.timestop),
56         FW3_OPT("weekdays",            weekdays,  redirect,     time.weekdays),
57         FW3_OPT("monthdays",           monthdays, redirect,     time.monthdays),
58
59         FW3_OPT("mark",                mark,      redirect,     mark),
60
61         FW3_OPT("reflection",          bool,      redirect,     reflection),
62         FW3_OPT("reflection_src",      reflection_source,
63                                                   redirect,     reflection_src),
64
65         FW3_OPT("target",              target,    redirect,     target),
66
67         { }
68 };
69
70
71 static bool
72 check_families(struct uci_element *e, struct fw3_redirect *r)
73 {
74         if (r->family == FW3_FAMILY_ANY)
75                 return true;
76
77         if (r->_src && r->_src->family && r->_src->family != r->family)
78         {
79                 warn_elem(e, "refers to source zone with different family");
80                 return false;
81         }
82
83         if (r->_dest && r->_dest->family && r->_dest->family != r->family)
84         {
85                 warn_elem(e, "refers to destination zone with different family");
86                 return false;
87         }
88
89         if (r->ipset.ptr && r->ipset.ptr->family &&
90             r->ipset.ptr->family != r->family)
91         {
92                 warn_elem(e, "refers to ipset with different family");
93                 return false;
94         }
95
96         if (r->helper.ptr && r->helper.ptr->family &&
97             r->helper.ptr->family != r->family)
98         {
99                 warn_elem(e, "refers to CT helper not supporting family");
100                 return false;
101         }
102
103         if (r->ip_src.family && r->ip_src.family != r->family)
104         {
105                 warn_elem(e, "uses source ip with different family");
106                 return false;
107         }
108
109         if (r->ip_dest.family && r->ip_dest.family != r->family)
110         {
111                 warn_elem(e, "uses destination ip with different family");
112                 return false;
113         }
114
115         if (r->ip_redir.family && r->ip_redir.family != r->family)
116         {
117                 warn_elem(e, "uses redirect ip with different family");
118                 return false;
119         }
120
121         return true;
122 }
123
124 static bool
125 compare_addr(struct fw3_address *a, struct fw3_address *b)
126 {
127         if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4)
128                 return false;
129
130         return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
131                 (b->address.v4.s_addr & a->mask.v4.s_addr));
132 }
133
134 static bool
135 resolve_dest(struct uci_element *e, struct fw3_redirect *redir,
136              struct fw3_state *state)
137 {
138         struct fw3_zone *zone;
139         struct fw3_address *addr;
140         struct list_head *addrs;
141
142         if (!redir->ip_redir.set)
143                 return false;
144
145         list_for_each_entry(zone, &state->zones, list)
146         {
147                 addrs = fw3_resolve_zone_addresses(zone, NULL);
148
149                 if (!addrs)
150                         continue;
151
152                 list_for_each_entry(addr, addrs, list)
153                 {
154                         if (!compare_addr(addr, &redir->ip_redir))
155                                 continue;
156
157                         strncpy(redir->dest.name, zone->name, sizeof(redir->dest.name) - 1);
158                         redir->dest.set = true;
159                         redir->_dest = zone;
160
161                         break;
162                 }
163
164                 fw3_free_list(addrs);
165
166                 if (redir->_dest)
167                         return true;
168         }
169
170         return false;
171 }
172
173 static bool
174 check_local(struct uci_element *e, struct fw3_redirect *redir,
175             struct fw3_state *state)
176 {
177         if (redir->target != FW3_FLAG_DNAT)
178                 return false;
179
180         if (!redir->ip_redir.set)
181                 redir->local = true;
182
183         return redir->local;
184 }
185
186 static void
187 select_helper(struct fw3_state *state, struct fw3_redirect *redir)
188 {
189         struct fw3_protocol *proto;
190         struct fw3_cthelper *helper;
191         int n_matches = 0;
192
193         if (!state->defaults.auto_helper)
194                 return;
195
196         if (!redir->_src || redir->target != FW3_FLAG_DNAT)
197                 return;
198
199         if (!redir->port_redir.set || redir->port_redir.invert)
200                 return;
201
202         if (redir->helper.set || redir->helper.ptr)
203                 return;
204
205         if (list_empty(&redir->proto))
206                 return;
207
208         list_for_each_entry(proto, &redir->proto, list)
209         {
210                 helper = fw3_lookup_cthelper_by_proto_port(state, proto, &redir->port_redir);
211
212                 if (helper)
213                         n_matches++;
214         }
215
216         if (n_matches != 1)
217                 return;
218
219         /* store pointer to auto-selected helper but set ".set" flag to false,
220          * to allow later code to decide between configured or auto-selected
221          * helpers */
222         redir->helper.set = false;
223         redir->helper.ptr = helper;
224
225         set(redir->_src->flags, FW3_FAMILY_V4, FW3_FLAG_HELPER);
226 }
227
228 static bool
229 check_redirect(struct fw3_state *state, struct fw3_redirect *redir, struct uci_element *e)
230 {
231         bool valid;
232
233         if (!redir->enabled)
234                 return false;
235
236         if (redir->src.invert)
237         {
238                 warn_section("redirect", redir, e, "must not have an inverted source");
239                 return false;
240         }
241         else if (redir->src.set && !redir->src.any &&
242                         !(redir->_src = fw3_lookup_zone(state, redir->src.name)))
243         {
244                 warn_section("redirect", redir, e, "refers to not existing zone '%s'",
245                                 redir->src.name);
246                 return false;
247         }
248         else if (redir->dest.set && !redir->dest.any &&
249                         !(redir->_dest = fw3_lookup_zone(state, redir->dest.name)))
250         {
251                 warn_section("redirect", redir, e, "refers to not existing zone '%s'",
252                                 redir->dest.name);
253                 return false;
254         }
255         else if (redir->ipset.set && state->disable_ipsets)
256         {
257                 warn_section("redirect", redir, e, "skipped due to disabled ipset support");
258                 return false;
259         }
260         else if (redir->ipset.set &&
261                         !(redir->ipset.ptr = fw3_lookup_ipset(state, redir->ipset.name)))
262         {
263                 warn_section("redirect", redir, e, "refers to unknown ipset '%s'",
264                                 redir->ipset.name);
265                 return false;
266         }
267         else if (redir->helper.set &&
268                  !(redir->helper.ptr = fw3_lookup_cthelper(state, redir->helper.name)))
269         {
270                 warn_section("redirect", redir, e, "refers to unknown CT helper '%s'",
271                              redir->helper.name);
272                 return false;
273         }
274
275         if (!check_families(e, redir))
276                 return false;
277
278         if (redir->target == FW3_FLAG_UNSPEC)
279         {
280                 warn_section("redirect", redir, e, "has no target specified, defaulting to DNAT");
281                 redir->target = FW3_FLAG_DNAT;
282         }
283         else if (redir->target < FW3_FLAG_DNAT || redir->target > FW3_FLAG_SNAT)
284         {
285                 warn_section("redirect", redir, e, "has invalid target specified, defaulting to DNAT");
286                 redir->target = FW3_FLAG_DNAT;
287         }
288
289         valid = false;
290
291         if (redir->target == FW3_FLAG_DNAT)
292         {
293                 if (redir->src.any)
294                         warn_section("redirect", redir, e, "must not have source '*' for DNAT target");
295                 else if (!redir->_src)
296                         warn_section("redirect", redir, e, "has no source specified");
297                 else if (redir->helper.invert)
298                         warn_section("redirect", redir, e, "must not use a negated helper match");
299                 else
300                 {
301                         set(redir->_src->flags, FW3_FAMILY_V4, redir->target);
302                         valid = true;
303
304                         if (!check_local(e, redir, state) && !redir->dest.set &&
305                                         resolve_dest(e, redir, state))
306                         {
307                                 warn_section("redirect", redir, e,
308                                                 "does not specify a destination, assuming '%s'",
309                                                 redir->dest.name);
310                         }
311
312                         if (redir->reflection && redir->_dest && redir->_src->masq)
313                         {
314                                 set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_ACCEPT);
315                                 set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_DNAT);
316                                 set(redir->_dest->flags, FW3_FAMILY_V4, FW3_FLAG_SNAT);
317                         }
318
319                         if (redir->helper.ptr)
320                                 set(redir->_src->flags, FW3_FAMILY_V4, FW3_FLAG_HELPER);
321                 }
322         }
323         else
324         {
325                 if (redir->dest.any)
326                         warn_section("redirect", redir, e,
327                                         "must not have destination '*' for SNAT target");
328                 else if (!redir->_dest)
329                         warn_section("redirect", redir, e, "has no destination specified");
330                 else if (!redir->ip_dest.set)
331                         warn_section("redirect", redir, e, "has no src_dip option specified");
332                 else if (!list_empty(&redir->mac_src))
333                         warn_section("redirect", redir, e, "must not use 'src_mac' option for SNAT target");
334                 else if (redir->helper.set)
335                         warn_section("redirect", redir, e, "must not use 'helper' option for SNAT target");
336                 else
337                 {
338                         set(redir->_dest->flags, FW3_FAMILY_V4, redir->target);
339                         valid = true;
340                 }
341         }
342
343         if (list_empty(&redir->proto))
344         {
345                 warn_section("redirect", redir, e, "does not specify a protocol, assuming TCP+UDP");
346                 fw3_parse_protocol(&redir->proto, "tcpudp", true);
347         }
348
349         if (!valid)
350                 return false;
351
352         if (redir->target == FW3_FLAG_DNAT && !redir->port_redir.set)
353                 redir->port_redir = redir->port_dest;
354
355         return true;
356 }
357
358 static struct fw3_redirect *
359 fw3_alloc_redirect(struct fw3_state *state)
360 {
361         struct fw3_redirect *redir;
362
363         redir = calloc(1, sizeof(*redir));
364         if (!redir)
365                 return NULL;
366
367         INIT_LIST_HEAD(&redir->proto);
368         INIT_LIST_HEAD(&redir->mac_src);
369
370         redir->enabled = true;
371         redir->reflection = true;
372
373         list_add_tail(&redir->list, &state->redirects);
374
375         return redir;
376 }
377
378 void
379 fw3_load_redirects(struct fw3_state *state, struct uci_package *p,
380                 struct blob_attr *a)
381 {
382         struct uci_section *s;
383         struct uci_element *e;
384         struct fw3_redirect *redir;
385         struct blob_attr *entry;
386         unsigned rem;
387
388         INIT_LIST_HEAD(&state->redirects);
389
390         blob_for_each_attr(entry, a, rem)
391         {
392                 const char *type;
393                 const char *name = "ubus redirect";
394
395                 if (!fw3_attr_parse_name_type(entry, &name, &type))
396                         continue;
397
398                 if (strcmp(type, "redirect"))
399                         continue;
400
401                 redir = fw3_alloc_redirect(state);
402                 if (!redir)
403                         continue;
404
405                 if (!fw3_parse_blob_options(redir, fw3_redirect_opts, entry, name))
406                 {
407                         warn_section("redirect", redir, NULL, "skipped due to invalid options");
408                         fw3_free_redirect(redir);
409                         continue;
410                 }
411
412                 if (!check_redirect(state, redir, NULL)) {
413                         fw3_free_redirect(redir);
414                         continue;
415                 }
416
417                 select_helper(state, redir);
418         }
419
420         uci_foreach_element(&p->sections, e)
421         {
422                 s = uci_to_section(e);
423
424                 if (strcmp(s->type, "redirect"))
425                         continue;
426
427                 redir = fw3_alloc_redirect(state);
428                 if (!redir)
429                         continue;
430
431                 if (!fw3_parse_options(redir, fw3_redirect_opts, s))
432                 {
433                         warn_elem(e, "skipped due to invalid options");
434                         fw3_free_redirect(redir);
435                         continue;
436                 }
437
438                 if (!check_redirect(state, redir, e)) {
439                         fw3_free_redirect(redir);
440                         continue;
441                 }
442
443                 select_helper(state, redir);
444         }
445 }
446
447 static void
448 append_chain_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir)
449 {
450         if (redir->target == FW3_FLAG_DNAT)
451                 fw3_ipt_rule_append(r, "zone_%s_prerouting", redir->src.name);
452         else
453                 fw3_ipt_rule_append(r, "zone_%s_postrouting", redir->dest.name);
454 }
455
456 static void
457 set_redirect(struct fw3_ipt_rule *r, struct fw3_port *port)
458 {
459         char buf[sizeof("65535-65535\0")];
460
461         fw3_ipt_rule_target(r, "REDIRECT");
462
463         if (port && port->set)
464         {
465                 if (port->port_min == port->port_max)
466                         sprintf(buf, "%u", port->port_min);
467                 else
468                         snprintf(buf, sizeof(buf), "%u-%u", port->port_min, port->port_max);
469
470                 fw3_ipt_rule_addarg(r, false, "--to-ports", buf);
471         }
472 }
473
474 static void
475 set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target,
476               struct fw3_address *addr, struct fw3_port *port)
477 {
478         char buf[sizeof("255.255.255.255:65535-65535\0")];
479
480         buf[0] = '\0';
481
482         if (addr && addr->set)
483         {
484                 inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf));
485         }
486
487         if (port && port->set)
488         {
489                 if (port->port_min == port->port_max)
490                         sprintf(buf + strlen(buf), ":%u", port->port_min);
491                 else
492                         sprintf(buf + strlen(buf), ":%u-%u",
493                                 port->port_min, port->port_max);
494         }
495
496         if (target == FW3_FLAG_DNAT)
497         {
498                 fw3_ipt_rule_target(r, "DNAT");
499                 fw3_ipt_rule_addarg(r, false, "--to-destination", buf);
500         }
501         else
502         {
503                 fw3_ipt_rule_target(r, "SNAT");
504                 fw3_ipt_rule_addarg(r, false, "--to-source", buf);
505         }
506 }
507
508 static void
509 set_target_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir)
510 {
511         if (redir->local)
512                 set_redirect(r, &redir->port_redir);
513         else if (redir->target == FW3_FLAG_DNAT)
514                 set_snat_dnat(r, redir->target, &redir->ip_redir, &redir->port_redir);
515         else
516                 set_snat_dnat(r, redir->target, &redir->ip_dest, &redir->port_dest);
517 }
518
519 static void
520 set_comment(struct fw3_ipt_rule *r, const char *name, int num, const char *suffix)
521 {
522         if (name)
523         {
524                 if (suffix)
525                         fw3_ipt_rule_comment(r, "%s (%s)", name, suffix);
526                 else
527                         fw3_ipt_rule_comment(r, name);
528         }
529         else
530         {
531                 if (suffix)
532                         fw3_ipt_rule_comment(r, "@redirect[%u] (%s)", num, suffix);
533                 else
534                         fw3_ipt_rule_comment(r, "@redirect[%u]", num);
535         }
536 }
537
538 static void
539 print_redirect(struct fw3_ipt_handle *h, struct fw3_state *state,
540                struct fw3_redirect *redir, int num,
541                struct fw3_protocol *proto, struct fw3_mac *mac)
542 {
543         struct fw3_ipt_rule *r;
544         struct fw3_address *src, *dst;
545         struct fw3_port *spt, *dpt;
546
547         switch (h->table)
548         {
549         case FW3_TABLE_NAT:
550                 src = &redir->ip_src;
551                 dst = &redir->ip_dest;
552                 spt = &redir->port_src;
553                 dpt = &redir->port_dest;
554
555                 if (redir->target == FW3_FLAG_SNAT)
556                 {
557                         dst = &redir->ip_redir;
558                         dpt = &redir->port_redir;
559                 }
560
561                 r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst);
562                 fw3_ipt_rule_sport_dport(r, spt, dpt);
563                 fw3_ipt_rule_mac(r, mac);
564                 fw3_ipt_rule_ipset(r, &redir->ipset);
565                 fw3_ipt_rule_helper(r, &redir->helper);
566                 fw3_ipt_rule_limit(r, &redir->limit);
567                 fw3_ipt_rule_time(r, &redir->time);
568                 fw3_ipt_rule_mark(r, &redir->mark);
569                 set_target_nat(r, redir);
570                 fw3_ipt_rule_extra(r, redir->extra);
571                 set_comment(r, redir->name, num, NULL);
572                 append_chain_nat(r, redir);
573                 break;
574
575         case FW3_TABLE_RAW:
576                 if (redir->target == FW3_FLAG_DNAT && redir->helper.ptr)
577                 {
578                         if (!fw3_cthelper_check_proto(redir->helper.ptr, proto))
579                         {
580                                 info("     ! Skipping protocol %s since helper '%s' does not support it",
581                                      fw3_protoname(proto), redir->helper.ptr->name);
582                                 return;
583                         }
584
585                         if (!redir->helper.set)
586                                 info("     - Auto-selected conntrack helper '%s' based on proto/port",
587                                      redir->helper.ptr->name);
588
589                         r = fw3_ipt_rule_create(h, proto, NULL, NULL, &redir->ip_src, &redir->ip_redir);
590                         fw3_ipt_rule_sport_dport(r, &redir->port_src, &redir->port_redir);
591                         fw3_ipt_rule_mac(r, mac);
592                         fw3_ipt_rule_ipset(r, &redir->ipset);
593                         fw3_ipt_rule_limit(r, &redir->limit);
594                         fw3_ipt_rule_time(r, &redir->time);
595                         fw3_ipt_rule_mark(r, &redir->mark);
596                         fw3_ipt_rule_addarg(r, false, "-m", "conntrack");
597                         fw3_ipt_rule_addarg(r, false, "--ctstate", "DNAT");
598                         fw3_ipt_rule_target(r, "CT");
599                         fw3_ipt_rule_addarg(r, false, "--helper", redir->helper.ptr->name);
600                         set_comment(r, redir->name, num, "CT helper");
601                         fw3_ipt_rule_append(r, "zone_%s_helper", redir->_src->name);
602                 }
603                 break;
604
605         default:
606                 break;
607         }
608 }
609
610 static void
611 print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state,
612                  struct fw3_redirect *redir, int num,
613                  struct fw3_protocol *proto, struct fw3_address *ra,
614                  struct fw3_address *ia, struct fw3_address *ea)
615 {
616         struct fw3_ipt_rule *r;
617
618         switch (h->table)
619         {
620         case FW3_TABLE_NAT:
621                 r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, ea);
622                 fw3_ipt_rule_sport_dport(r, NULL, &redir->port_dest);
623                 fw3_ipt_rule_limit(r, &redir->limit);
624                 fw3_ipt_rule_time(r, &redir->time);
625                 set_comment(r, redir->name, num, "reflection");
626                 set_snat_dnat(r, FW3_FLAG_DNAT, &redir->ip_redir, &redir->port_redir);
627                 fw3_ipt_rule_replace(r, "zone_%s_prerouting", redir->dest.name);
628
629                 r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, &redir->ip_redir);
630                 fw3_ipt_rule_sport_dport(r, NULL, &redir->port_redir);
631                 fw3_ipt_rule_limit(r, &redir->limit);
632                 fw3_ipt_rule_time(r, &redir->time);
633                 set_comment(r, redir->name, num, "reflection");
634                 set_snat_dnat(r, FW3_FLAG_SNAT, ra, NULL);
635                 fw3_ipt_rule_replace(r, "zone_%s_postrouting", redir->dest.name);
636                 break;
637
638         default:
639                 break;
640         }
641 }
642
643 static void
644 expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state,
645                 struct fw3_redirect *redir, int num)
646 {
647         struct list_head *ext_addrs, *int_addrs;
648         struct fw3_address *ext_addr, *int_addr, ref_addr;
649         struct fw3_protocol *proto;
650         struct fw3_mac *mac;
651
652         if (redir->name)
653                 info("   * Redirect '%s'", redir->name);
654         else
655                 info("   * Redirect #%u", num);
656
657         if (!fw3_is_family(redir->_src, handle->family) ||
658                 !fw3_is_family(redir->_dest, handle->family))
659         {
660                 info("     ! Skipping due to different family of zone");
661                 return;
662         }
663
664         if (!fw3_is_family(&redir->ip_src, handle->family) ||
665             !fw3_is_family(&redir->ip_dest, handle->family) ||
666                 !fw3_is_family(&redir->ip_redir, handle->family))
667         {
668                 if (!redir->ip_src.resolved ||
669                     !redir->ip_dest.resolved ||
670                     !redir->ip_redir.resolved)
671                         info("     ! Skipping due to different family of ip address");
672
673                 return;
674         }
675
676         if (redir->ipset.ptr)
677         {
678                 if (!fw3_is_family(redir->ipset.ptr, handle->family))
679                 {
680                         info("     ! Skipping due to different family in ipset");
681                         return;
682                 }
683
684                 if (!fw3_check_ipset(redir->ipset.ptr))
685                 {
686                         info("     ! Skipping due to missing ipset '%s'",
687                              redir->ipset.ptr->external ?
688                                         redir->ipset.ptr->external : redir->ipset.ptr->name);
689                         return;
690                 }
691
692                 set(redir->ipset.ptr->flags, handle->family, handle->family);
693         }
694
695         fw3_foreach(proto, &redir->proto)
696         fw3_foreach(mac, &redir->mac_src)
697                 print_redirect(handle, state, redir, num, proto, mac);
698
699         /* reflection rules */
700         if (redir->target != FW3_FLAG_DNAT || !redir->reflection || redir->local)
701                 return;
702
703         if (!redir->_dest || !redir->_src->masq)
704                 return;
705
706         ext_addrs = fw3_resolve_zone_addresses(redir->_src, &redir->ip_dest);
707         int_addrs = fw3_resolve_zone_addresses(redir->_dest, NULL);
708
709         if (!ext_addrs || !int_addrs)
710                 goto out;
711
712         list_for_each_entry(ext_addr, ext_addrs, list)
713         {
714                 if (!fw3_is_family(ext_addr, handle->family))
715                         continue;
716
717                 list_for_each_entry(int_addr, int_addrs, list)
718                 {
719                         if (!fw3_is_family(int_addr, handle->family))
720                                 continue;
721
722                         fw3_foreach(proto, &redir->proto)
723                         {
724                                 if (!proto)
725                                         continue;
726
727                                 if (redir->reflection_src == FW3_REFLECTION_INTERNAL)
728                                         ref_addr = *int_addr;
729                                 else
730                                         ref_addr = *ext_addr;
731
732                                 ref_addr.mask.v4.s_addr = 0xFFFFFFFF;
733                                 ext_addr->mask.v4.s_addr = 0xFFFFFFFF;
734
735                                 print_reflection(handle, state, redir, num, proto,
736                                                                  &ref_addr, int_addr, ext_addr);
737                         }
738                 }
739         }
740
741 out:
742         fw3_free_list(ext_addrs);
743         fw3_free_list(int_addrs);
744 }
745
746 void
747 fw3_print_redirects(struct fw3_ipt_handle *handle, struct fw3_state *state)
748 {
749         int num = 0;
750         struct fw3_redirect *redir;
751
752         if (handle->family == FW3_FAMILY_V6)
753                 return;
754
755         if (handle->table != FW3_TABLE_FILTER &&
756             handle->table != FW3_TABLE_NAT &&
757             handle->table != FW3_TABLE_RAW)
758                 return;
759
760         list_for_each_entry(redir, &state->redirects, list)
761         {
762                 if (handle->table == FW3_TABLE_RAW && !redir->helper.ptr)
763                         continue;
764
765                 expand_redirect(handle, state, redir, num++);
766         }
767 }