firewall3 currently creates one rule for each interface that is a member of a
zone. On for example devices with multiple interfaces, the current firewall3
behavior quickly leads to a lot of rules. In order to reduce the number of
rules, this patch replaces the per-interface rules with ipset matches (if ipset
is available). Since 2011, ipset has supported the set type "hash:net,iface".
By adding (and matching on) on pairs consiting of the v4/v6 any-address and an
interface name, we get the same behavior as the current interface-rules.
After applying this patch (and assuming ipset is available), the following
actions are performed when a zone is created:
* We creates (allocate) an ipset of type "hash:net,iface" for each zone. The
name follows the following format: zone__<4/6>_set.
* If creating a set fails, then we ignore the zone. This is something we can
change, but my reason for this behavior is to have consistent firewall rules.
I.e., zone-rules either match on ipset or interface names, and not a mix.
* Each set is populated with pairs consisting of the IPv4/IPv6 any-address and
an interface name, for example "0.0.0.0/0, eth0.2".
* Instead of one rule per device, a single rule is created matching on the
ipset.
* The check used to select the OUTPUT/PREROUTING-chain when adding rules to the
raw-table has been moved to print_interface_rules_{default,set}. The motivation
behind this move was to avoid changing print_interface_rule() too much. As far
as I can see (and have tested), the logic for selecting chain/creating the
rules is the same as before.
Because the change introduced by this patch is quite intrusive and I am sure
there will be comments/disagreements/suggestions, I have sent this patch as an
RFC. One thing that I am aware of and will fix before the final submission, is
to add support for printing ipsets. Right now "fw3 print" prints per-interface
rules.
Signed-off-by: Kristian Evensen
---
ipsets.c | 38 ++-
ipsets.h | 7 ++
main.c| 8 +-
options.c | 3 +-
options.h | 4 +
zones.c | 291 +++---
zones.h | 15 ++-
7 files changed, 347 insertions(+), 19 deletions(-)
diff --git a/ipsets.c b/ipsets.c
index 280845b..e052278 100644
--- a/ipsets.c
+++ b/ipsets.c
@@ -81,6 +81,8 @@ static struct ipset_type ipset_types[] = {
OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
T(HASH, NET, PORT, UNSPEC, 0,
OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
+ T(HASH, NET, IFACE, UNSPEC, 0,
+ OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
T(HASH, IP, PORT, IP, 0,
OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM),
T(HASH, IP, PORT, NET,0,
@@ -247,7 +249,7 @@ check_ipset(struct fw3_state *state, struct fw3_ipset
*ipset, struct uci_element
return false;
}
-static struct fw3_ipset *
+struct fw3_ipset *
fw3_alloc_ipset(struct fw3_state *state)
{
struct fw3_ipset *ipset;
@@ -611,3 +613,37 @@ fw3_ipsets_update_run_state(enum fw3_family family, struct
fw3_state *run_state,
ipset_run->reload_set = ipset_cfg->reload_set;
}
}
+
+void
+fw3_ipset_add_devices(struct list_head *devices, enum fw3_family family,
+ const char *set_name)
+{
+ struct fw3_device *dev;
+ bool exec = false;
+ const char *addr;
+
+ fw3_foreach(dev, devices) {
+ if (!dev)
+ continue;
+
+ if (!exec) {
+ exec = fw3_command_pipe(false, "ipset", "-exist", "-");
+
+ if (!exec)
+ return;
+ }
+
+ if (family == FW3_FAMILY_V4) {
+ addr = "0.0.0.0/0";
+ } else {
+ addr = "::/0";
+ }
+
+ fw3_pr("add %s %s,%s\n", set_name, addr, dev->name);
+ }
+
+ if (exec) {
+ fw3_pr("quit\n");
+ fw3_command_close();
+ }
+}
diff --git a/ipsets.h b/ipsets.h
index 76078d4..19528e9 100644
--- a/ipsets.h
+++ b/ipsets.h
@@ -41,6 +41,13 @@ void
fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state
*run_state,
struct fw3_state *cfg_state);
+struct fw3_ipset *
+fw3_alloc_ipset(struct fw3_state *state);
+
+void
+fw3_ipset_add_devices(struct list_head *devices, enum fw3_family family,
+ const char *set_name);
+
static inline void fw3_free_ipset(struct fw3_ipset *ipset)
{
list_del(>list);
diff --git a/main.c b/main.c
index 7ad00b4..7b9c9e3 100644
--- a/main.c
+++ b/main.c
@@ -266,6 +266,9 @@ start(void)
continue;
}
+ if (!print_family)
+ fw3_fill_zone_ipsets(family, cfg_state);
+
for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
{
if