diff mbox series

bridge: Support nf_call_{ip,ip6,arp}tables attributes

Message ID 20220913211601.30750-1-riemensberger@cadami.net
State New
Headers show
Series bridge: Support nf_call_{ip,ip6,arp}tables attributes | expand

Commit Message

Maximilian Riemensberger Sept. 13, 2022, 9:16 p.m. UTC
The bridge driver allows passing bridged frames to netfilter.  Add
bridge config options nf_call_iptables, nf_call_ip6tables,
nf_call_arptables to opt in.

Signed-off-by: Maximilian Riemensberger <riemensberger@cadami.net>
---
 bridge.c       | 15 +++++++++++++++
 system-dummy.c |  6 ++++--
 system-linux.c |  3 +++
 system.h       |  4 ++++
 4 files changed, 26 insertions(+), 2 deletions(-)

Comments

Etienne Champetier Sept. 14, 2022, 12:24 a.m. UTC | #1
Hi Maximilian

Le mar. 13 sept. 2022, 17:23, Maximilian Riemensberger
<riemensberger@cadami.net> a écrit :
>
> The bridge driver allows passing bridged frames to netfilter.  Add
> bridge config options nf_call_iptables, nf_call_ip6tables,
> nf_call_arptables to opt in.

You should have a look at using nftables instead,
no need for those coarse grain options and way more flexible / powerful.
https://wiki.nftables.org/wiki-nftables/index.php/Bridge_filtering

Here an example switching from iptables + br_netfilter to nftables +
table bridge:
https://github.com/nccgroup/phantap/commit/b066ce2c2bb21038958a117b3b67413e9a0ea0a3
https://github.com/openwrt/packages/commit/66b7c19992688b924d2ecbbbc20781b32a82452f

Etienne
Maximilian Riemensberger Sept. 14, 2022, 6:50 a.m. UTC | #2
Hi Etienne,

On 9/14/22 02:24, Etienne Champetier wrote:
>> The bridge driver allows passing bridged frames to netfilter.  Add
>> bridge config options nf_call_iptables, nf_call_ip6tables,
>> nf_call_arptables to opt in.
> 
> You should have a look at using nftables instead,
> no need for those coarse grain options and way more flexible / powerful.
> https://wiki.nftables.org/wiki-nftables/index.php/Bridge_filtering
> 
> Here an example switching from iptables + br_netfilter to nftables +
> table bridge:
> https://github.com/nccgroup/phantap/commit/b066ce2c2bb21038958a117b3b67413e9a0ea0a3
> https://github.com/openwrt/packages/commit/66b7c19992688b924d2ecbbbc20781b32a82452f

Thanks for the hints. Unfortunately, we use openNDS for splash portals, which is relying heavily on legacy iptables etc.  So we are well served by those bridge settings.  Exposing them as config parameters makes it much easier to configure them correctly per bridge within the lifecycle of an interface.  We used to just globally enable the corresponding sysctls, but that's even cruder and has performance downsides if not all bridges need the filtering.

Side note: How can I ensure with nftables that the cost of going to the firewall (ebtables/iptables replacement) is only incurred on some bridges? Or does nftables figure that out on it's own?  With nf_call_iptables, I can set it on a per bridge basis.

Best
Max

> 
> Etienne
Maximilian Riemensberger Sept. 14, 2022, 6:51 a.m. UTC | #3
Hi Etienne,

On 9/14/22 02:24, Etienne Champetier wrote:
>> The bridge driver allows passing bridged frames to netfilter.  Add
>> bridge config options nf_call_iptables, nf_call_ip6tables,
>> nf_call_arptables to opt in.
> 
> You should have a look at using nftables instead,
> no need for those coarse grain options and way more flexible / powerful.
> https://wiki.nftables.org/wiki-nftables/index.php/Bridge_filtering
> 
> Here an example switching from iptables + br_netfilter to nftables +
> table bridge:
> https://github.com/nccgroup/phantap/commit/b066ce2c2bb21038958a117b3b67413e9a0ea0a3
> https://github.com/openwrt/packages/commit/66b7c19992688b924d2ecbbbc20781b32a82452f

Thanks for the hints. Unfortunately, we use openNDS for splash portals, which is relying heavily on legacy iptables etc.  So we are well served by those bridge settings.  Exposing them as config parameters makes it much easier to configure them correctly per bridge within the lifecycle of an interface.  We used to just globally enable the corresponding sysctls, but that's even cruder and has performance downsides if not all bridges need the filtering.

Side note: How can I ensure with nftables that the cost of going to the firewall (ebtables/iptables replacement) is only incurred on some bridges? Or does nftables figure that out on it's own?  With nf_call_iptables, I can set it on a per bridge basis.

Best
Max

> 
> Etienne
diff mbox series

Patch

diff --git a/bridge.c b/bridge.c
index 7e61b9d..153e41f 100644
--- a/bridge.c
+++ b/bridge.c
@@ -43,6 +43,9 @@  enum {
 	BRIDGE_ATTR_HAS_VLANS,
 	BRIDGE_ATTR_STP_KERNEL,
 	BRIDGE_ATTR_STP_PROTO,
+	BRIDGE_ATTR_NF_CALL_IPTABLES,
+	BRIDGE_ATTR_NF_CALL_IP6TABLES,
+	BRIDGE_ATTR_NF_CALL_ARPTABLES,
 	__BRIDGE_ATTR_MAX
 };
 
@@ -66,6 +69,9 @@  static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
 	[BRIDGE_ATTR_HAS_VLANS] = { "__has_vlans", BLOBMSG_TYPE_BOOL }, /* internal */
 	[BRIDGE_ATTR_STP_KERNEL] = { "stp_kernel", BLOBMSG_TYPE_BOOL },
 	[BRIDGE_ATTR_STP_PROTO] = { "stp_proto", BLOBMSG_TYPE_STRING },
+	[BRIDGE_ATTR_NF_CALL_IPTABLES] = { "nf_call_iptables", BLOBMSG_TYPE_BOOL },
+	[BRIDGE_ATTR_NF_CALL_IP6TABLES] = { "nf_call_ip6tables", BLOBMSG_TYPE_BOOL },
+	[BRIDGE_ATTR_NF_CALL_ARPTABLES] = { "nf_call_arptables", BLOBMSG_TYPE_BOOL },
 };
 
 static const struct uci_blob_param_info bridge_attr_info[__BRIDGE_ATTR_MAX] = {
@@ -1114,6 +1120,15 @@  bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
 
 	if ((cur = tb[BRIDGE_ATTR_VLAN_FILTERING]))
 		cfg->vlan_filtering = blobmsg_get_bool(cur);
+
+	if ((cur = tb[BRIDGE_ATTR_NF_CALL_IPTABLES]))
+		cfg->nf_call_iptables = blobmsg_get_bool(cur);
+
+	if ((cur = tb[BRIDGE_ATTR_NF_CALL_IP6TABLES]))
+		cfg->nf_call_ip6tables = blobmsg_get_bool(cur);
+
+	if ((cur = tb[BRIDGE_ATTR_NF_CALL_ARPTABLES]))
+		cfg->nf_call_arptables = blobmsg_get_bool(cur);
 }
 
 static enum dev_change_type
diff --git a/system-dummy.c b/system-dummy.c
index b13bc87..811404d 100644
--- a/system-dummy.c
+++ b/system-dummy.c
@@ -32,8 +32,10 @@  int system_init(void)
 
 int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
 {
-	D(SYSTEM, "brctl addbr %s vlan_filtering=%d\n",
-	  bridge->ifname, cfg->vlan_filtering);
+	D(SYSTEM,
+	  "brctl addbr %s vlan_filtering=%d nf_call_iptables=%d nf_call_ip6tables=%d nf_call_arptables=%d\n",
+	  bridge->ifname, cfg->vlan_filtering, cfg->nf_call_iptables,
+	  cfg->nf_call_ip6tables, cfg->nf_call_arptables);
 	return 0;
 }
 
diff --git a/system-linux.c b/system-linux.c
index 0f13a99..71e9ec6 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -1342,6 +1342,9 @@  int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg)
 	}
 
 	nla_put_u8(msg, IFLA_BR_VLAN_FILTERING, !!cfg->vlan_filtering);
+	nla_put_u8(msg, IFLA_BR_NF_CALL_IPTABLES, !!cfg->nf_call_iptables);
+	nla_put_u8(msg, IFLA_BR_NF_CALL_IP6TABLES, !!cfg->nf_call_ip6tables);
+	nla_put_u8(msg, IFLA_BR_NF_CALL_ARPTABLES, !!cfg->nf_call_arptables);
 	nla_put_u16(msg, IFLA_BR_PRIORITY, cfg->priority);
 	nla_put_u32(msg, IFLA_BR_HELLO_TIME, sec_to_jiffies(cfg->hello_time));
 	nla_put_u32(msg, IFLA_BR_MAX_AGE, sec_to_jiffies(cfg->max_age));
diff --git a/system.h b/system.h
index 0f08c26..c551b13 100644
--- a/system.h
+++ b/system.h
@@ -208,6 +208,10 @@  struct bridge_config {
 	int hash_max;
 
 	bool vlan_filtering;
+
+	bool nf_call_iptables;
+	bool nf_call_ip6tables;
+	bool nf_call_arptables;
 };
 
 enum macvlan_opt {