lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 14 Aug 2015 06:23:52 +0000
From:	Premkumar Jonnala <pjonnala@...adcom.com>
To:	"netdev@...r.kernel.org" <netdev@...r.kernel.org>
Subject: [PATCH] bridge: Enable configuration of ageing interval for bridges
 and switch devices.

Bridge devices have ageing interval used to age out MAC addresses
from FDB.  This ageing interval was not configuratble.

Enable netlink based configuration of ageing interval for bridges and
switch devices.  The ageing interval changes the timer used to purge
inactive FDB entries in bridges.  The ageing interval config is
propagated to switch devices, so that platform or hardware based
ageing works according to configuration.

Signed-off-by: Premkumar Jonnala <pjonnala@...adcom.com>

---

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 607b5f4..e3b0c45 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1053,7 +1053,16 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
  *	This function is used to pass protocol port error state information
  *	to the switch driver. The switch driver can react to the proto_down
  *      by doing a phys down on the associated switch port.
- *
+ * int (*ndo_bridge_setageing)(const struct net_device *dev,
+ *			       int ageing_interval);
+ *     Called to set FDB aging interval for a given bridge device.
+ * int (*ndo_bridge_getageing_nl)(struct sk_buff *skb,
+ *				const struct net_device *dev,
+ *				struct netlink_callback *cb);
+ *     Called to return the ageing interval for the given bridge device,
+ *     in a format suitable for netlink messaging.
+ * int (*ndo_bridge_getageing)(const struct net_device *dev);
+ *     Called to retrieve the ageing interval for the given bridge device.
  */
 struct net_device_ops {
 	int			(*ndo_init)(struct net_device *dev);
@@ -1226,6 +1235,13 @@ struct net_device_ops {
 	int			(*ndo_get_iflink)(const struct net_device *dev);
 	int			(*ndo_change_proto_down)(struct net_device *dev,
 							 bool proto_down);
+	int	(*ndo_bridge_setageing)(const struct net_device *dev,
+					int ageing_interval);
+	int	(*ndo_bridge_getageing_nl)(struct sk_buff *skb,
+					   const struct net_device *dev,
+					   struct netlink_callback *cb);
+
+	int	(*ndo_bridge_getageing)(const struct net_device *dev);
 };
 
 /**
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 89da893..7186fea 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -129,6 +129,10 @@ int switchdev_port_attr_get(struct net_device *dev,
 			    struct switchdev_attr *attr);
 int switchdev_port_attr_set(struct net_device *dev,
 			    struct switchdev_attr *attr);
+int netdev_switch_ageing_set(struct net_device *dev, int ageing_interval);
+int netdev_switch_ageing_get(struct sk_buff *skb,
+			     const struct net_device *dev,
+			     struct netlink_callback *cb);
 int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj);
 int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj);
 int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj);
@@ -163,6 +167,17 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
 
 #else
 
+static inline int netdev_switch_ageing_set(struct net_device *dev,
+					int ageing_interval)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int netdev_switch_ageing_get(struct net_device *dev)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline int switchdev_port_attr_get(struct net_device *dev,
 					  struct switchdev_attr *attr)
 {
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 3635b77..a32ab4d 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -199,4 +199,23 @@ enum {
 };
 #define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
 
+struct admsg {
+	__u8 adm_family;
+	__u8 adm_pad1;
+	__u16 adm_pad2;
+	__s32 adm_ifindex;
+	__u16 adm_ageing_interval;
+};
+
+/* The value of this macro is based on the value recommended by IEEE
+ * standard 802.1d.
+ */
+#define MIN_AGEING_INTERVAL_SECS (10)
+
+/* The value of DEFAULT_AGEING_INTERVAL_SECS is the default ageing
+ * interval that was used in br_device.c.  This default value is also
+ * recommended by IEEE Standard 802.1d.
+ */
+#define DEFAULT_AGEING_INTERVAL_SECS (300)
+
 #endif /* _UAPI_LINUX_IF_BRIDGE_H */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 47d24cb..9321818 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -139,6 +139,13 @@ enum {
 	RTM_GETNSID = 90,
 #define RTM_GETNSID RTM_GETNSID
 
+	RTM_SETAGEING = 92,
+#define RTM_SETAGEING RTM_SETAGEING
+	RTM_SETDEFAULTAGEING = 93,
+#define RTM_SETDEFAULTAGEING RTM_SETDEFAULTAGEING
+	RTM_GETAGEING = 94,
+#define RTM_GETAGEING RTM_GETAGEING
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 4ff77a1..2c81190 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -18,6 +18,7 @@
 #include <linux/ethtool.h>
 #include <linux/list.h>
 #include <linux/netfilter_bridge.h>
+#include <net/netlink.h>
 
 #include <asm/uaccess.h>
 #include "br_private.h"
@@ -309,6 +310,43 @@ static int br_del_slave(struct net_device *dev, struct net_device *slave_dev)
 	return br_del_if(br, slave_dev);
 }
 
+static int br_setageing(const struct net_device *dev, int ageing_interval)
+{
+	struct net_bridge *br = netdev_priv(dev);
+
+	return br_fdb_setageing(br, ageing_interval);
+}
+
+static int br_getageing(const struct net_device *dev)
+{
+	struct net_bridge *br = netdev_priv(dev);
+
+	return br->ageing_time / HZ;
+}
+
+static int br_getageing_nl(struct sk_buff *skb,
+			   const struct net_device *dev,
+			   struct netlink_callback *cb)
+{
+	struct nlmsghdr *nlhdr;
+	struct admsg *amsg;
+
+	struct net_bridge *br = netdev_priv(dev);
+
+	nlhdr = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+			  cb->nlh->nlmsg_seq, RTM_GETAGEING, sizeof(*amsg), 0);
+	if (!nlhdr)
+		return -EMSGSIZE;
+
+	amsg = nlmsg_data(nlhdr);
+
+	amsg->adm_family = AF_BRIDGE;
+	amsg->adm_ifindex = dev->ifindex;
+	amsg->adm_ageing_interval = br->ageing_time / HZ;
+
+	return 0;
+}
+
 static const struct ethtool_ops br_ethtool_ops = {
 	.get_drvinfo    = br_getinfo,
 	.get_link	= ethtool_op_get_link,
@@ -339,6 +377,9 @@ static const struct net_device_ops br_netdev_ops = {
 	.ndo_bridge_getlink	 = br_getlink,
 	.ndo_bridge_setlink	 = br_setlink,
 	.ndo_bridge_dellink	 = br_dellink,
+	.ndo_bridge_setageing    = br_setageing,
+	.ndo_bridge_getageing_nl = br_getageing_nl,
+	.ndo_bridge_getageing    = br_getageing,
 };
 
 static void br_dev_free(struct net_device *dev)
@@ -391,7 +432,7 @@ void br_dev_setup(struct net_device *dev)
 	br->bridge_max_age = br->max_age = 20 * HZ;
 	br->bridge_hello_time = br->hello_time = 2 * HZ;
 	br->bridge_forward_delay = br->forward_delay = 15 * HZ;
-	br->ageing_time = 300 * HZ;
+	br->ageing_time = DEFAULT_AGEING_INTERVAL_SECS * HZ;
 
 	br_netfilter_rtable_init(br);
 	br_stp_timer_init(br);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 9e9875d..c46f734 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -283,6 +283,25 @@ out:
 	spin_unlock_bh(&br->hash_lock);
 }
 
+int br_fdb_setageing(struct net_bridge *br, int ageing_interval)
+{
+	unsigned long next_timer;
+	unsigned long exp_time;
+
+	if (ageing_interval < MIN_AGEING_INTERVAL_SECS)
+		return -EINVAL;
+
+	br->ageing_time = ageing_interval * HZ;
+
+	next_timer = jiffies + hold_time(br);
+	exp_time = br->gc_timer.expires;
+
+	if (exp_time > next_timer)
+		mod_timer(&br->gc_timer, round_jiffies_up(next_timer));
+
+	return 0;
+}
+
 void br_fdb_cleanup(unsigned long _data)
 {
 	struct net_bridge *br = (struct net_bridge *)_data;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 3ad1290..b745e06 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -410,6 +410,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
 			      const unsigned char *addr, u16 vid);
 int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
 			      const unsigned char *addr, u16 vid);
+int br_fdb_setageing(struct net_bridge *br, int ageing_interval);
 
 /* br_forward.c */
 void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 5fb4af2..e7c6197 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3277,6 +3277,128 @@ out:
 	return err;
 }
 
+static int configure_ageing_interval(struct net_device  *dev, int interval)
+{
+	int err = -EOPNOTSUPP;
+	int old_ageing_timer = -1;
+	const struct net_device_ops *ops = dev->netdev_ops;
+
+	if (ops->ndo_bridge_getageing)
+		old_ageing_timer = ops->ndo_bridge_getageing(dev);
+
+	if (ops->ndo_bridge_setageing)
+		err = ops->ndo_bridge_setageing(dev, interval);
+
+	if (!err) {
+		err = netdev_switch_ageing_set(dev, interval);
+
+		if (err == -EOPNOTSUPP)
+			return 0;
+
+		if (err && (old_ageing_timer >= MIN_AGEING_INTERVAL_SECS))
+			ops->ndo_bridge_setageing(dev, old_ageing_timer);
+	}
+
+	return err;
+}
+
+static int rtnl_bridge_setageing(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+	struct net *net = sock_net(skb->sk);
+	struct admsg *adm;
+	struct net_device *dev;
+	int err;
+	struct nlattr *tb[NDA_MAX + 1];
+
+	err = nlmsg_parse(nlh, sizeof(*adm), tb, NDA_MAX, NULL);
+	if (err < 0)
+		return err;
+
+	adm = nlmsg_data(nlh);
+	if (adm->adm_ifindex == 0)
+		return -EINVAL;
+
+	dev = __dev_get_by_index(net, adm->adm_ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	err = -EOPNOTSUPP;
+
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return err;
+
+	err = configure_ageing_interval(dev, adm->adm_ageing_interval);
+
+	return err;
+}
+
+static int rtnl_bridge_setdefaultageing(struct sk_buff *skb,
+					struct nlmsghdr *nlh)
+{
+	struct net *net = sock_net(skb->sk);
+	struct admsg *adm;
+	struct net_device *dev;
+	int err;
+	struct nlattr *tb[NDA_MAX + 1];
+
+	err = nlmsg_parse(nlh, sizeof(*adm), tb, NDA_MAX, NULL);
+	if (err < 0)
+		return err;
+
+	adm = nlmsg_data(nlh);
+	if (adm->adm_ifindex == 0)
+		return -EINVAL;
+
+	dev = __dev_get_by_index(net, adm->adm_ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	err = -EOPNOTSUPP;
+
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return err;
+
+	err = configure_ageing_interval(dev, DEFAULT_AGEING_INTERVAL_SECS);
+
+	return err;
+}
+
+static int rtnl_bridge_getageing(struct sk_buff *skb,
+				 struct netlink_callback *cb)
+{
+	struct net *net = sock_net(skb->sk);
+	struct admsg *adm;
+	struct net_device *dev;
+	int err;
+	struct nlattr *tb[NDA_MAX + 1];
+
+	err = nlmsg_parse(cb->nlh, sizeof(*adm), tb, NDA_MAX, NULL);
+	if (err < 0)
+		return err;
+
+	adm = nlmsg_data(cb->nlh);
+	if (adm->adm_ifindex == 0)
+		return -EINVAL;
+
+	dev = __dev_get_by_index(net, adm->adm_ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	err = -EOPNOTSUPP;
+
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return err;
+
+	if (dev->netdev_ops->ndo_bridge_getageing_nl)
+		err = dev->netdev_ops->ndo_bridge_getageing_nl(skb, dev, cb);
+
+	if (err)
+		return err;
+
+	cb->args[0] = 1;
+	return 0;
+}
+
 /* Process one rtnetlink message. */
 
 static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -3427,5 +3549,11 @@ void __init rtnetlink_init(void)
 	rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
 	rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL);
 	rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
+	rtnl_register(PF_BRIDGE, RTM_GETAGEING, NULL,
+		      rtnl_bridge_getageing, NULL);
+	rtnl_register(PF_BRIDGE, RTM_SETAGEING,
+		      rtnl_bridge_setageing, NULL, NULL);
+	rtnl_register(PF_BRIDGE, RTM_SETDEFAULTAGEING,
+		      rtnl_bridge_setdefaultageing, NULL, NULL);
 }
 
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 33bafa2..501e892 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -1142,3 +1142,34 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
 	dev->offload_fwd_mark = mark;
 }
 EXPORT_SYMBOL_GPL(switchdev_port_fwd_mark_set);
+
+int netdev_switch_ageing_set(struct net_device *dev,
+			     int ageing_interval)
+{
+	int err = 0;
+
+	struct net_device *pd;
+	struct list_head *iter;
+
+	netdev_for_each_lower_dev(dev, pd, iter) {
+		if (!pd || !pd->netdev_ops ||
+		    !pd->netdev_ops->ndo_bridge_setageing)
+			continue;
+
+		err = pd->netdev_ops->ndo_bridge_setageing(pd,
+							ageing_interval);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(netdev_switch_ageing_set);
+
+int netdev_switch_ageing_get(struct sk_buff *skb,
+			     const struct net_device *dev,
+			     struct netlink_callback *cb)
+{
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(netdev_switch_ageing_get);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2bbb418..e29836e 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -154,7 +154,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 	switch (sclass) {
 	case SECCLASS_NETLINK_ROUTE_SOCKET:
 		/* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
-		BUILD_BUG_ON(RTM_MAX != (RTM_NEWNSID + 3));
+		BUILD_BUG_ON(RTM_MAX != (RTM_SETAGEING + 3));
 		err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
 				 sizeof(nlmsg_route_perms));
 		break;
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ