+static bool
+compare_addr(struct fw3_address *a, struct fw3_address *b)
+{
+ if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4)
+ return false;
+
+ return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
+ (b->address.v4.s_addr & a->mask.v4.s_addr));
+}
+
+static bool
+resolve_dest(struct uci_element *e, struct fw3_redirect *redir,
+ struct fw3_state *state)
+{
+ struct fw3_zone *zone;
+ struct fw3_address *addr;
+ struct list_head *addrs;
+
+ if (!redir->ip_redir.set)
+ return false;
+
+ list_for_each_entry(zone, &state->zones, list)
+ {
+ addrs = fw3_resolve_zone_addresses(zone, NULL);
+
+ if (!addrs)
+ continue;
+
+ list_for_each_entry(addr, addrs, list)
+ {
+ if (!compare_addr(addr, &redir->ip_redir))
+ continue;
+
+ strncpy(redir->dest.name, zone->name, sizeof(redir->dest.name));
+ redir->dest.set = true;
+ redir->_dest = zone;
+
+ break;
+ }
+
+ fw3_free_list(addrs);
+
+ if (redir->_dest)
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+check_local(struct uci_element *e, struct fw3_redirect *redir,
+ struct fw3_state *state)
+{
+ struct fw3_zone *zone;
+ struct fw3_device *net;
+ struct fw3_address *addr, *tmp;
+
+ if (redir->target != FW3_FLAG_DNAT)
+ return false;
+
+ if (!redir->ip_redir.set)
+ redir->local = true;
+
+ if (redir->local)
+ return true;
+
+ list_for_each_entry(zone, &state->zones, list)
+ {
+ list_for_each_entry(net, &zone->networks, list)
+ {
+ LIST_HEAD(addrs);
+
+ fw3_ubus_address(&addrs, net->name);
+ list_for_each_entry_safe(addr, tmp, &addrs, list)
+ {
+ if (!redir->local && compare_addr(&redir->ip_redir, addr)) {
+ warn_elem(e, "refers to a destination address on this router, "
+ "assuming port redirection");
+
+ redir->local = true;
+ }
+
+ list_del(&addr->list);
+ free(addr);
+ }
+
+ if (redir->local)
+ return true;
+ }
+ }
+
+ return false;
+}
+