Netdev List
 help / color / mirror / Atom feed
* [RFC PATCH net-next 4/5] bridge: vlan lwt and dst_metadata netlink support
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

From: Roopa Prabhu <roopa@cumulusnetworks.com>

This patch adds support to attach per vlan tunnel info dst
metadata. This enables bridge driver to map vlan to tunnel_info
at ingress and egress

The initial use case is vlan to vni bridging, but the api is generic
to extend to any tunnel_info in the future:
    - Uapi to configure/unconfigure/dump per vlan tunnel data
    - netlink functions to configure vlan and tunnel_info mapping
    - Introduces bridge port flag BR_LWT_VLAN to enable attach/detach
    dst_metadata to bridged packets on ports.

Use case:
example use for this is a vxlan bridging gateway or vtep
which maps vlans to vn-segments (or vnis). User can configure
per-vlan tunnel information which the bridge driver can use
to bridge vlan into the corresponding tunnel.

CC: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
CC'ing Nikolay for some more eyes as he has been trying to keep the
bridge driver fast path lite.

 include/linux/if_bridge.h |    1 +
 net/bridge/br_input.c     |    1 +
 net/bridge/br_netlink.c   |  410 ++++++++++++++++++++++++++++++++++++++-------
 net/bridge/br_private.h   |   18 ++
 net/bridge/br_vlan.c      |  138 ++++++++++++++-
 5 files changed, 507 insertions(+), 61 deletions(-)

diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index c6587c0..36ff611 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -46,6 +46,7 @@ struct br_ip_list {
 #define BR_LEARNING_SYNC	BIT(9)
 #define BR_PROXYARP_WIFI	BIT(10)
 #define BR_MCAST_FLOOD		BIT(11)
+#define BR_LWT_VLAN		BIT(12)
 
 #define BR_DEFAULT_AGEING_TIME	(300 * HZ)
 
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 855b72f..83f356f 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -20,6 +20,7 @@
 #include <net/arp.h>
 #include <linux/export.h>
 #include <linux/rculist.h>
+#include <net/dst_metadata.h>
 #include "br_private.h"
 
 /* Hook for brouter */
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 71c7453..df997ad 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -17,17 +17,30 @@
 #include <net/net_namespace.h>
 #include <net/sock.h>
 #include <uapi/linux/if_bridge.h>
+#include <net/dst_metadata.h>
 
 #include "br_private.h"
 #include "br_private_stp.h"
 
-static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
-				u32 filter_mask)
+static size_t br_get_vlan_tinfo_size(void)
 {
+	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
+		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
+		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
+		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
+}
+
+static int __get_num_vlan_infos(struct net_bridge_port *p,
+				struct net_bridge_vlan_group *vg,
+				u32 filter_mask, int *num_vtinfos)
+{
+	struct net_bridge_vlan *vbegin = NULL, *vend = NULL;
+	struct net_bridge_vlan *vtbegin = NULL, *vtend = NULL;
 	struct net_bridge_vlan *v;
-	u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+	bool get_tinfos = (p && p->flags & BR_LWT_VLAN) ? true: false;
+	bool vcontinue, vtcontinue;
+	int num_vinfos = 0;
 	u16 flags, pvid;
-	int num_vlans = 0;
 
 	if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
 		return 0;
@@ -36,6 +49,8 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
 	/* Count number of vlan infos */
 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
 		flags = 0;
+		vcontinue = false;
+		vtcontinue = false;
 		/* only a context, bridge vlan not activated */
 		if (!br_vlan_should_use(v))
 			continue;
@@ -45,47 +60,79 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
 		if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
 			flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
-		if (vid_range_start == 0) {
-			goto initvars;
-		} else if ((v->vid - vid_range_end) == 1 &&
-			flags == vid_range_flags) {
-			vid_range_end = v->vid;
+		if (!vbegin) {
+			vbegin = v;
+			vend = v;
+			vcontinue = true;
+		} else if ((v->vid - vend->vid) == 1 &&
+			flags == vbegin->flags) {
+			vend = v;
+			vcontinue = true;
+		}
+
+		if (!vcontinue) {
+			if ((vend->vid - vbegin->vid) > 0)
+				num_vinfos += 2;
+			else
+				num_vinfos += 1;
+		}
+
+		if (!get_tinfos && !v->tinfo.tunnel_id)
 			continue;
-		} else {
-			if ((vid_range_end - vid_range_start) > 0)
-				num_vlans += 2;
+
+		if (!vtbegin) {
+			vtbegin = v;
+			vtend = v;
+			vtcontinue = true;
+		} else if ((v->vid - vtend->vid) == 1 &&
+		    vlan_tunnel_id_isrange(vtend, v)) {
+			vtend = v;
+			vtcontinue = true;
+		}
+
+		if (!vtcontinue) {
+			if ((vtend->vid - vtbegin->vid) > 0)
+				num_vtinfos += 2;
 			else
-				num_vlans += 1;
+				num_vtinfos += 1;
+			vbegin = NULL;
+			vend = NULL;
 		}
-initvars:
-		vid_range_start = v->vid;
-		vid_range_end = v->vid;
-		vid_range_flags = flags;
 	}
 
-	if (vid_range_start != 0) {
-		if ((vid_range_end - vid_range_start) > 0)
-			num_vlans += 2;
+	if (vbegin) {
+		if ((vend->vid - vbegin->vid) > 0)
+			num_vinfos += 2;
 		else
-			num_vlans += 1;
+			num_vinfos += 1;
 	}
 
-	return num_vlans;
+	if (get_tinfos && vtbegin && vtbegin->tinfo.tunnel_id) {
+		if ((vtend->vid - vtbegin->vid) > 0)
+			*num_vtinfos += 2;
+		else
+			*num_vtinfos += 1;
+	}
+
+	return num_vinfos;
 }
 
-static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
-				 u32 filter_mask)
+static int br_get_num_vlan_infos(struct net_bridge_port *p,
+				 struct net_bridge_vlan_group *vg,
+				 int *num_tinfos, u32 filter_mask)
 {
 	int num_vlans;
 
 	if (!vg)
 		return 0;
 
-	if (filter_mask & RTEXT_FILTER_BRVLAN)
+	if (filter_mask & RTEXT_FILTER_BRVLAN) {
+		*num_tinfos = vg->num_vlans;
 		return vg->num_vlans;
+	}
 
 	rcu_read_lock();
-	num_vlans = __get_num_vlan_infos(vg, filter_mask);
+	num_vlans = __get_num_vlan_infos(p, vg, filter_mask, num_tinfos);
 	rcu_read_unlock();
 
 	return num_vlans;
@@ -95,9 +142,10 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
 					   u32 filter_mask)
 {
 	struct net_bridge_vlan_group *vg = NULL;
-	struct net_bridge_port *p;
+	struct net_bridge_port *p = NULL;
 	struct net_bridge *br;
-	int num_vlan_infos;
+	int num_vlan_infos, num_vlan_tinfos = 0;
+	size_t retsize = 0;
 
 	rcu_read_lock();
 	if (br_port_exists(dev)) {
@@ -107,11 +155,15 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
 		br = netdev_priv(dev);
 		vg = br_vlan_group_rcu(br);
 	}
-	num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
+	num_vlan_infos = br_get_num_vlan_infos(p, vg, &num_vlan_tinfos,
+					       filter_mask);
 	rcu_read_unlock();
 
 	/* Each VLAN is returned in bridge_vlan_info along with flags */
-	return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
+	retsize =  num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)) +
+			num_vlan_tinfos * br_get_vlan_tinfo_size();
+
+	return retsize;
 }
 
 static inline size_t br_port_info_size(void)
@@ -191,7 +243,8 @@ static int br_port_fill_attrs(struct sk_buff *skb,
 	    nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
 	    nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
 		       p->topology_change_ack) ||
-	    nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending))
+	    nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
+	    nla_put_u8(skb, IFLA_BRPORT_LWT_VLAN, !!(p->flags & BR_LWT_VLAN)))
 		return -EMSGSIZE;
 
 	timerval = br_timer_value(&p->message_age_timer);
@@ -216,6 +269,34 @@ static int br_port_fill_attrs(struct sk_buff *skb,
 	return 0;
 }
 
+static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
+                              __be64 tunnel_id, u16 flags)
+{
+    __be32 tid = tunnel_id_to_key32(tunnel_id);
+    struct nlattr *tmap;
+
+	tmap = nla_nest_start(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
+	if (!tmap)
+		return -EMSGSIZE;
+	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
+            be32_to_cpu(tid)))
+		goto nla_put_failure;
+	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
+			vid))
+		goto nla_put_failure;
+	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
+			flags))
+		goto nla_put_failure;
+	nla_nest_end(skb, tmap);
+
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, tmap);
+
+	return -EMSGSIZE;
+}
+
 static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
 				    u16 vid_end, u16 flags)
 {
@@ -249,20 +330,24 @@ static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
 }
 
 static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
+					 struct net_bridge_port *p,
 					 struct net_bridge_vlan_group *vg)
 {
+	struct net_bridge_vlan *vbegin = NULL, *vend = NULL;
+	struct net_bridge_vlan *vtbegin = NULL, *vtend = NULL;
+	bool fill_tinfos = (p && p->flags & BR_LWT_VLAN) ? true: false;
 	struct net_bridge_vlan *v;
-	u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
+	bool vcontinue, vtcontinue;
 	u16 flags, pvid;
-	int err = 0;
+	int err;
 
-	/* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
-	 * and mark vlan info with begin and end flags
-	 * if vlaninfo represents a range
-	 */
 	pvid = br_get_pvid(vg);
+	/* Count number of vlan infos */
 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
 		flags = 0;
+		vcontinue = false;
+		vtcontinue = false;
+		/* only a context, bridge vlan not activated */
 		if (!br_vlan_should_use(v))
 			continue;
 		if (v->vid == pvid)
@@ -271,44 +356,103 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
 		if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
 			flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 
-		if (vid_range_start == 0) {
-			goto initvars;
-		} else if ((v->vid - vid_range_end) == 1 &&
-			flags == vid_range_flags) {
-			vid_range_end = v->vid;
-			continue;
-		} else {
-			err = br_fill_ifvlaninfo_range(skb, vid_range_start,
-						       vid_range_end,
-						       vid_range_flags);
+		if (!vbegin) {
+			vbegin = v;
+			vend = v;
+			vcontinue = true;
+		} else if ((v->vid - vend->vid) == 1 &&
+			flags == vbegin->flags) {
+			vend = v;
+			vcontinue = true;
+		}
+
+		if (!vcontinue) {
+			err = br_fill_ifvlaninfo_range(skb,
+						       vbegin->vid,
+						       vend->vid,
+						       vbegin->flags);
 			if (err)
 				return err;
+			vbegin = vend = NULL;
+		}
+
+		if (!fill_tinfos || !v->tinfo.tunnel_id)
+			continue;
+
+		if (!vtbegin) {
+			vtbegin = v;
+			vtend = v;
+			vtcontinue = true;
+		} else if ((v->vid - vtend->vid) == 1 &&
+		    vlan_tunnel_id_isrange(vtend, v)) {
+			vtend = v;
+			vtcontinue = true;
 		}
 
-initvars:
-		vid_range_start = v->vid;
-		vid_range_end = v->vid;
-		vid_range_flags = flags;
+		if (!vtcontinue && vtbegin->tinfo.tunnel_id) {
+			if ((vtend->vid - vtbegin->vid) > 0) {
+				err = br_fill_vlan_tinfo(skb, vbegin->vid,
+							 vbegin->tinfo.tunnel_id,
+						BRIDGE_VLAN_INFO_RANGE_BEGIN);
+				if (err)
+					return err;
+				err = br_fill_vlan_tinfo(skb, vend->vid,
+							 vend->tinfo.tunnel_id,
+						BRIDGE_VLAN_INFO_RANGE_END);
+				if (err)
+					return err;
+			} else {
+				err = br_fill_vlan_tinfo(skb, vbegin->vid,
+							 vbegin->tinfo.tunnel_id,
+							 0);
+				if (err)
+					return err;
+			}
+			vbegin = NULL;
+			vend = NULL;
+		}
 	}
 
-	if (vid_range_start != 0) {
-		/* Call it once more to send any left over vlans */
-		err = br_fill_ifvlaninfo_range(skb, vid_range_start,
-					       vid_range_end,
-					       vid_range_flags);
+	if (vbegin) {
+		err = br_fill_ifvlaninfo_range(skb, vbegin->vid,
+					       vend->vid,
+					       vbegin->flags);
 		if (err)
 			return err;
 	}
 
+	if (fill_tinfos && vtbegin && vtbegin->tinfo.tunnel_id) {
+		if ((vtend->vid - vtbegin->vid) > 0) {
+			err = br_fill_vlan_tinfo(skb, vbegin->vid,
+						 vbegin->tinfo.tunnel_id,
+						 BRIDGE_VLAN_INFO_RANGE_BEGIN);
+			if (err)
+				return err;
+			err = br_fill_vlan_tinfo(skb, vend->vid,
+						 vend->tinfo.tunnel_id,
+						 BRIDGE_VLAN_INFO_RANGE_END);
+			if (err)
+				return err;
+		} else {
+			err = br_fill_vlan_tinfo(skb, vbegin->vid,
+						 vbegin->tinfo.tunnel_id, 0);
+			if (err)
+				return err;
+		}
+	}
+
 	return 0;
 }
 
 static int br_fill_ifvlaninfo(struct sk_buff *skb,
+			      struct net_bridge_port *p,
 			      struct net_bridge_vlan_group *vg)
 {
 	struct bridge_vlan_info vinfo;
 	struct net_bridge_vlan *v;
+	bool fill_tinfos = (p && p->flags & BR_LWT_VLAN) ? true : false;
 	u16 pvid;
+	int err;
 
 	pvid = br_get_pvid(vg);
 	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
@@ -326,6 +470,14 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
 		if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
 			    sizeof(vinfo), &vinfo))
 			goto nla_put_failure;
+
+		if (!fill_tinfos || !v->tinfo.tunnel_id)
+			continue;
+
+		err = br_fill_vlan_tinfo(skb, v->vid,
+					 v->tinfo.tunnel_id, 0);
+		if (err)
+			return err;
 	}
 
 	return 0;
@@ -411,9 +563,9 @@ static int br_fill_ifinfo(struct sk_buff *skb,
 			goto nla_put_failure;
 		}
 		if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
-			err = br_fill_ifvlaninfo_compressed(skb, vg);
+			err = br_fill_ifvlaninfo_compressed(skb, port, vg);
 		else
-			err = br_fill_ifvlaninfo(skb, vg);
+			err = br_fill_ifvlaninfo(skb, port, vg);
 		rcu_read_unlock();
 		if (err)
 			goto nla_put_failure;
@@ -514,6 +666,127 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
 	return err;
 }
 
+static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
+	[IFLA_BRIDGE_VLAN_TUNNEL_ID]= { .type = NLA_U32 },
+	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
+	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
+};
+
+static int br_add_vlan_tunnel_info(struct net_bridge *br,
+				   struct net_bridge_port *p, int cmd,
+				   u16 vid, u32 tun_id)
+{
+	int err;
+
+	switch (cmd) {
+	case RTM_SETLINK:
+		if (p) {
+			/* if the MASTER flag is set this will act on the global
+			 * per-VLAN entry as well
+			 */
+			err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
+			if (err)
+				break;
+		} else {
+			return -EINVAL;
+		}
+
+		break;
+
+	case RTM_DELLINK:
+		if (p)
+			nbp_vlan_tunnel_info_delete(p, vid);
+		else
+			return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+struct vtunnel_info {
+	u32	tunid;
+	u16	vid;
+	u16	flags;
+};
+
+static int br_parse_vlan_tunnel_info(struct nlattr *attr,
+				     struct vtunnel_info *tinfo)
+{
+	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
+	u32 tun_id;
+	u16 vid, flags;
+	int err;
+
+	err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
+			       attr, vlan_tunnel_policy);
+	if (err < 0)
+		return err;
+
+	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
+		tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
+	else
+		return -EINVAL;
+
+	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]) {
+		vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
+		if (vid >= VLAN_VID_MASK)
+			return -ERANGE;
+	} else {
+		return -EINVAL;
+	}
+
+	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
+		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
+
+	tinfo->tunid = tun_id;
+	tinfo->vid = vid;
+	tinfo->flags = flags;
+
+	return 0;
+}
+
+static int br_process_vlan_tunnel_info(struct net_bridge *br,
+				       struct net_bridge_port *p, int cmd,
+				       struct vtunnel_info *tinfo_curr,
+				       struct vtunnel_info *tinfo_last)
+{
+	int t, v;
+	int err;
+
+	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
+			return -EINVAL;
+		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
+	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
+		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
+			return -EINVAL;
+		if ((tinfo_curr->vid - tinfo_last->vid) !=
+		    (tinfo_curr->tunid - tinfo_last->tunid))
+			return -EINVAL;
+		/* XXX: tun id and vlan id attrs must be same
+		 */
+		t = tinfo_last->tunid;
+		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
+			err = br_add_vlan_tunnel_info(br, p, cmd,
+							  v, t);
+			if (err)
+				return err;
+			t++;
+		}
+		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
+		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
+	} else {
+		err = br_add_vlan_tunnel_info(br, p, cmd,
+					      tinfo_curr->vid,
+					      tinfo_curr->tunid);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
 static int br_afspec(struct net_bridge *br,
 		     struct net_bridge_port *p,
 		     struct nlattr *af_spec,
@@ -522,10 +795,30 @@ static int br_afspec(struct net_bridge *br,
 	struct bridge_vlan_info *vinfo_start = NULL;
 	struct bridge_vlan_info *vinfo = NULL;
 	struct nlattr *attr;
+	struct vtunnel_info tinfo_last = {
+		.tunid = 0,
+		.vid = 0,
+		.flags = 0};
+	struct vtunnel_info tinfo_curr = {
+		.tunid = 0,
+		.vid = 0,
+		.flags = 0};
 	int err = 0;
 	int rem;
 
 	nla_for_each_nested(attr, af_spec, rem) {
+		if (nla_type(attr) == IFLA_BRIDGE_VLAN_TUNNEL_INFO) {
+			err = br_parse_vlan_tunnel_info(attr, &tinfo_curr);
+			if (err)
+				return err;
+			err = br_process_vlan_tunnel_info(br, p, cmd,
+							  &tinfo_curr,
+							  &tinfo_last);
+			if (err)
+				return err;
+			continue;
+		}
+
 		if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
 			continue;
 		if (nla_len(attr) != sizeof(struct bridge_vlan_info))
@@ -638,6 +931,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
 	br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
 	br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
 	br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
+	br_set_port_flag(p, tb, IFLA_BRPORT_LWT_VLAN, BR_LWT_VLAN);
 
 	if (tb[IFLA_BRPORT_COST]) {
 		err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 8ce621e..f68e360 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -91,6 +91,11 @@ struct br_vlan_stats {
 	struct u64_stats_sync syncp;
 };
 
+struct br_tunnel_info {
+	__be64			tunnel_id;
+	struct metadata_dst	*tunnel_dst;
+};
+
 /**
  * struct net_bridge_vlan - per-vlan entry
  *
@@ -113,6 +118,7 @@ struct br_vlan_stats {
  */
 struct net_bridge_vlan {
 	struct rhash_head		vnode;
+	struct rhash_head		tnode;
 	u16				vid;
 	u16				flags;
 	struct br_vlan_stats __percpu	*stats;
@@ -124,6 +130,9 @@ struct net_bridge_vlan {
 		atomic_t		refcnt;
 		struct net_bridge_vlan	*brvlan;
 	};
+
+	struct br_tunnel_info		tinfo;
+
 	struct list_head		vlist;
 
 	struct rcu_head			rcu;
@@ -145,6 +154,7 @@ struct net_bridge_vlan {
  */
 struct net_bridge_vlan_group {
 	struct rhashtable		vlan_hash;
+	struct rhashtable		tunnel_hash;
 	struct list_head		vlan_list;
 	u16				num_vlans;
 	u16				pvid;
@@ -786,6 +796,14 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
 void br_vlan_get_stats(const struct net_bridge_vlan *v,
 		       struct br_vlan_stats *stats);
+int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
+			   struct net_bridge_vlan *vlan, u32 tun_id);
+int __vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
+                           struct net_bridge_vlan *vlan);
+int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid);
+int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id);
+bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v_end,
+			    struct net_bridge_vlan *v);
 
 static inline struct net_bridge_vlan_group *br_vlan_group(
 					const struct net_bridge *br)
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index b6de4f4..2040f08 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -3,6 +3,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/slab.h>
 #include <net/switchdev.h>
+#include <net/dst_metadata.h>
 
 #include "br_private.h"
 
@@ -31,6 +32,31 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
 	return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
 }
 
+static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
+				    const void *ptr)
+{
+	const struct net_bridge_vlan *vle = ptr;
+	__be64 tunid = *(__be64 *)arg->key;
+
+	return vle->tinfo.tunnel_id != tunid;
+}
+
+static const struct rhashtable_params br_vlan_tunnel_rht_params = {
+	.head_offset = offsetof(struct net_bridge_vlan, tnode),
+	.key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id),
+	.key_len = sizeof(__be64),
+	.nelem_hint = 3,
+	.locks_mul = 1,
+	.obj_cmpfn = br_vlan_tunid_cmp,
+	.automatic_shrinking = true,
+};
+
+static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
+						     u64 tunnel_id)
+{
+	return rhashtable_lookup_fast(tbl, &tunnel_id, br_vlan_tunnel_rht_params);
+}
+
 static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid)
 {
 	if (vg->pvid == vid)
@@ -325,6 +351,7 @@ static void __vlan_group_free(struct net_bridge_vlan_group *vg)
 {
 	WARN_ON(!list_empty(&vg->vlan_list));
 	rhashtable_destroy(&vg->vlan_hash);
+	rhashtable_destroy(&vg->tunnel_hash);
 	kfree(vg);
 }
 
@@ -613,6 +640,8 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
 	br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
 	br_fdb_delete_by_port(br, NULL, vid, 0);
 
+	__vlan_tunnel_info_del(vg, v);
+
 	return __vlan_del(v);
 }
 
@@ -918,6 +947,9 @@ int br_vlan_init(struct net_bridge *br)
 	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
 	if (ret)
 		goto err_rhtbl;
+	ret = rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
+	if (ret)
+		goto err_rhtbl2;
 	INIT_LIST_HEAD(&vg->vlan_list);
 	br->vlan_proto = htons(ETH_P_8021Q);
 	br->default_pvid = 1;
@@ -932,6 +964,8 @@ int br_vlan_init(struct net_bridge *br)
 	return ret;
 
 err_vlan_add:
+	rhashtable_destroy(&vg->tunnel_hash);
+err_rhtbl2:
 	rhashtable_destroy(&vg->vlan_hash);
 err_rhtbl:
 	kfree(vg);
@@ -960,7 +994,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
 
 	ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
 	if (ret)
-		goto err_rhtbl;
+		goto err_rhtbl1;
+	ret = rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
+	if (ret)
+		goto err_rhtbl2;
 	INIT_LIST_HEAD(&vg->vlan_list);
 	rcu_assign_pointer(p->vlgrp, vg);
 	if (p->br->default_pvid) {
@@ -976,9 +1013,11 @@ int nbp_vlan_init(struct net_bridge_port *p)
 err_vlan_add:
 	RCU_INIT_POINTER(p->vlgrp, NULL);
 	synchronize_rcu();
-	rhashtable_destroy(&vg->vlan_hash);
+	rhashtable_destroy(&vg->tunnel_hash);
 err_vlan_enabled:
-err_rhtbl:
+err_rhtbl2:
+	rhashtable_destroy(&vg->vlan_hash);
+err_rhtbl1:
 	kfree(vg);
 
 	goto out;
@@ -1081,3 +1120,96 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
 		stats->tx_packets += txpackets;
 	}
 }
+
+bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v_end,
+			    struct net_bridge_vlan *v)
+{
+	/* XXX: check other tunnel attributes */
+	return (be32_to_cpu(tunnel_id_to_key32(v_end->tinfo.tunnel_id)) -
+		be32_to_cpu(tunnel_id_to_key32(v->tinfo.tunnel_id)) == 1);
+}
+
+int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
+			   struct net_bridge_vlan *vlan, u32 tun_id)
+{
+	struct metadata_dst *metadata = NULL;
+	__be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
+	int err;
+
+	if (vlan->tinfo.tunnel_dst)
+		return -EEXIST;
+
+	metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
+				    key, 0);
+	if (!metadata)
+		return -EINVAL;
+
+	metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
+	vlan->tinfo.tunnel_dst = metadata;
+	vlan->tinfo.tunnel_id = key;
+
+	err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
+					    br_vlan_tunnel_rht_params);
+	if (err)
+		goto out;
+
+	return 0;
+out:
+	dst_release(&vlan->tinfo.tunnel_dst->dst);
+
+	return err;
+}
+
+int __vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
+			   struct net_bridge_vlan *vlan)
+{
+	if (vlan->tinfo.tunnel_dst) {
+		vlan->tinfo.tunnel_id = 0;
+		dst_release(&vlan->tinfo.tunnel_dst->dst);
+
+		rhashtable_remove_fast(&vg->tunnel_hash, &vlan->vnode,
+				       br_vlan_tunnel_rht_params);
+	}
+
+	return 0;
+}
+
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
+int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
+{
+	struct net_bridge_vlan_group *vg;
+	struct net_bridge_vlan *vlan;
+
+	ASSERT_RTNL();
+
+	vg = nbp_vlan_group(port);
+	vlan = br_vlan_find(vg, vid);
+	if (!vlan)
+		return -EINVAL;
+
+	__vlan_tunnel_info_add(vg, vlan, tun_id);
+
+	return 0;
+}
+
+/* Must be protected by RTNL.
+ * Must be called with vid in range from 1 to 4094 inclusive.
+ */
+int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
+{
+	struct net_bridge_vlan_group *vg;
+	struct net_bridge_vlan *v;
+
+	ASSERT_RTNL();
+
+	vg = nbp_vlan_group(port);
+	v = br_vlan_find(vg, vid);
+	if (!v)
+		return -ENOENT;
+
+	__vlan_tunnel_info_del(vg, v);
+
+	return 0;
+}
-- 
1.7.10.4

^ permalink raw reply related

* [RFC PATCH net-next 5/5] bridge: vlan lwt dst_metadata hooks in ingress and egress paths
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

From: Roopa Prabhu <roopa@cumulusnetworks.com>

- ingress hook:
    - if port is a lwt tunnel port, use tunnel info in
      attached dst_metadata to map it to a local vlan
- egress hook:
    - if port is a lwt tunnel port, use tunnel info attached to
      vlan to set dst_metadata on the skb

CC: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
CC'ing Nikolay for some more eyes as he has been trying to keep the
bridge driver fast path lite.

 net/bridge/br_input.c   |    4 ++++
 net/bridge/br_private.h |    4 ++++
 net/bridge/br_vlan.c    |   55 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+)

diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 83f356f..96602a1 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -262,6 +262,10 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
 		return RX_HANDLER_CONSUMED;
 
 	p = br_port_get_rcu(skb->dev);
+	if (p->flags & BR_LWT_VLAN) {
+		if (br_handle_ingress_vlan_tunnel(skb, p, nbp_vlan_group_rcu(p)))
+			goto drop;
+	}
 
 	if (unlikely(is_link_local_ether_addr(dest))) {
 		u16 fwd_mask = p->br->group_fwd_mask_required;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index f68e360..68a23c5 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -804,6 +804,10 @@ int __vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
 int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id);
 bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v_end,
 			    struct net_bridge_vlan *v);
+int br_handle_ingress_vlan_tunnel(struct sk_buff *skb, struct net_bridge_port *p,
+				  struct net_bridge_vlan_group *vg);
+int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
+				 struct net_bridge_vlan *vlan);
 
 static inline struct net_bridge_vlan_group *br_vlan_group(
 					const struct net_bridge *br)
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 2040f08..6cf2344 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -405,6 +405,11 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
 
 	if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
 		skb->vlan_tci = 0;
+
+	if (br_handle_egress_vlan_tunnel(skb, v)) {
+		kfree_skb(skb);
+		return NULL;
+	}
 out:
 	return skb;
 }
@@ -1213,3 +1218,53 @@ int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
 
 	return 0;
 }
+
+int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
+				  struct net_bridge_port *p,
+				  struct net_bridge_vlan_group *vg)
+{
+	struct ip_tunnel_info *tinfo = skb_tunnel_info(skb);
+	struct net_bridge_vlan *vlan;
+
+	if (!vg || !tinfo)
+		return 0;
+
+	/* if already tagged, ignore */
+	if (skb_vlan_tagged(skb))
+		return 0;
+
+	/* lookup vid, given tunnel id */
+	vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id);
+	if (!vlan)
+		return 0;
+
+	skb_dst_drop(skb);
+
+	__vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
+
+	return 0;
+}
+
+int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
+				 struct net_bridge_vlan *vlan)
+{
+	__be32 tun_id;
+	int err;
+
+	if (!vlan || !vlan->tinfo.tunnel_id)
+		return 0;
+
+	if (unlikely(!skb_vlan_tag_present(skb)))
+		return 0;
+
+	skb_dst_drop(skb);
+	tun_id = tunnel_id_to_key32(vlan->tinfo.tunnel_id);
+
+	err = skb_vlan_pop(skb);
+	if (err)
+		return err;
+
+	skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
+
+	return 0;
+}
-- 
1.7.10.4

^ permalink raw reply related

* [RFC PATCH net-next 3/5] bridge: uapi: add per vlan tunnel info
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

From: Roopa Prabhu <roopa@cumulusnetworks.com>

New netlink api to associate tunnel info per vlan.
This is used by bridge driver to send tunnel metadata to
bridge ports in LWT tunnel dst metadata mode.

One example use for this is a vxlan bridging gateway or vtep
which maps vlans to vn-segments (or vnis). User can configure
per-vlan tunnel information which the bridge driver can use
to bridge vlan into the corresponding vn-segment.

This patch also introduces a bridge port flag IFLA_BRPORT_LWT_VLAN
to enable this feature on a tunnel bridge port. It is off by default.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
 include/uapi/linux/if_bridge.h |   11 +++++++++++
 include/uapi/linux/if_link.h   |    1 +
 2 files changed, 12 insertions(+)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index ab92bca..a9e6244 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -118,6 +118,7 @@ enum {
 	IFLA_BRIDGE_FLAGS,
 	IFLA_BRIDGE_MODE,
 	IFLA_BRIDGE_VLAN_INFO,
+	IFLA_BRIDGE_VLAN_TUNNEL_INFO,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -134,6 +135,16 @@ struct bridge_vlan_info {
 	__u16 vid;
 };
 
+enum {
+	IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC,
+	IFLA_BRIDGE_VLAN_TUNNEL_ID,
+	IFLA_BRIDGE_VLAN_TUNNEL_VID,
+	IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
+	__IFLA_BRIDGE_VLAN_TUNNEL_MAX,
+};
+
+#define IFLA_BRIDGE_VLAN_TUNNEL_MAX (__IFLA_BRIDGE_VLAN_TUNNEL_MAX - 1)
+
 struct bridge_vlan_xstats {
 	__u64 rx_bytes;
 	__u64 rx_packets;
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 184b16e..f0356b9 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -321,6 +321,7 @@ enum {
 	IFLA_BRPORT_MULTICAST_ROUTER,
 	IFLA_BRPORT_PAD,
 	IFLA_BRPORT_MCAST_FLOOD,
+	IFLA_BRPORT_LWT_VLAN,
 	__IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-- 
1.7.10.4

^ permalink raw reply related

* [RFC PATCH net-next 2/5] vxlan: make COLLECT_METADATA mode bridge friendly
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

From: Roopa Prabhu <roopa@cumulusnetworks.com>

This patch series makes vxlan COLLECT_METADATA mode bridge
and layer2 network friendly. Vxlan COLLECT_METADATA mode today
solves the per-vni netdev scalability problem in l3 networks.
When vxlan collect metadata device participates in bridging
vlan to vn-segments, It can only get the vlan mapped vni in
the xmit tunnel dst metadata. It will need the vxlan driver to
continue learn, hold forwarding state and remote destination
information similar to how it already does for non COLLECT_METADATA
vxlan netdevices today.

Changes introduced by this patch:
    - allow learning and forwarding database state to vxlan netdev in
      COLLECT_METADATA mode. Current behaviour is not changed
      by default. tunnel info flag IP_TUNNEL_INFO_BRIDGE is used
      to support the new bridge friendly mode.
    - A single fdb table hashed by (mac, vni) to allow fdb entries with
      multiple vnis in the same fdb table
    - rx path already has the vni
    - tx path expects a vni in the packet with dst_metadata
    - prior to this series, fdb remote_dsts carried remote vni and
      the vxlan device carrying the fdb table represented the
      source vni. With the vxlan device now representing multiple vnis,
      this patch adds a src vni attribute to the fdb entry. The remote
      vni already uses NDA_VNI attribute. This patch introduces
      NDA_SRC_VNI netlink attribute to represent the src vni in a multi
      vni fdb table.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
 drivers/net/vxlan.c            |  209 +++++++++++++++++++++++++---------------
 include/uapi/linux/neighbour.h |    1 +
 2 files changed, 134 insertions(+), 76 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index ca7196c..fb114b3 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -57,6 +57,8 @@
 
 static const u8 all_zeros_mac[ETH_ALEN + 2];
 
+static u32 fdb_salt __read_mostly;
+
 static int vxlan_sock_add(struct vxlan_dev *vxlan);
 
 /* per-network namespace private data for this module */
@@ -75,6 +77,7 @@ struct vxlan_fdb {
 	struct list_head  remotes;
 	u8		  eth_addr[ETH_ALEN];
 	u16		  state;	/* see ndm_state */
+	__be32		  vni;
 	u8		  flags;	/* see ndm_flags */
 };
 
@@ -302,6 +305,10 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,
 	if (rdst->remote_vni != vxlan->default_dst.remote_vni &&
 	    nla_put_u32(skb, NDA_VNI, be32_to_cpu(rdst->remote_vni)))
 		goto nla_put_failure;
+	if ((vxlan->flags & VXLAN_F_COLLECT_METADATA) && fdb->vni &&
+	    nla_put_u32(skb, NDA_SRC_VNI,
+			be32_to_cpu(fdb->vni)))
+		goto nla_put_failure;
 	if (rdst->remote_ifindex &&
 	    nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))
 		goto nla_put_failure;
@@ -400,34 +407,50 @@ static u32 eth_hash(const unsigned char *addr)
 	return hash_64(value, FDB_HASH_BITS);
 }
 
+static u32 eth_vni_hash(const unsigned char *addr, __be32 vni)
+{
+	/* use 1 byte of OUI and 3 bytes of NIC */
+	u32 key = get_unaligned((u32 *)(addr + 2));
+	return jhash_2words(key, vni, fdb_salt) & (FDB_HASH_SIZE - 1);
+}
+
 /* Hash chain to use given mac address */
 static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan,
-						const u8 *mac)
+						const u8 *mac, __be32 vni)
 {
-	return &vxlan->fdb_head[eth_hash(mac)];
+	if (vxlan->flags & VXLAN_F_COLLECT_METADATA)
+		return &vxlan->fdb_head[eth_vni_hash(mac, vni)];
+	else
+		return &vxlan->fdb_head[eth_hash(mac)];
 }
 
 /* Look up Ethernet address in forwarding table */
 static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan,
-					const u8 *mac)
+					  const u8 *mac, __be32 vni)
 {
-	struct hlist_head *head = vxlan_fdb_head(vxlan, mac);
+	struct hlist_head *head = vxlan_fdb_head(vxlan, mac, vni);
 	struct vxlan_fdb *f;
 
 	hlist_for_each_entry_rcu(f, head, hlist) {
-		if (ether_addr_equal(mac, f->eth_addr))
-			return f;
+		if (ether_addr_equal(mac, f->eth_addr)) {
+			if (vxlan->flags & VXLAN_F_COLLECT_METADATA) {
+				if (vni == f->vni)
+					return f;
+			} else {
+				return f;
+			}
+		}
 	}
 
 	return NULL;
 }
 
 static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,
-					const u8 *mac)
+					const u8 *mac, __be32 vni)
 {
 	struct vxlan_fdb *f;
 
-	f = __vxlan_find_mac(vxlan, mac);
+	f = __vxlan_find_mac(vxlan, mac, vni);
 	if (f)
 		f->used = jiffies;
 
@@ -605,15 +628,15 @@ static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
 static int vxlan_fdb_create(struct vxlan_dev *vxlan,
 			    const u8 *mac, union vxlan_addr *ip,
 			    __u16 state, __u16 flags,
-			    __be16 port, __be32 vni, __u32 ifindex,
-			    __u8 ndm_flags)
+			    __be16 port, __be32 src_vni, __be32 vni,
+			    __u32 ifindex, __u8 ndm_flags)
 {
 	struct vxlan_rdst *rd = NULL;
 	struct vxlan_fdb *f;
 	int notify = 0;
 	int rc;
 
-	f = __vxlan_find_mac(vxlan, mac);
+	f = __vxlan_find_mac(vxlan, mac, src_vni);
 	if (f) {
 		if (flags & NLM_F_EXCL) {
 			netdev_dbg(vxlan->dev,
@@ -670,6 +693,7 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
 		f->state = state;
 		f->flags = ndm_flags;
 		f->updated = f->used = jiffies;
+		f->vni = src_vni;
 		INIT_LIST_HEAD(&f->remotes);
 		memcpy(f->eth_addr, mac, ETH_ALEN);
 
@@ -681,7 +705,7 @@ static int vxlan_fdb_create(struct vxlan_dev *vxlan,
 
 		++vxlan->addrcnt;
 		hlist_add_head_rcu(&f->hlist,
-				   vxlan_fdb_head(vxlan, mac));
+				   vxlan_fdb_head(vxlan, mac, src_vni));
 	}
 
 	if (notify) {
@@ -718,8 +742,8 @@ static void vxlan_fdb_destroy(struct vxlan_dev *vxlan, struct vxlan_fdb *f)
 }
 
 static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
-			   union vxlan_addr *ip, __be16 *port, __be32 *vni,
-			   u32 *ifindex)
+			   union vxlan_addr *ip, __be16 *port, __be32 *src_vni,
+			   __be32 *vni, u32 *ifindex)
 {
 	struct net *net = dev_net(vxlan->dev);
 	int err;
@@ -757,6 +781,14 @@ static int vxlan_fdb_parse(struct nlattr *tb[], struct vxlan_dev *vxlan,
 		*vni = vxlan->default_dst.remote_vni;
 	}
 
+	if (tb[NDA_SRC_VNI]) {
+		if (nla_len(tb[NDA_SRC_VNI]) != sizeof(u32))
+			return -EINVAL;
+		*src_vni = cpu_to_be32(nla_get_u32(tb[NDA_SRC_VNI]));
+	} else {
+		*src_vni = vxlan->default_dst.remote_vni;
+	}
+
 	if (tb[NDA_IFINDEX]) {
 		struct net_device *tdev;
 
@@ -782,7 +814,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	/* struct net *net = dev_net(vxlan->dev); */
 	union vxlan_addr ip;
 	__be16 port;
-	__be32 vni;
+	__be32 src_vni, vni;
 	u32 ifindex;
 	int err;
 
@@ -795,7 +827,7 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 	if (tb[NDA_DST] == NULL)
 		return -EINVAL;
 
-	err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex);
+	err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
 	if (err)
 		return err;
 
@@ -804,36 +836,23 @@ static int vxlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
 
 	spin_lock_bh(&vxlan->hash_lock);
 	err = vxlan_fdb_create(vxlan, addr, &ip, ndm->ndm_state, flags,
-			       port, vni, ifindex, ndm->ndm_flags);
+			       port, src_vni, vni, ifindex, ndm->ndm_flags);
 	spin_unlock_bh(&vxlan->hash_lock);
 
 	return err;
 }
 
-/* Delete entry (via netlink) */
-static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
-			    struct net_device *dev,
-			    const unsigned char *addr, u16 vid)
+static int __vxlan_fdb_delete(struct vxlan_dev *vxlan, const unsigned char *addr,
+			      union vxlan_addr ip, __be16 port, __be32 src_vni,
+			      u32 vni, u32 ifindex, u16 vid)
 {
-	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_fdb *f;
 	struct vxlan_rdst *rd = NULL;
-	union vxlan_addr ip;
-	__be16 port;
-	__be32 vni;
-	u32 ifindex;
-	int err;
-
-	err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &vni, &ifindex);
-	if (err)
-		return err;
+	int err = -ENOENT;
 
-	err = -ENOENT;
-
-	spin_lock_bh(&vxlan->hash_lock);
-	f = vxlan_find_mac(vxlan, addr);
+	f = vxlan_find_mac(vxlan, addr, src_vni);
 	if (!f)
-		goto out;
+		return err;
 
 	if (!vxlan_addr_any(&ip)) {
 		rd = vxlan_fdb_find_rdst(f, &ip, port, vni, ifindex);
@@ -841,8 +860,6 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 			goto out;
 	}
 
-	err = 0;
-
 	/* remove a destination if it's not the only one on the list,
 	 * otherwise destroy the fdb entry
 	 */
@@ -856,6 +873,28 @@ static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 	vxlan_fdb_destroy(vxlan, f);
 
 out:
+	return 0;
+}
+
+/* Delete entry (via netlink) */
+static int vxlan_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
+			    struct net_device *dev,
+			    const unsigned char *addr, u16 vid)
+{
+	struct vxlan_dev *vxlan = netdev_priv(dev);
+	union vxlan_addr ip;
+	__be32 src_vni, vni;
+	__be16 port;
+	u32 ifindex;
+	int err;
+
+	err = vxlan_fdb_parse(tb, vxlan, &ip, &port, &src_vni, &vni, &ifindex);
+	if (err)
+		return err;
+
+	spin_lock_bh(&vxlan->hash_lock);
+	err = __vxlan_fdb_delete(vxlan, addr, ip, port, src_vni, vni, ifindex,
+				 vid);
 	spin_unlock_bh(&vxlan->hash_lock);
 
 	return err;
@@ -901,12 +940,13 @@ static int vxlan_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
  * Return true if packet is bogus and should be dropped.
  */
 static bool vxlan_snoop(struct net_device *dev,
-			union vxlan_addr *src_ip, const u8 *src_mac)
+			union vxlan_addr *src_ip, const u8 *src_mac,
+			__be32 vni)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct vxlan_fdb *f;
 
-	f = vxlan_find_mac(vxlan, src_mac);
+	f = vxlan_find_mac(vxlan, src_mac, vni);
 	if (likely(f)) {
 		struct vxlan_rdst *rdst = first_remote_rcu(f);
 
@@ -930,13 +970,15 @@ static bool vxlan_snoop(struct net_device *dev,
 		spin_lock(&vxlan->hash_lock);
 
 		/* close off race between vxlan_flush and incoming packets */
-		if (netif_running(dev))
+		if (netif_running(dev)) {
 			vxlan_fdb_create(vxlan, src_mac, src_ip,
 					 NUD_REACHABLE,
 					 NLM_F_EXCL|NLM_F_CREATE,
 					 vxlan->cfg.dst_port,
+					 vni,
 					 vxlan->default_dst.remote_vni,
 					 0, NTF_SELF);
+		}
 		spin_unlock(&vxlan->hash_lock);
 	}
 
@@ -1202,7 +1244,7 @@ static bool vxlan_parse_gpe_hdr(struct vxlanhdr *unparsed,
 
 static bool vxlan_set_mac(struct vxlan_dev *vxlan,
 			  struct vxlan_sock *vs,
-			  struct sk_buff *skb)
+			  struct sk_buff *skb, __be32 vni)
 {
 	union vxlan_addr saddr;
 
@@ -1226,7 +1268,7 @@ static bool vxlan_set_mac(struct vxlan_dev *vxlan,
 	}
 
 	if ((vxlan->flags & VXLAN_F_LEARN) &&
-	    vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source))
+	    vxlan_snoop(skb->dev, &saddr, eth_hdr(skb)->h_source, vni))
 		return false;
 
 	return true;
@@ -1268,6 +1310,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	__be16 protocol = htons(ETH_P_TEB);
 	bool raw_proto = false;
 	void *oiph;
+	__be32 vni = 0;
 
 	/* Need UDP and VXLAN header to be present */
 	if (!pskb_may_pull(skb, VXLAN_HLEN))
@@ -1289,7 +1332,12 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	if (!vs)
 		goto drop;
 
-	vxlan = vxlan_vs_find_vni(vs, vxlan_vni(vxlan_hdr(skb)->vx_vni));
+	vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
+
+	if ((vs->flags & VXLAN_F_COLLECT_METADATA) && !vni)
+		goto drop;
+
+	vxlan = vxlan_vs_find_vni(vs, vni);
 	if (!vxlan)
 		goto drop;
 
@@ -1307,7 +1355,6 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 			goto drop;
 
 	if (vxlan_collect_metadata(vs)) {
-		__be32 vni = vxlan_vni(vxlan_hdr(skb)->vx_vni);
 		struct metadata_dst *tun_dst;
 
 		tun_dst = udp_tun_rx_dst(skb, vxlan_get_sk_family(vs), TUNNEL_KEY,
@@ -1345,7 +1392,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	}
 
 	if (!raw_proto) {
-		if (!vxlan_set_mac(vxlan, vs, skb))
+		if (!vxlan_set_mac(vxlan, vs, skb, vni))
 			goto drop;
 	} else {
 		skb_reset_mac_header(skb);
@@ -1377,7 +1424,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
 	return 0;
 }
 
-static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
+static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct arphdr *parp;
@@ -1424,7 +1471,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb)
 			goto out;
 		}
 
-		f = vxlan_find_mac(vxlan, n->ha);
+		f = vxlan_find_mac(vxlan, n->ha, vni);
 		if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) {
 			/* bridge-local neighbor */
 			neigh_release(n);
@@ -1548,7 +1595,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request,
 	return reply;
 }
 
-static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
+static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct nd_msg *msg;
@@ -1585,7 +1632,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb)
 			goto out;
 		}
 
-		f = vxlan_find_mac(vxlan, n->ha);
+		f = vxlan_find_mac(vxlan, n->ha, vni);
 		if (f && vxlan_addr_any(&(first_remote_rcu(f)->remote_ip))) {
 			/* bridge-local neighbor */
 			neigh_release(n);
@@ -1906,7 +1953,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
 
 /* Bypass encapsulation if the destination is local */
 static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
-			       struct vxlan_dev *dst_vxlan)
+			       struct vxlan_dev *dst_vxlan, __be32 vni)
 {
 	struct pcpu_sw_netstats *tx_stats, *rx_stats;
 	union vxlan_addr loopback;
@@ -1932,7 +1979,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 	}
 
 	if (dst_vxlan->flags & VXLAN_F_LEARN)
-		vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source);
+		vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source, vni);
 
 	u64_stats_update_begin(&tx_stats->syncp);
 	tx_stats->tx_packets++;
@@ -1976,7 +2023,7 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
 
 			return -ENOENT;
 		}
-		vxlan_encap_bypass(skb, vxlan, dst_vxlan);
+		vxlan_encap_bypass(skb, vxlan, dst_vxlan, vni);
 		return 1;
 	}
 
@@ -1984,7 +2031,8 @@ static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev,
 }
 
 static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
-			   struct vxlan_rdst *rdst, bool did_rsc)
+			   __be32 default_vni, struct vxlan_rdst *rdst,
+			   bool did_rsc)
 {
 	struct dst_cache *dst_cache;
 	struct ip_tunnel_info *info;
@@ -2011,14 +2059,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 		if (vxlan_addr_any(dst)) {
 			if (did_rsc) {
 				/* short-circuited back to local bridge */
-				vxlan_encap_bypass(skb, vxlan, vxlan);
+				vxlan_encap_bypass(skb, vxlan, vxlan, default_vni);
 				return;
 			}
 			goto drop;
 		}
 
 		dst_port = rdst->remote_port ? rdst->remote_port : vxlan->cfg.dst_port;
-		vni = rdst->remote_vni;
+		vni = (rdst->remote_vni) ? : default_vni;
 		src = &vxlan->cfg.saddr;
 		dst_cache = &rdst->dst_cache;
 		md->gbp = skb->mark;
@@ -2173,23 +2221,29 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 	bool did_rsc = false;
 	struct vxlan_rdst *rdst, *fdst = NULL;
 	struct vxlan_fdb *f;
+	__be32 vni = 0;
 
 	info = skb_tunnel_info(skb);
 
 	skb_reset_mac_header(skb);
 
 	if (vxlan->flags & VXLAN_F_COLLECT_METADATA) {
-		if (info && info->mode & IP_TUNNEL_INFO_TX)
-			vxlan_xmit_one(skb, dev, NULL, false);
-		else
-			kfree_skb(skb);
-		return NETDEV_TX_OK;
+		if (info && info->mode & IP_TUNNEL_INFO_BRIDGE &&
+		    info->mode & IP_TUNNEL_INFO_TX) {
+			vni = tunnel_id_to_key32(info->key.tun_id);
+		} else {
+			if (info && info->mode & IP_TUNNEL_INFO_TX)
+				vxlan_xmit_one(skb, dev, vni, NULL, false);
+			else
+				kfree_skb(skb);
+			return NETDEV_TX_OK;
+		}
 	}
 
 	if (vxlan->flags & VXLAN_F_PROXY) {
 		eth = eth_hdr(skb);
 		if (ntohs(eth->h_proto) == ETH_P_ARP)
-			return arp_reduce(dev, skb);
+			return arp_reduce(dev, skb, vni);
 #if IS_ENABLED(CONFIG_IPV6)
 		else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
 			 pskb_may_pull(skb, sizeof(struct ipv6hdr)
@@ -2200,13 +2254,13 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 				msg = (struct nd_msg *)skb_transport_header(skb);
 				if (msg->icmph.icmp6_code == 0 &&
 				    msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
-					return neigh_reduce(dev, skb);
+					return neigh_reduce(dev, skb, vni);
 		}
 #endif
 	}
 
 	eth = eth_hdr(skb);
-	f = vxlan_find_mac(vxlan, eth->h_dest);
+	f = vxlan_find_mac(vxlan, eth->h_dest, vni);
 	did_rsc = false;
 
 	if (f && (f->flags & NTF_ROUTER) && (vxlan->flags & VXLAN_F_RSC) &&
@@ -2214,11 +2268,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 	     ntohs(eth->h_proto) == ETH_P_IPV6)) {
 		did_rsc = route_shortcircuit(dev, skb);
 		if (did_rsc)
-			f = vxlan_find_mac(vxlan, eth->h_dest);
+			f = vxlan_find_mac(vxlan, eth->h_dest, vni);
 	}
 
 	if (f == NULL) {
-		f = vxlan_find_mac(vxlan, all_zeros_mac);
+		f = vxlan_find_mac(vxlan, all_zeros_mac, vni);
 		if (f == NULL) {
 			if ((vxlan->flags & VXLAN_F_L2MISS) &&
 			    !is_multicast_ether_addr(eth->h_dest))
@@ -2239,11 +2293,11 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 		}
 		skb1 = skb_clone(skb, GFP_ATOMIC);
 		if (skb1)
-			vxlan_xmit_one(skb1, dev, rdst, did_rsc);
+			vxlan_xmit_one(skb1, dev, vni, rdst, did_rsc);
 	}
 
 	if (fdst)
-		vxlan_xmit_one(skb, dev, fdst, did_rsc);
+		vxlan_xmit_one(skb, dev, vni, fdst, did_rsc);
 	else
 		kfree_skb(skb);
 	return NETDEV_TX_OK;
@@ -2307,12 +2361,12 @@ static int vxlan_init(struct net_device *dev)
 	return 0;
 }
 
-static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan)
+static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
 {
 	struct vxlan_fdb *f;
 
 	spin_lock_bh(&vxlan->hash_lock);
-	f = __vxlan_find_mac(vxlan, all_zeros_mac);
+	f = __vxlan_find_mac(vxlan, all_zeros_mac, vni);
 	if (f)
 		vxlan_fdb_destroy(vxlan, f);
 	spin_unlock_bh(&vxlan->hash_lock);
@@ -2322,7 +2376,7 @@ static void vxlan_uninit(struct net_device *dev)
 {
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 
-	vxlan_fdb_delete_default(vxlan);
+	vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
 
 	free_percpu(dev->tstats);
 }
@@ -2536,6 +2590,8 @@ static void vxlan_setup(struct net_device *dev)
 	dev->vlan_features = dev->features;
 	dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
 	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
+	dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+	dev->features |= dev->hw_features;
 	netif_keep_dst(dev);
 	dev->priv_flags |= IFF_NO_QUEUE;
 
@@ -2921,6 +2977,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
 				       NLM_F_EXCL|NLM_F_CREATE,
 				       vxlan->cfg.dst_port,
 				       vxlan->default_dst.remote_vni,
+				       vxlan->default_dst.remote_vni,
 				       vxlan->default_dst.remote_ifindex,
 				       NTF_SELF);
 		if (err)
@@ -2929,7 +2986,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
 
 	err = register_netdevice(dev);
 	if (err) {
-		vxlan_fdb_delete_default(vxlan);
+		vxlan_fdb_delete_default(vxlan, vxlan->cfg.vni);
 		return err;
 	}
 
@@ -3023,19 +3080,19 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
 		conf.flags |= VXLAN_F_UDP_ZERO_CSUM_TX;
 
 	if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] &&
-	    nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
+	    !nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]))
 		conf.flags |= VXLAN_F_UDP_ZERO_CSUM6_TX;
 
 	if (data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] &&
-	    nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
+	    !nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
 		conf.flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
 
 	if (data[IFLA_VXLAN_REMCSUM_TX] &&
-	    nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX]))
+	    !nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX]))
 		conf.flags |= VXLAN_F_REMCSUM_TX;
 
 	if (data[IFLA_VXLAN_REMCSUM_RX] &&
-	    nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX]))
+	    !nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX]))
 		conf.flags |= VXLAN_F_REMCSUM_RX;
 
 	if (data[IFLA_VXLAN_GBP])
diff --git a/include/uapi/linux/neighbour.h b/include/uapi/linux/neighbour.h
index bd99a8d..f3d16db 100644
--- a/include/uapi/linux/neighbour.h
+++ b/include/uapi/linux/neighbour.h
@@ -26,6 +26,7 @@ enum {
 	NDA_IFINDEX,
 	NDA_MASTER,
 	NDA_LINK_NETNSID,
+	NDA_SRC_VNI,
 	__NDA_MAX
 };
 
-- 
1.7.10.4

^ permalink raw reply related

* [RFC PATCH net-next 0/5] bridge: per vlan lwt and dst_metadata support
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi

From: Roopa Prabhu <roopa@cumulusnetworks.com>

High level summary:
lwt and dst_metadata/collect_metadata have enabled vxlan l3 deployments
to use a single vxlan netdev for multiple vnis eliminating the scalability
problem with using a single vxlan netdev per vni. This series tries to
do the same for vxlan netdevs in pure l2 bridged networks.
Use-case/deployment and details are below.

Deployment scerario details:
As we know VXLAN is used to build layer 2 virtual networks across the
underlay layer3 infrastructure. A VXLAN tunnel endpoint (VTEP)
originates and terminates VXLAN tunnels. And a VTEP can be a TOR switch
or a vswitch in the hypervisor. This patch series mainly
focuses on the TOR switch configured as a Vtep. Vxlan segment ID (vni)
along with vlan id is used to identify layer 2 segments in a vxlan
overlay network. Vxlan bridging is the function provided by Vteps to terminate
vxlan tunnels and map the vxlan vni to traditional end host vlan. This is
covered in the "VXLAN Deployment Scenarios" in sections 6 and 6.1 in RFC 7348.
To provide vxlan bridging function, a vtep has to map vlan to a vni. The rfc
says that the ingress VTEP device shall remove the IEEE 802.1Q VLAN tag in
the original Layer 2 packet if there is one before encapsulating the packet
into the VXLAN format to transmit it through the underlay network. The remote
VTEP devices have information about the VLAN in which the packet will be
placed based on their own VLAN-to-VXLAN VNI mapping configurations.

Existing solution:
Without this patch series one can deploy such a vtep configuration by
by adding the local ports and vxlan netdevs into a vlan filtering bridge.
The local ports are configured as trunk ports carrying all vlans.
A vxlan netdev per vni is added to the bridge. Vlan mapping to vni is
achieved by configuring the vlan as pvid on the corresponding vxlan netdev.
The vxlan netdev only receives traffic corresponding to the vlan it is mapped
to. This configuration maps traffic belonging to a vlan to the corresponding
vxlan segment.

          -----------------------------------
         |              bridge               |
         |                                   |
          -----------------------------------
            |100,200       |100 (pvid)    |200 (pvid)
            |              |              |
           swp1          vxlan1000      vxlan2000
                    
This provides the required vxlan bridging function but poses a
scalability problem with using a single vxlan netdev for each vni.

Solution in this patch series:
The Goal is to use a single vxlan device to carry all vnis similar
to the vxlan collect metadata mode but vxlan driver still carrying all
the forwarding information.
- vxlan driver changes:
    - enable collect metadata mode device to be used with learning,
      replication, fdb
    - A single fdb table hashed by (mac, vni)
    - rx path already has the vni
    - tx path expects a vni in the packet with dst_metadata and vxlan
      driver has all the forwarding information for the vni in the
      dst_metadata.

- Bridge driver changes: per vlan LWT and dst_metadata support:
    - Our use case is vxlan and 1-1 mapping between vlan and vni, but I have
      kept the api generic for any tunnel info
    - Uapi to configure/unconfigure/dump per vlan tunnel data
    - new bridge port flag to turn this feature on/off. off by default
    - ingress hook:
        - if port is a lwt tunnel port, use tunnel info in
          attached dst_metadata to map it to a local vlan
    - egress hook:
        - if port is a lwt tunnel port, use tunnel info attached to vlan
          to set dst_metadata on the skb

Other approaches tried and vetoed:
- tc vlan push/pop and tunnel metadata dst:
    - posses a tc rule scalability problem (2 rules per vni)
    - cannot handle the case where a packet needs to be replicated to
      multiple vxlan remote tunnel end-points.. which the vxlan driver
      can do today by having multiple remote destinations per fdb.
- making vxlan driver understand vlan-vni mapping:
    - I had a series almost ready with this one but soon realized
      it duplicated a lot of vlan handling code in the vxlan driver

This series is briefly tested for functionality. Sending it out as RFC while
I continue to test it more. There are some rough edges which I am in the process
of fixing.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>

Roopa Prabhu (5):
  ip_tunnels: new IP_TUNNEL_INFO_BRIDGE flag for ip_tunnel_info mode
  vxlan: make COLLECT_METADATA mode bridge friendly
  bridge: uapi: add per vlan tunnel info
  bridge: vlan lwt and dst_metadata netlink support
  bridge: vlan lwt dst_metadata hooks in ingress and egress paths

 drivers/net/vxlan.c            |  209 ++++++++++++--------
 include/linux/if_bridge.h      |    1 +
 include/net/ip_tunnels.h       |    1 +
 include/uapi/linux/if_bridge.h |   11 ++
 include/uapi/linux/if_link.h   |    1 +
 include/uapi/linux/neighbour.h |    1 +
 net/bridge/br_input.c          |    5 +
 net/bridge/br_netlink.c        |  410 ++++++++++++++++++++++++++++++++++------
 net/bridge/br_private.h        |   22 +++
 net/bridge/br_vlan.c           |  193 ++++++++++++++++++-
 10 files changed, 717 insertions(+), 137 deletions(-)

-- 
1.7.10.4

^ permalink raw reply

* [RFC PATCH net-next 1/5] ip_tunnels: new IP_TUNNEL_INFO_BRIDGE flag for ip_tunnel_info mode
From: Roopa Prabhu @ 2017-01-21  5:46 UTC (permalink / raw)
  To: netdev; +Cc: davem, stephen, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

From: Roopa Prabhu <roopa@cumulusnetworks.com>

New ip_tunnel_info flag to represent bridged tunnel metadata.
Used by bridge driver later in the series to pass per vlan dst
metadata to bridge ports.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
 include/net/ip_tunnels.h |    1 +
 1 file changed, 1 insertion(+)

diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 3d4ca4d..9505679 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -58,6 +58,7 @@ struct ip_tunnel_key {
 /* Flags for ip_tunnel_info mode. */
 #define IP_TUNNEL_INFO_TX	0x01	/* represents tx tunnel parameters */
 #define IP_TUNNEL_INFO_IPV6	0x02	/* key contains IPv6 addresses */
+#define IP_TUNNEL_INFO_BRIDGE	0x04	/* represents a bridged tunnel id */
 
 /* Maximum tunnel options length. */
 #define IP_TUNNEL_OPTS_MAX					\
-- 
1.7.10.4

^ permalink raw reply related

* Re: [PATCH] net: qcom/emac: claim the irq only when the device is opened
From: Lino Sanfilippo @ 2017-01-21  2:14 UTC (permalink / raw)
  To: Timur Tabi, David Miller, netdev
In-Reply-To: <91ecc8ae-2ee4-5b08-ea04-b65890d3fa0e@codeaurora.org>


On 20.01.2017 22:36, Timur Tabi wrote:
> On 01/20/2017 03:31 PM, Lino Sanfilippo wrote:
>>
>> In emac_mac_down() however we need synchronize_irq(), since it ensures
>> that the irq
>> handler is not running any more when it (synchronize_irq) returns.
>
> So in general, if a driver disables a interrupt but does not free it, 
> it should call synchronize_irq()?
>

Yes, thats right.

Regards,
Lino

^ permalink raw reply

* [PATCH v3 net-next] Introduce a sysctl that modifies the value of PROT_SOCK.
From: Krister Johansen @ 2017-01-21  1:49 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Stephen Hemminger, David S. Miller, netdev
In-Reply-To: <1484231997.15816.36.camel@edumazet-glaptop3.roam.corp.google.com>

Add net.ipv4.ip_unprivileged_port_start, which is a per namespace sysctl
that denotes the first unprivileged inet port in the namespace.  To
disable all privileged ports set this to zero.  It also checks for
overlap with the local port range.  The privileged and local range may
not overlap.

The use case for this change is to allow containerized processes to bind
to priviliged ports, but prevent them from ever being allowed to modify
their container's network configuration.  The latter is accomplished by
ensuring that the network namespace is not a child of the user
namespace.  This modification was needed to allow the container manager
to disable a namespace's priviliged port restrictions without exposing
control of the network namespace to processes in the user namespace.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 Documentation/networking/ip-sysctl.txt |  9 ++++++
 include/net/ip.h                       | 10 +++++++
 include/net/netns/ipv4.h               |  1 +
 net/ipv4/af_inet.c                     |  5 +++-
 net/ipv4/sysctl_net_ipv4.c             | 50 +++++++++++++++++++++++++++++++++-
 net/ipv6/af_inet6.c                    |  3 +-
 net/netfilter/ipvs/ip_vs_ctl.c         |  7 ++---
 net/sctp/socket.c                      | 10 ++++---
 security/selinux/hooks.c               |  3 +-
 9 files changed, 86 insertions(+), 12 deletions(-)

Changes v1 -> v2:

Remove LOWPORT_SYSCTL config option.  This is now always enabled as long
as CONFIG_SYSCTL is.

Changes v2 -> v3:

Add documentation to ip-sysctl.txt.
Rename "protected" variables and functions to "privileged."

diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index aa1bb49..17f2e77 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -822,6 +822,15 @@ ip_local_reserved_ports - list of comma separated ranges
 
 	Default: Empty
 
+ip_unprivileged_port_start - INTEGER
+	This is a per-namespace sysctl.  It defines the first
+	unprivileged port in the network namespace.  Privileged ports
+	require root or CAP_NET_BIND_SERVICE in order to bind to them.
+	To disable all privileged ports, set this to 0.  It may not
+	overlap with the ip_local_reserved_ports range.
+
+	Default: 1024
+
 ip_nonlocal_bind - BOOLEAN
 	If set, allows processes to bind() to non-local IP addresses,
 	which can be quite useful - but may break some applications.
diff --git a/include/net/ip.h b/include/net/ip.h
index ab6761a..bf264a8 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -263,11 +263,21 @@ static inline bool sysctl_dev_name_is_allowed(const char *name)
 	return strcmp(name, "default") != 0  && strcmp(name, "all") != 0;
 }
 
+static inline int inet_prot_sock(struct net *net)
+{
+	return net->ipv4.sysctl_ip_prot_sock;
+}
+
 #else
 static inline int inet_is_local_reserved_port(struct net *net, int port)
 {
 	return 0;
 }
+
+static inline int inet_prot_sock(struct net *net)
+{
+	return PROT_SOCK;
+}
 #endif
 
 __be32 inet_current_timestamp(void);
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 8e3f5b6..e365732 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -135,6 +135,7 @@ struct netns_ipv4 {
 
 #ifdef CONFIG_SYSCTL
 	unsigned long *sysctl_local_reserved_ports;
+	int sysctl_ip_prot_sock;
 #endif
 
 #ifdef CONFIG_IP_MROUTE
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index aae410b..28fe8da 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -479,7 +479,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
 	snum = ntohs(addr->sin_port);
 	err = -EACCES;
-	if (snum && snum < PROT_SOCK &&
+	if (snum && snum < inet_prot_sock(net) &&
 	    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
 		goto out;
 
@@ -1700,6 +1700,9 @@ static __net_init int inet_init_net(struct net *net)
 	net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
 	net->ipv4.sysctl_ip_dynaddr = 0;
 	net->ipv4.sysctl_ip_early_demux = 1;
+#ifdef CONFIG_SYSCTL
+	net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
+#endif
 
 	return 0;
 }
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index c8d2836..1b86199 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -35,6 +35,8 @@ static int ip_local_port_range_min[] = { 1, 1 };
 static int ip_local_port_range_max[] = { 65535, 65535 };
 static int tcp_adv_win_scale_min = -31;
 static int tcp_adv_win_scale_max = 31;
+static int ip_privileged_port_min;
+static int ip_privileged_port_max = 65535;
 static int ip_ttl_min = 1;
 static int ip_ttl_max = 255;
 static int tcp_syn_retries_min = 1;
@@ -79,7 +81,12 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0) {
-		if (range[1] < range[0])
+		/* Ensure that the upper limit is not smaller than the lower,
+		 * and that the lower does not encroach upon the privileged
+		 * port limit.
+		 */
+		if ((range[1] < range[0]) ||
+		    (range[0] < net->ipv4.sysctl_ip_prot_sock))
 			ret = -EINVAL;
 		else
 			set_local_port_range(net, range);
@@ -88,6 +95,40 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
 	return ret;
 }
 
+/* Validate changes from /proc interface. */
+static int ipv4_privileged_ports(struct ctl_table *table, int write,
+				void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(table->data, struct net,
+	    ipv4.sysctl_ip_prot_sock);
+	int ret;
+	int pports;
+	int range[2];
+	struct ctl_table tmp = {
+		.data = &pports,
+		.maxlen = sizeof(pports),
+		.mode = table->mode,
+		.extra1 = &ip_privileged_port_min,
+		.extra2 = &ip_privileged_port_max,
+	};
+
+	pports = net->ipv4.sysctl_ip_prot_sock;
+
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+	if (write && ret == 0) {
+		inet_get_local_port_range(net, &range[0], &range[1]);
+		/* Ensure that the local port range doesn't overlap with the
+		 * privileged port range.
+		 */
+		if (range[0] < pports)
+			ret = -EINVAL;
+		else
+			net->ipv4.sysctl_ip_prot_sock = pports;
+	}
+
+	return ret;
+}
 
 static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
 {
@@ -964,6 +1005,13 @@ static struct ctl_table ipv4_net_table[] = {
 		.extra2		= &one,
 	},
 #endif
+	{
+		.procname	= "ip_unprivileged_port_start",
+		.maxlen		= sizeof(int),
+		.data		= &init_net.ipv4.sysctl_ip_prot_sock,
+		.mode		= 0644,
+		.proc_handler	= ipv4_privileged_ports,
+	},
 	{ }
 };
 
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index aa42123..04db406 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -302,7 +302,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 		return -EINVAL;
 
 	snum = ntohs(addr->sin6_port);
-	if (snum && snum < PROT_SOCK && !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+	if (snum && snum < inet_prot_sock(net) &&
+	    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
 		return -EACCES;
 
 	lock_sock(sk);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 55e0169..8b7416f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -426,10 +426,9 @@ ip_vs_service_find(struct netns_ipvs *ipvs, int af, __u32 fwmark, __u16 protocol
 	 */
 	svc = __ip_vs_service_find(ipvs, af, protocol, vaddr, vport);
 
-	if (svc == NULL
-	    && protocol == IPPROTO_TCP
-	    && atomic_read(&ipvs->ftpsvc_counter)
-	    && (vport == FTPDATA || ntohs(vport) >= PROT_SOCK)) {
+	if (!svc && protocol == IPPROTO_TCP &&
+	    atomic_read(&ipvs->ftpsvc_counter) &&
+	    (vport == FTPDATA || ntohs(vport) >= inet_prot_sock(ipvs->net))) {
 		/*
 		 * Check if ftp service entry exists, the packet
 		 * might belong to FTP data connections.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index bee4dd3..d699d2c 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -360,7 +360,7 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
 		}
 	}
 
-	if (snum && snum < PROT_SOCK &&
+	if (snum && snum < inet_prot_sock(net) &&
 	    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
 		return -EACCES;
 
@@ -1152,8 +1152,10 @@ static int __sctp_connect(struct sock *sk,
 				 * accept new associations, but it SHOULD NOT
 				 * be permitted to open new associations.
 				 */
-				if (ep->base.bind_addr.port < PROT_SOCK &&
-				    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
+				if (ep->base.bind_addr.port <
+				    inet_prot_sock(net) &&
+				    !ns_capable(net->user_ns,
+				    CAP_NET_BIND_SERVICE)) {
 					err = -EACCES;
 					goto out_free;
 				}
@@ -1818,7 +1820,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 			 * but it SHOULD NOT be permitted to open new
 			 * associations.
 			 */
-			if (ep->base.bind_addr.port < PROT_SOCK &&
+			if (ep->base.bind_addr.port < inet_prot_sock(net) &&
 			    !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
 				err = -EACCES;
 				goto out_unlock;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c7c6619..53cb6da 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4365,7 +4365,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
 
 			inet_get_local_port_range(sock_net(sk), &low, &high);
 
-			if (snum < max(PROT_SOCK, low) || snum > high) {
+			if (snum < max(inet_prot_sock(sock_net(sk)), low) ||
+			    snum > high) {
 				err = sel_netport_sid(sk->sk_protocol,
 						      snum, &sid);
 				if (err)
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH net-next 8/8] net: dsa: mv88e6xxx: Fix typ0 when configuring 2.5Gbps
From: Vivien Didelot @ 2017-01-21  0:53 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Andrew Lunn
In-Reply-To: <1484955062-26718-9-git-send-email-andrew@lunn.ch>

Hi Andrew,

Andrew Lunn <andrew@lunn.ch> writes:

> In order to enable 2.5Gbps mode, we need the base speed of 10G, plus
> the Alt bit setting. Fix a typ0 that used 1Gb base speed.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>

Thanks,

        Vivien

^ permalink raw reply

* Re: [PATCH net-next 5/8] net: dsa: mv88e6xxx: Workaround missing PHY ID on mv88e6390
From: Vivien Didelot @ 2017-01-21  0:26 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Andrew Lunn
In-Reply-To: <1484955062-26718-6-git-send-email-andrew@lunn.ch>

Hi Andrew,

Andrew Lunn <andrew@lunn.ch> writes:

> The internal PHYs of the mv88e6390 do not have a model ID. Trap any
> calls to the ID register, and if it is zero, return the ID for the
> mv88e6390. The Marvell PHY driver can then bind to this ID.

This, in addition to the temperature code not working (despite what the
datasheet says) makes me wonder if this is intentional from Marvell. Do
we have a revision number for the 88E6390X's on the ZII Dev Rev C board?

It would be interesting to ask Gregory maybe about that. This looks not
"production-ready".

Other than that, I have no objection on the patch itself if that is
indeed expected from them...

Thanks,

        Vivien

^ permalink raw reply

* [PATCH net v2 1/2] net: Specify the owning module for lwtunnel ops
From: Robert Shearman @ 2017-01-21  0:21 UTC (permalink / raw)
  To: davem
  Cc: netdev, Tom Herbert, Roopa Prabhu, David Lebrun, Thomas Graf,
	David Ahern, Robert Shearman
In-Reply-To: <1484958086-6151-1-git-send-email-rshearma@brocade.com>

Modules implementing lwtunnel ops should not be allowed to unload
while there is state alive using those ops, so specify the owning
module for all lwtunnel ops.

Signed-off-by: Robert Shearman <rshearma@brocade.com>
---
 include/net/lwtunnel.h    | 2 ++
 net/core/lwt_bpf.c        | 1 +
 net/ipv4/ip_tunnel_core.c | 2 ++
 net/ipv6/ila/ila_lwt.c    | 1 +
 net/ipv6/seg6_iptunnel.c  | 1 +
 net/mpls/mpls_iptunnel.c  | 1 +
 6 files changed, 8 insertions(+)

diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h
index 0b585f1fd340..73dd87647460 100644
--- a/include/net/lwtunnel.h
+++ b/include/net/lwtunnel.h
@@ -44,6 +44,8 @@ struct lwtunnel_encap_ops {
 	int (*get_encap_size)(struct lwtunnel_state *lwtstate);
 	int (*cmp_encap)(struct lwtunnel_state *a, struct lwtunnel_state *b);
 	int (*xmit)(struct sk_buff *skb);
+
+	struct module *owner;
 };
 
 #ifdef CONFIG_LWTUNNEL
diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c
index 71bb3e2eca08..b3eef90b2df9 100644
--- a/net/core/lwt_bpf.c
+++ b/net/core/lwt_bpf.c
@@ -386,6 +386,7 @@ static const struct lwtunnel_encap_ops bpf_encap_ops = {
 	.fill_encap	= bpf_fill_encap_info,
 	.get_encap_size = bpf_encap_nlsize,
 	.cmp_encap	= bpf_encap_cmp,
+	.owner		= THIS_MODULE,
 };
 
 static int __init bpf_lwt_init(void)
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index fed3d29f9eb3..0fd1976ab63b 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -313,6 +313,7 @@ static const struct lwtunnel_encap_ops ip_tun_lwt_ops = {
 	.fill_encap = ip_tun_fill_encap_info,
 	.get_encap_size = ip_tun_encap_nlsize,
 	.cmp_encap = ip_tun_cmp_encap,
+	.owner = THIS_MODULE,
 };
 
 static const struct nla_policy ip6_tun_policy[LWTUNNEL_IP6_MAX + 1] = {
@@ -403,6 +404,7 @@ static const struct lwtunnel_encap_ops ip6_tun_lwt_ops = {
 	.fill_encap = ip6_tun_fill_encap_info,
 	.get_encap_size = ip6_tun_encap_nlsize,
 	.cmp_encap = ip_tun_cmp_encap,
+	.owner = THIS_MODULE,
 };
 
 void __init ip_tunnel_core_init(void)
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
index a7bc54ab46e2..13b5e85fe0d5 100644
--- a/net/ipv6/ila/ila_lwt.c
+++ b/net/ipv6/ila/ila_lwt.c
@@ -238,6 +238,7 @@ static const struct lwtunnel_encap_ops ila_encap_ops = {
 	.fill_encap = ila_fill_encap_info,
 	.get_encap_size = ila_encap_nlsize,
 	.cmp_encap = ila_encap_cmp,
+	.owner = THIS_MODULE,
 };
 
 int ila_lwt_init(void)
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 1d60cb132835..c46f8cbf5ab5 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -422,6 +422,7 @@ static const struct lwtunnel_encap_ops seg6_iptun_ops = {
 	.fill_encap = seg6_fill_encap_info,
 	.get_encap_size = seg6_encap_nlsize,
 	.cmp_encap = seg6_encap_cmp,
+	.owner = THIS_MODULE,
 };
 
 int __init seg6_iptunnel_init(void)
diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
index 2f7ccd934416..1d281c1ff7c1 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -215,6 +215,7 @@ static const struct lwtunnel_encap_ops mpls_iptun_ops = {
 	.fill_encap = mpls_fill_encap_info,
 	.get_encap_size = mpls_encap_nlsize,
 	.cmp_encap = mpls_encap_cmp,
+	.owner = THIS_MODULE,
 };
 
 static int __init mpls_iptunnel_init(void)
-- 
2.1.4

^ permalink raw reply related

* [PATCH net v2 2/2] lwtunnel: Fix oops on state free after encap module unload
From: Robert Shearman @ 2017-01-21  0:21 UTC (permalink / raw)
  To: davem
  Cc: netdev, Tom Herbert, Roopa Prabhu, David Lebrun, Thomas Graf,
	David Ahern, Robert Shearman
In-Reply-To: <1484958086-6151-1-git-send-email-rshearma@brocade.com>

When attempting to free lwtunnel state after the module for the encap
has been unloaded an oops occurs:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000008
IP: lwtstate_free+0x18/0x40
[..]
task: ffff88003e372380 task.stack: ffffc900001fc000
RIP: 0010:lwtstate_free+0x18/0x40
RSP: 0018:ffff88003fd83e88 EFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff88002bbb3380 RCX: ffff88000c91a300
[..]
Call Trace:
 <IRQ>
 free_fib_info_rcu+0x195/0x1a0
 ? rt_fibinfo_free+0x50/0x50
 rcu_process_callbacks+0x2d3/0x850
 ? rcu_process_callbacks+0x296/0x850
 __do_softirq+0xe4/0x4cb
 irq_exit+0xb0/0xc0
 smp_apic_timer_interrupt+0x3d/0x50
 apic_timer_interrupt+0x93/0xa0
[..]
Code: e8 6e c6 fc ff 89 d8 5b 5d c3 bb de ff ff ff eb f4 66 90 66 66 66 66 90 55 48 89 e5 53 0f b7 07 48 89 fb 48 8b 04 c5 00 81 d5 81 <48> 8b 40 08 48 85 c0 74 13 ff d0 48 8d 7b 20 be 20 00 00 00 e8

The problem is after the module for the encap is unloaded the
corresponding ops is removed and thus is NULL here.

Modules implementing lwtunnel ops should not be allowed to unload
while there is state alive using those ops, so grab the module
reference for the ops on creating lwtunnel state and of course release
the reference when freeing the state.

Fixes: 1104d9ba443a ("lwtunnel: Add destroy state operation")
Signed-off-by: Robert Shearman <rshearma@brocade.com>
---
 net/core/lwtunnel.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index 47b1dd65947b..ebfaffaa777b 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -115,8 +115,12 @@ int lwtunnel_build_state(struct net_device *dev, u16 encap_type,
 	ret = -EOPNOTSUPP;
 	rcu_read_lock();
 	ops = rcu_dereference(lwtun_encaps[encap_type]);
-	if (likely(ops && ops->build_state))
-		ret = ops->build_state(dev, encap, family, cfg, lws);
+	if (likely(ops)) {
+		if (likely(try_module_get(ops->owner) && ops->build_state))
+			ret = ops->build_state(dev, encap, family, cfg, lws);
+		if (ret)
+			module_put(ops->owner);
+	}
 	rcu_read_unlock();
 
 	return ret;
@@ -194,6 +198,7 @@ void lwtstate_free(struct lwtunnel_state *lws)
 	} else {
 		kfree(lws);
 	}
+	module_put(ops->owner);
 }
 EXPORT_SYMBOL(lwtstate_free);
 
-- 
2.1.4

^ permalink raw reply related

* [PATCH net v2 0/2] net: Fix oops on state free after lwt module unload
From: Robert Shearman @ 2017-01-21  0:21 UTC (permalink / raw)
  To: davem
  Cc: netdev, Tom Herbert, Roopa Prabhu, David Lebrun, Thomas Graf,
	David Ahern, Robert Shearman
In-Reply-To: <1484753523-26230-1-git-send-email-rshearma@brocade.com>

An oops is seen in lwtstate_free after an lwt ops module has been
unloaded. This patchset fixes this by preventing modules implementing
lwtunnel ops from being unloaded whilst there's state alive using
those ops. The first patch adds fills in a new owner field in all lwt
ops and the second patch makes use of this to reference count the
modules as state is built and destroyed using them.

Changes in v2:
 - specify module owner for all modules as suggested by DaveM
 - reference count all modules building lwt state, not just those ops
   implementing destroy_state, as also suggested by DaveM.
 - rebased on top of David Ahern's lwtunnel changes

Robert Shearman (2):
  net: Specify the owning module for lwtunnel ops
  lwtunnel: Fix oops on state free after encap module unload

 include/net/lwtunnel.h    | 2 ++
 net/core/lwt_bpf.c        | 1 +
 net/core/lwtunnel.c       | 9 +++++++--
 net/ipv4/ip_tunnel_core.c | 2 ++
 net/ipv6/ila/ila_lwt.c    | 1 +
 net/ipv6/seg6_iptunnel.c  | 1 +
 net/mpls/mpls_iptunnel.c  | 1 +
 7 files changed, 15 insertions(+), 2 deletions(-)

-- 
2.1.4

^ permalink raw reply

* Re: [PATCH net-next 4/8] net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
From: Vivien Didelot @ 2017-01-21  0:20 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Andrew Lunn
In-Reply-To: <1484955062-26718-5-git-send-email-andrew@lunn.ch>

Hi Andrew,

Andrew Lunn <andrew@lunn.ch> writes:

> Unlike most ports, ports 9 and 10 of the 6390X family have configurable
> PHY modes. Set the mode as part of adjust_link().
>
> Ordering is important, because the SERDES interfaces connected to
> ports 9 and 10 can be split and assigned to other ports. The CMODE has
> to be correctly set before the SERDES interface on another port can be
> configured. Such configuration is likely to be performed in
> port_enable() and port_disabled(), called on slave_open() and
> slave_close().
>
> The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
> case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
> called early in the switch setup.
>
> When ports 9 or 10 are used as user ports, and have a fixed-phy, when
> the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
> which results in the adjust_link function being called, setting the
> cmode. The port_enable() will for other ports will be called much
> later.
>
> When ports 9 or 10 are used as user ports and have a real phy attached
> which does not use all the available SERDES interface, e.g. a 1Gbps
> SGMII, there is currently no mechanism in place to set the CMODE of
> the port from software. It must be hoped the stripping resistors are
> correct.
>
> At the same time, add a function to get the cmode. This will be needed
> when configuring the SERDES interfaces.
>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>

Thanks for the very descriptive message, the patch looks perfect.

        Vivien

^ permalink raw reply

* Re: [PATCH net-next 2/8] net: phy: Add 2000base-x, 2500base-x and rxaui modes
From: Florian Fainelli @ 2017-01-21  0:07 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Vivien Didelot
In-Reply-To: <1484955062-26718-3-git-send-email-andrew@lunn.ch>

On 01/20/2017 03:30 PM, Andrew Lunn wrote:
> The mv88e6390 ports 9 and 10 supports some additional PHY modes. Add
> these modes to the PHY core so they can be used in the binding.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

Can you also send a Device Tree specification patch with these updates?
Thanks!
-- 
Florian

^ permalink raw reply

* Re: [PATCH net-next 3/8] net: dsa: mv88e6xxx: Fix ATU age timer for MV88E6390
From: Vivien Didelot @ 2017-01-21  0:10 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Andrew Lunn
In-Reply-To: <1484955062-26718-4-git-send-email-andrew@lunn.ch>

Hi Andrew,

Andrew Lunn <andrew@lunn.ch> writes:

> The MV88E6390 family uses a different ATU age timer coefficient.
> Fix the the info structures.

Redundant "the" here. Otherwise good catch, the minimum age time is 3.75
seconds.

> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>

Thanks,

        Vivien

^ permalink raw reply

* [PATCH 2/4] xfrm_user: Allow common functions to be called from another file
From: Kevin Cernekee @ 2017-01-21  0:05 UTC (permalink / raw)
  To: steffen.klassert, herbert, davem, paul, sds, eparis
  Cc: linux-kernel, netdev, selinux, fw, fan.du, dianders, dtor
In-Reply-To: <20170121000507.34381-1-cernekee@chromium.org>

xfrm_user_legacy.c will need to call a few common functions.  Make
sure them have an "xfrm_" prefix, and declare them in a new xfrm_user.h
header.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
---
 net/xfrm/xfrm_user.c | 147 +++++++++++++++++----------------------------------
 net/xfrm/xfrm_user.h |  90 +++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+), 99 deletions(-)
 create mode 100644 net/xfrm/xfrm_user.h

diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index ed389aad4994..4d733f02c3a1 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -32,6 +32,7 @@
 #include <linux/in6.h>
 #endif
 #include <asm/unaligned.h>
+#include "xfrm_user.h"
 
 static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type)
 {
@@ -100,7 +101,7 @@ static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,
 		*addrp = nla_data(rt);
 }
 
-static inline int verify_sec_ctx_len(struct nlattr **attrs)
+int xfrm_verify_sec_ctx_len(struct nlattr **attrs)
 {
 	struct nlattr *rt = attrs[XFRMA_SEC_CTX];
 	struct xfrm_user_sec_ctx *uctx;
@@ -148,8 +149,8 @@ static inline int verify_replay(const struct xfrm_usersa_info *p,
 	return 0;
 }
 
-static int verify_newsa_info(const struct xfrm_usersa_info *p,
-			     struct nlattr **attrs)
+int xfrm_verify_newsa_info(const struct xfrm_usersa_info *p,
+			   struct nlattr **attrs)
 {
 	int err;
 
@@ -241,7 +242,7 @@ static int verify_newsa_info(const struct xfrm_usersa_info *p,
 		goto out;
 	if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP)))
 		goto out;
-	if ((err = verify_sec_ctx_len(attrs)))
+	if ((err = xfrm_verify_sec_ctx_len(attrs)))
 		goto out;
 	if ((err = verify_replay(p, attrs)))
 		goto out;
@@ -460,17 +461,6 @@ static int xfrm_alloc_replay_state_esn(
 	return 0;
 }
 
-static inline int xfrm_user_sec_ctx_size(const struct xfrm_sec_ctx *xfrm_ctx)
-{
-	int len = 0;
-
-	if (xfrm_ctx) {
-		len += sizeof(struct xfrm_user_sec_ctx);
-		len += xfrm_ctx->ctx_len;
-	}
-	return len;
-}
-
 static void copy_from_user_state(struct xfrm_state *x,
 				 const struct xfrm_usersa_info *p)
 {
@@ -537,10 +527,10 @@ static void xfrm_update_ae_params(struct xfrm_state *x,
 		x->replay_maxdiff = nla_get_u32(rt);
 }
 
-static struct xfrm_state *xfrm_state_construct(struct net *net,
-					       const struct xfrm_usersa_info *p,
-					       struct nlattr **attrs,
-					       int *errp)
+struct xfrm_state *xfrm_state_construct(struct net *net,
+					const struct xfrm_usersa_info *p,
+					struct nlattr **attrs,
+					int *errp)
 {
 	struct xfrm_state *x = xfrm_state_alloc(net);
 	int err = -ENOMEM;
@@ -634,7 +624,7 @@ static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	int err;
 	struct km_event c;
 
-	err = verify_newsa_info(p, attrs);
+	err = xfrm_verify_newsa_info(p, attrs);
 	if (err)
 		return err;
 
@@ -666,10 +656,10 @@ static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
-						 const struct xfrm_usersa_id *p,
-						 struct nlattr **attrs,
-						 int *errp)
+struct xfrm_state *xfrm_user_state_lookup(struct net *net,
+					  const struct xfrm_usersa_id *p,
+					  struct nlattr **attrs,
+					  int *errp)
 {
 	struct xfrm_state *x = NULL;
 	struct xfrm_mark m;
@@ -757,14 +747,7 @@ static void copy_to_user_state(const struct xfrm_state *x,
 	p->seq = x->km.seq;
 }
 
-struct xfrm_dump_info {
-	struct sk_buff *in_skb;
-	struct sk_buff *out_skb;
-	u32 nlmsg_seq;
-	u16 nlmsg_flags;
-};
-
-static int copy_sec_ctx(const struct xfrm_sec_ctx *s, struct sk_buff *skb)
+int xfrm_copy_sec_ctx(const struct xfrm_sec_ctx *s, struct sk_buff *skb)
 {
 	struct xfrm_user_sec_ctx *uctx;
 	struct nlattr *attr;
@@ -785,8 +768,8 @@ static int copy_sec_ctx(const struct xfrm_sec_ctx *s, struct sk_buff *skb)
 	return 0;
 }
 
-static int copy_to_user_auth(const struct xfrm_algo_auth *auth,
-			     struct sk_buff *skb)
+int xfrm_copy_to_user_auth(const struct xfrm_algo_auth *auth,
+			   struct sk_buff *skb)
 {
 	struct xfrm_algo *algo;
 	struct nlattr *nla;
@@ -837,7 +820,7 @@ static int copy_to_user_state_extra(const struct xfrm_state *x,
 			goto out;
 	}
 	if (x->aalg) {
-		ret = copy_to_user_auth(x->aalg, skb);
+		ret = xfrm_copy_to_user_auth(x->aalg, skb);
 		if (!ret)
 			ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC,
 				      xfrm_alg_auth_len(x->aalg), x->aalg);
@@ -877,7 +860,7 @@ static int copy_to_user_state_extra(const struct xfrm_state *x,
 	if (ret)
 		goto out;
 	if (x->security)
-		ret = copy_sec_ctx(x->security, skb);
+		ret = xfrm_copy_sec_ctx(x->security, skb);
 out:
 	return ret;
 }
@@ -992,8 +975,8 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
 /* A wrapper for nlmsg_multicast() checking that nlsk is still available.
  * Must be called with RCU read lock.
  */
-static inline int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
-				       u32 pid, unsigned int group)
+int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
+			 u32 pid, unsigned int group)
 {
 	struct sock *nlsk = rcu_dereference(net->xfrm.nlsk);
 
@@ -1270,7 +1253,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int verify_policy_dir(u8 dir)
+int xfrm_verify_policy_dir(u8 dir)
 {
 	switch (dir) {
 	case XFRM_POLICY_IN:
@@ -1301,7 +1284,7 @@ static int verify_policy_type(u8 type)
 	return 0;
 }
 
-static int verify_newpolicy_info(const struct xfrm_userpolicy_info *p)
+int xfrm_verify_newpolicy_info(const struct xfrm_userpolicy_info *p)
 {
 	int ret;
 
@@ -1340,7 +1323,7 @@ static int verify_newpolicy_info(const struct xfrm_userpolicy_info *p)
 		return -EINVAL;
 	}
 
-	ret = verify_policy_dir(p->dir);
+	ret = xfrm_verify_policy_dir(p->dir);
 	if (ret)
 		return ret;
 	if (p->index && ((p->index & XFRM_POLICY_MAX) != p->dir))
@@ -1440,7 +1423,7 @@ static int copy_from_user_tmpl(struct xfrm_policy *pol, struct nlattr **attrs)
 	return 0;
 }
 
-static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
+int xfrm_copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
 {
 	struct nlattr *rt = attrs[XFRMA_POLICY_TYPE];
 	struct xfrm_userpolicy_type *upt;
@@ -1490,11 +1473,10 @@ static void copy_to_user_policy(const struct xfrm_policy *xp,
 	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
 }
 
-static struct xfrm_policy *xfrm_policy_construct(
-	struct net *net,
-	const struct xfrm_userpolicy_info *p,
-	struct nlattr **attrs,
-	int *errp)
+struct xfrm_policy *xfrm_policy_construct(struct net *net,
+					  const struct xfrm_userpolicy_info *p,
+					  struct nlattr **attrs,
+					  int *errp)
 {
 	struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
 	int err;
@@ -1506,7 +1488,7 @@ static struct xfrm_policy *xfrm_policy_construct(
 
 	copy_from_user_policy(xp, p);
 
-	err = copy_from_user_policy_type(&xp->type, attrs);
+	err = xfrm_copy_from_user_policy_type(&xp->type, attrs);
 	if (err)
 		goto error;
 
@@ -1535,10 +1517,10 @@ static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	int err;
 	int excl;
 
-	err = verify_newpolicy_info(p);
+	err = xfrm_verify_newpolicy_info(p);
 	if (err)
 		return err;
-	err = verify_sec_ctx_len(attrs);
+	err = xfrm_verify_sec_ctx_len(attrs);
 	if (err)
 		return err;
 
@@ -1570,7 +1552,7 @@ static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return 0;
 }
 
-static int copy_to_user_tmpl(const struct xfrm_policy *xp, struct sk_buff *skb)
+int xfrm_copy_to_user_tmpl(const struct xfrm_policy *xp, struct sk_buff *skb)
 {
 	struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH];
 	int i;
@@ -1599,22 +1581,6 @@ static int copy_to_user_tmpl(const struct xfrm_policy *xp, struct sk_buff *skb)
 		       sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec);
 }
 
-static inline int copy_to_user_state_sec_ctx(const struct xfrm_state *x,
-					     struct sk_buff *skb)
-{
-	if (x->security) {
-		return copy_sec_ctx(x->security, skb);
-	}
-	return 0;
-}
-
-static inline int copy_to_user_sec_ctx(const struct xfrm_policy *xp,
-				       struct sk_buff *skb)
-{
-	if (xp->security)
-		return copy_sec_ctx(xp->security, skb);
-	return 0;
-}
 static inline size_t userpolicy_type_attrsize(void)
 {
 #ifdef CONFIG_XFRM_SUB_POLICY
@@ -1624,23 +1590,6 @@ static inline size_t userpolicy_type_attrsize(void)
 #endif
 }
 
-#ifdef CONFIG_XFRM_SUB_POLICY
-static int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
-{
-	struct xfrm_userpolicy_type upt = {
-		.type = type,
-	};
-
-	return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);
-}
-
-#else
-static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
-{
-	return 0;
-}
-#endif
-
 static int dump_one_policy(const struct xfrm_policy *xp,
 			   int dir,
 			   int count,
@@ -1660,7 +1609,7 @@ static int dump_one_policy(const struct xfrm_policy *xp,
 
 	p = nlmsg_data(nlh);
 	copy_to_user_policy(xp, p, dir);
-	err = copy_to_user_tmpl(xp, skb);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
 	if (!err)
 		err = copy_to_user_sec_ctx(xp, skb);
 	if (!err)
@@ -1751,11 +1700,11 @@ static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	p = nlmsg_data(nlh);
 	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
 
-	err = copy_from_user_policy_type(&type, attrs);
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
 		return err;
 
-	err = verify_policy_dir(p->dir);
+	err = xfrm_verify_policy_dir(p->dir);
 	if (err)
 		return err;
 
@@ -1765,7 +1714,7 @@ static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
 		struct xfrm_sec_ctx *ctx;
 
-		err = verify_sec_ctx_len(attrs);
+		err = xfrm_verify_sec_ctx_len(attrs);
 		if (err)
 			return err;
 
@@ -2013,7 +1962,7 @@ static int xfrm_flush_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err;
 
-	err = copy_from_user_policy_type(&type, attrs);
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
 		return err;
 
@@ -2045,11 +1994,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	struct xfrm_mark m;
 	u32 mark = xfrm_mark_get(attrs, &m);
 
-	err = copy_from_user_policy_type(&type, attrs);
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
 		return err;
 
-	err = verify_policy_dir(p->dir);
+	err = xfrm_verify_policy_dir(p->dir);
 	if (err)
 		return err;
 
@@ -2059,7 +2008,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
 		struct xfrm_sec_ctx *ctx;
 
-		err = verify_sec_ctx_len(attrs);
+		err = xfrm_verify_sec_ctx_len(attrs);
 		if (err)
 			return err;
 
@@ -2146,7 +2095,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	xfrm_mark_get(attrs, &mark);
 
-	err = verify_newpolicy_info(&ua->policy);
+	err = xfrm_verify_newpolicy_info(&ua->policy);
 	if (err)
 		goto free_state;
 
@@ -2245,7 +2194,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	kmp = attrs[XFRMA_KMADDRESS] ? &km : NULL;
 
-	err = copy_from_user_policy_type(&type, attrs);
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
 		return err;
 
@@ -2625,7 +2574,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c)
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 }
 
-static inline size_t xfrm_sa_len(const struct xfrm_state *x)
+size_t xfrm_sa_len(const struct xfrm_state *x)
 {
 	size_t l = 0;
 	if (x->aead)
@@ -2777,7 +2726,7 @@ static int build_acquire(struct sk_buff *skb,
 	ua->calgos = xt->calgos;
 	ua->seq = x->km.seq = seq;
 
-	err = copy_to_user_tmpl(xp, skb);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
 	if (!err)
 		err = copy_to_user_state_sec_ctx(x, skb);
 	if (!err)
@@ -2846,7 +2795,7 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
 	*dir = -EINVAL;
 
 	if (len < sizeof(*p) ||
-	    verify_newpolicy_info(p))
+	    xfrm_verify_newpolicy_info(p))
 		return NULL;
 
 	nr = ((len - sizeof(*p)) / sizeof(*ut));
@@ -2896,7 +2845,7 @@ static int build_polexpire(struct sk_buff *skb,
 
 	upe = nlmsg_data(nlh);
 	copy_to_user_policy(xp, &upe->pol, dir);
-	err = copy_to_user_tmpl(xp, skb);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
 	if (!err)
 		err = copy_to_user_sec_ctx(xp, skb);
 	if (!err)
@@ -2981,7 +2930,7 @@ static int xfrm_notify_policy(const struct xfrm_policy *xp,
 	}
 
 	copy_to_user_policy(xp, p, dir);
-	err = copy_to_user_tmpl(xp, skb);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
 	if (!err)
 		err = copy_to_user_policy_type(xp->type, skb);
 	if (!err)
diff --git a/net/xfrm/xfrm_user.h b/net/xfrm/xfrm_user.h
new file mode 100644
index 000000000000..29bab2ebee83
--- /dev/null
+++ b/net/xfrm/xfrm_user.h
@@ -0,0 +1,90 @@
+#ifndef _XFRM_USER_H
+#define _XFRM_USER_H
+
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <linux/xfrm.h>
+#include <net/net_namespace.h>
+
+struct xfrm_dump_info {
+	struct sk_buff *in_skb;
+	struct sk_buff *out_skb;
+	u32 nlmsg_seq;
+	u16 nlmsg_flags;
+};
+
+/* Common functions */
+
+int xfrm_copy_sec_ctx(const struct xfrm_sec_ctx *s, struct sk_buff *skb);
+int xfrm_copy_to_user_auth(const struct xfrm_algo_auth *auth,
+			   struct sk_buff *skb);
+int xfrm_verify_newpolicy_info(const struct xfrm_userpolicy_info *p);
+struct xfrm_policy *xfrm_policy_construct(struct net *net,
+					  const struct xfrm_userpolicy_info *p,
+					  struct nlattr **attrs,
+					  int *errp);
+int xfrm_copy_from_user_policy_type(u8 *tp, struct nlattr **attrs);
+int xfrm_verify_policy_dir(u8 dir);
+int xfrm_verify_sec_ctx_len(struct nlattr **attrs);
+int xfrm_nlmsg_multicast(struct net *net, struct sk_buff *skb,
+			 u32 pid, unsigned int group);
+int xfrm_copy_to_user_tmpl(const struct xfrm_policy *xp, struct sk_buff *skb);
+size_t xfrm_sa_len(const struct xfrm_state *x);
+int xfrm_verify_newsa_info(const struct xfrm_usersa_info *p,
+			   struct nlattr **attrs);
+struct xfrm_state *xfrm_state_construct(struct net *net,
+					const struct xfrm_usersa_info *p,
+					struct nlattr **attrs,
+					int *errp);
+struct xfrm_state *xfrm_user_state_lookup(struct net *net,
+					  const struct xfrm_usersa_id *p,
+					  struct nlattr **attrs,
+					  int *errp);
+
+#ifdef CONFIG_XFRM_SUB_POLICY
+static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
+{
+	struct xfrm_userpolicy_type upt = {
+		.type = type,
+	};
+
+	return nla_put(skb, XFRMA_POLICY_TYPE, sizeof(upt), &upt);
+}
+
+#else
+static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
+{
+	return 0;
+}
+#endif
+
+static inline int copy_to_user_sec_ctx(const struct xfrm_policy *xp,
+				       struct sk_buff *skb)
+{
+	if (xp->security)
+		return xfrm_copy_sec_ctx(xp->security, skb);
+	return 0;
+}
+
+static inline int xfrm_user_sec_ctx_size(const struct xfrm_sec_ctx *xfrm_ctx)
+{
+	int len = 0;
+
+	if (xfrm_ctx) {
+		len += sizeof(struct xfrm_user_sec_ctx);
+		len += xfrm_ctx->ctx_len;
+	}
+	return len;
+}
+
+static inline int copy_to_user_state_sec_ctx(const struct xfrm_state *x,
+					     struct sk_buff *skb)
+{
+	if (x->security) {
+		return xfrm_copy_sec_ctx(x->security, skb);
+	}
+	return 0;
+}
+
+#endif /* _XFRM_USER_H */
-- 
2.11.0.483.g087da7b7c-goog

^ permalink raw reply related

* [PATCH net] net: dsa: Check return value of phy_connect_direct()
From: Florian Fainelli @ 2017-01-21  0:05 UTC (permalink / raw)
  To: netdev; +Cc: davem, vivien.didelot, andrew, Florian Fainelli

We need to check the return value of phy_connect_direct() in
dsa_slave_phy_connect() otherwise we may be continuing the
initialization of a slave network device with a PHY that already
attached somewhere else and which will soon be in error because the PHY
device is in error.

The conditions for such an error to occur are that we have a port of our
switch that is not disabled, and has the same port number as a PHY
address (say both 5) that can be probed using the DSA slave MII bus. We
end-up having this slave network device find a PHY at the same address
as our port number, and we try to attach to it.

A slave network (e.g: port 0) has already attached to our PHY device,
and we try to re-attach it with a different network device, but since we
ignore the error we would end-up initializating incorrect device
references by the time the slave network interface is opened.

The code has been (re)organized several times, making it hard to provide
an exact Fixes tag, this is a bugfix nonetheless.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 net/dsa/slave.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 68c9eea00518..ba1b6b9630d2 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1105,10 +1105,8 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
 	/* Use already configured phy mode */
 	if (p->phy_interface == PHY_INTERFACE_MODE_NA)
 		p->phy_interface = p->phy->interface;
-	phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
-			   p->phy_interface);
-
-	return 0;
+	return phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
+				  p->phy_interface);
 }
 
 static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
-- 
2.11.0

^ permalink raw reply related

* [PATCH 4/4] xfrm_user: Add new 32/64-agnostic netlink messages
From: Kevin Cernekee @ 2017-01-21  0:05 UTC (permalink / raw)
  To: steffen.klassert, herbert, davem, paul, sds, eparis
  Cc: linux-kernel, netdev, selinux, fw, fan.du, dianders, dtor
In-Reply-To: <20170121000507.34381-1-cernekee@chromium.org>

Add several new message types to address longstanding 32-bit/64-bit
compatibility issues.  Use xfrm_user_legacy to handle the existing
message types, which will retain the old IDs for compatibility with
existing binaries.

For user->kernel messages, the nlmsg_type will determine whether to use
the old format or the new format (for both requests and replies).  For
kernel->user multicasts, both types will be sent.

setsockopt() will deduce the format from the length.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
---
 include/uapi/linux/xfrm.h   | 152 ++++++++++++++++++++++++++++++---------
 net/xfrm/xfrm_user.c        | 136 ++++++++++++++++++++++++++++++++---
 net/xfrm/xfrm_user.h        |  75 ++++++++++++++++++++
 net/xfrm/xfrm_user_legacy.c | 169 ++++++++++++++++++++++++++++----------------
 security/selinux/nlmsgtab.c |  61 +++++++++-------
 5 files changed, 466 insertions(+), 127 deletions(-)

diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index 1fc62b239f1b..ae5f97681989 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -1,6 +1,7 @@
 #ifndef _LINUX_XFRM_H
 #define _LINUX_XFRM_H
 
+#include <linux/compiler.h>
 #include <linux/in6.h>
 #include <linux/types.h>
 
@@ -157,34 +158,34 @@ enum {
 enum {
 	XFRM_MSG_BASE = 0x10,
 
-	XFRM_MSG_NEWSA = 0x10,
-#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA
-	XFRM_MSG_DELSA,
-#define XFRM_MSG_DELSA XFRM_MSG_DELSA
-	XFRM_MSG_GETSA,
-#define XFRM_MSG_GETSA XFRM_MSG_GETSA
-
-	XFRM_MSG_NEWPOLICY,
-#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY
-	XFRM_MSG_DELPOLICY,
-#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY
-	XFRM_MSG_GETPOLICY,
-#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY
-
-	XFRM_MSG_ALLOCSPI,
-#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI
-	XFRM_MSG_ACQUIRE,
-#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE
-	XFRM_MSG_EXPIRE,
-#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE
-
-	XFRM_MSG_UPDPOLICY,
-#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY
-	XFRM_MSG_UPDSA,
-#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA
-
-	XFRM_MSG_POLEXPIRE,
-#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE
+	XFRM_MSG_NEWSA_LEGACY = 0x10,
+#define XFRM_MSG_NEWSA_LEGACY XFRM_MSG_NEWSA_LEGACY
+	XFRM_MSG_DELSA_LEGACY,
+#define XFRM_MSG_DELSA_LEGACY XFRM_MSG_DELSA_LEGACY
+	XFRM_MSG_GETSA_LEGACY,
+#define XFRM_MSG_GETSA_LEGACY XFRM_MSG_GETSA_LEGACY
+
+	XFRM_MSG_NEWPOLICY_LEGACY,
+#define XFRM_MSG_NEWPOLICY_LEGACY XFRM_MSG_NEWPOLICY_LEGACY
+	XFRM_MSG_DELPOLICY_LEGACY,
+#define XFRM_MSG_DELPOLICY_LEGACY XFRM_MSG_DELPOLICY_LEGACY
+	XFRM_MSG_GETPOLICY_LEGACY,
+#define XFRM_MSG_GETPOLICY_LEGACY XFRM_MSG_GETPOLICY_LEGACY
+
+	XFRM_MSG_ALLOCSPI_LEGACY,
+#define XFRM_MSG_ALLOCSPI_LEGACY XFRM_MSG_ALLOCSPI_LEGACY
+	XFRM_MSG_ACQUIRE_LEGACY,
+#define XFRM_MSG_ACQUIRE_LEGACY XFRM_MSG_ACQUIRE_LEGACY
+	XFRM_MSG_EXPIRE_LEGACY,
+#define XFRM_MSG_EXPIRE_LEGACY XFRM_MSG_EXPIRE_LEGACY
+
+	XFRM_MSG_UPDPOLICY_LEGACY,
+#define XFRM_MSG_UPDPOLICY_LEGACY XFRM_MSG_UPDPOLICY_LEGACY
+	XFRM_MSG_UPDSA_LEGACY,
+#define XFRM_MSG_UPDSA_LEGACY XFRM_MSG_UPDSA_LEGACY
+
+	XFRM_MSG_POLEXPIRE_LEGACY,
+#define XFRM_MSG_POLEXPIRE_LEGACY XFRM_MSG_POLEXPIRE_LEGACY
 
 	XFRM_MSG_FLUSHSA,
 #define XFRM_MSG_FLUSHSA XFRM_MSG_FLUSHSA
@@ -214,6 +215,34 @@ enum {
 
 	XFRM_MSG_MAPPING,
 #define XFRM_MSG_MAPPING XFRM_MSG_MAPPING
+
+	XFRM_MSG_ALLOCSPI,
+#define XFRM_MSG_ALLOCSPI XFRM_MSG_ALLOCSPI
+	XFRM_MSG_ACQUIRE,
+#define XFRM_MSG_ACQUIRE XFRM_MSG_ACQUIRE
+	XFRM_MSG_EXPIRE,
+#define XFRM_MSG_EXPIRE XFRM_MSG_EXPIRE
+	XFRM_MSG_POLEXPIRE,
+#define XFRM_MSG_POLEXPIRE XFRM_MSG_POLEXPIRE
+
+	XFRM_MSG_NEWSA,
+#define XFRM_MSG_NEWSA XFRM_MSG_NEWSA
+	XFRM_MSG_UPDSA,
+#define XFRM_MSG_UPDSA XFRM_MSG_UPDSA
+	XFRM_MSG_DELSA,
+#define XFRM_MSG_DELSA XFRM_MSG_DELSA
+	XFRM_MSG_GETSA,
+#define XFRM_MSG_GETSA XFRM_MSG_GETSA
+
+	XFRM_MSG_NEWPOLICY,
+#define XFRM_MSG_NEWPOLICY XFRM_MSG_NEWPOLICY
+	XFRM_MSG_UPDPOLICY,
+#define XFRM_MSG_UPDPOLICY XFRM_MSG_UPDPOLICY
+	XFRM_MSG_DELPOLICY,
+#define XFRM_MSG_DELPOLICY XFRM_MSG_DELPOLICY
+	XFRM_MSG_GETPOLICY,
+#define XFRM_MSG_GETPOLICY XFRM_MSG_GETPOLICY
+
 	__XFRM_MSG_MAX
 };
 #define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
@@ -221,7 +250,7 @@ enum {
 #define XFRM_NR_MSGTYPES (XFRM_MSG_MAX + 1 - XFRM_MSG_BASE)
 
 /*
- * Generic LSM security context for comunicating to user space
+ * Generic LSM security context for communicating to user space
  * NOTE: Same format as sadb_x_sec_ctx
  */
 struct xfrm_user_sec_ctx {
@@ -357,6 +386,22 @@ struct xfrmu_spdhthresh {
 	__u8 rbits;
 };
 
+/* Legacy structs are incompatible between 32-bit and 64-bit. */
+struct xfrm_usersa_info_legacy {
+	struct xfrm_selector		sel;
+	struct xfrm_id			id;
+	xfrm_address_t			saddr;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	struct xfrm_stats		stats;
+	__u32				seq;
+	__u32				reqid;
+	__u16				family;
+	__u8				mode;		/* XFRM_MODE_xxx */
+	__u8				replay_window;
+	__u8				flags;
+};
+
 struct xfrm_usersa_info {
 	struct xfrm_selector		sel;
 	struct xfrm_id			id;
@@ -378,7 +423,8 @@ struct xfrm_usersa_info {
 #define XFRM_STATE_AF_UNSPEC	32
 #define XFRM_STATE_ALIGN4	64
 #define XFRM_STATE_ESN		128
-};
+	__u8				reserved[7];
+} __packed;
 
 #define XFRM_SA_XFLAG_DONT_ENCAP_DSCP	1
 
@@ -396,10 +442,28 @@ struct xfrm_aevent_id {
 	__u32				reqid;
 };
 
+struct xfrm_userspi_info_legacy {
+	struct xfrm_usersa_info_legacy	info;
+	__u32				min;
+	__u32				max;
+};
+
 struct xfrm_userspi_info {
 	struct xfrm_usersa_info		info;
 	__u32				min;
 	__u32				max;
+} __packed;
+
+struct xfrm_userpolicy_info_legacy {
+	struct xfrm_selector		sel;
+	struct xfrm_lifetime_cfg	lft;
+	struct xfrm_lifetime_cur	curlft;
+	__u32				priority;
+	__u32				index;
+	__u8				dir;
+	__u8				action;
+	__u8				flags;
+	__u8				share;
 };
 
 struct xfrm_userpolicy_info {
@@ -417,7 +481,8 @@ struct xfrm_userpolicy_info {
 	/* Automatically expand selector to include matching ICMP payloads. */
 #define XFRM_POLICY_ICMP	2
 	__u8				share;
-};
+	__u8				reserved[4];
+} __packed;
 
 struct xfrm_userpolicy_id {
 	struct xfrm_selector		sel;
@@ -425,6 +490,17 @@ struct xfrm_userpolicy_id {
 	__u8				dir;
 };
 
+struct xfrm_user_acquire_legacy {
+	struct xfrm_id				id;
+	xfrm_address_t				saddr;
+	struct xfrm_selector			sel;
+	struct xfrm_userpolicy_info_legacy	policy;
+	__u32					aalgos;
+	__u32					ealgos;
+	__u32					calgos;
+	__u32					seq;
+};
+
 struct xfrm_user_acquire {
 	struct xfrm_id			id;
 	xfrm_address_t			saddr;
@@ -434,17 +510,29 @@ struct xfrm_user_acquire {
 	__u32				ealgos;
 	__u32				calgos;
 	__u32				seq;
+} __packed;
+
+struct xfrm_user_expire_legacy {
+	struct xfrm_usersa_info_legacy	state;
+	__u8				hard;
 };
 
 struct xfrm_user_expire {
 	struct xfrm_usersa_info		state;
 	__u8				hard;
+	__u8				reserved[7];
+} __packed;
+
+struct xfrm_user_polexpire_legacy {
+	struct xfrm_userpolicy_info_legacy	pol;
+	__u8					hard;
 };
 
 struct xfrm_user_polexpire {
 	struct xfrm_userpolicy_info	pol;
 	__u8				hard;
-};
+	__u8				reserved[7];
+} __packed;
 
 struct xfrm_usersa_flush {
 	__u8				proto;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 4d733f02c3a1..5456dde974bc 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2350,6 +2350,32 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
 	[XFRM_MSG_GETSADINFO  - XFRM_MSG_BASE] = sizeof(u32),
 	[XFRM_MSG_NEWSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = sizeof(u32),
+
+	[XFRM_MSG_ALLOCSPI_LEGACY  - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userspi_info_legacy),
+	[XFRM_MSG_ACQUIRE_LEGACY   - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_acquire_legacy),
+	[XFRM_MSG_EXPIRE_LEGACY    - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_expire_legacy),
+	[XFRM_MSG_POLEXPIRE_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_user_polexpire_legacy),
+
+	[XFRM_MSG_NEWSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_info_legacy),
+	[XFRM_MSG_UPDSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_info_legacy),
+	[XFRM_MSG_DELSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_id),
+	[XFRM_MSG_GETSA_LEGACY     - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_usersa_id),
+	[XFRM_MSG_NEWPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_info_legacy),
+	[XFRM_MSG_UPDPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_info_legacy),
+	[XFRM_MSG_DELPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_id),
+	[XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE] =
+		XMSGSIZE(xfrm_userpolicy_id),
 };
 
 #undef XMSGSIZE
@@ -2396,6 +2422,7 @@ static const struct xfrm_link {
 	int (*done)(struct netlink_callback *);
 	const struct nla_policy *nla_pol;
 	int nla_max;
+	bool legacy;
 } xfrm_dispatch[XFRM_NR_MSGTYPES] = {
 	[XFRM_MSG_NEWSA       - XFRM_MSG_BASE] = { .doit = xfrm_add_sa        },
 	[XFRM_MSG_DELSA       - XFRM_MSG_BASE] = { .doit = xfrm_del_sa        },
@@ -2423,6 +2450,62 @@ static const struct xfrm_link {
 						   .nla_pol = xfrma_spd_policy,
 						   .nla_max = XFRMA_SPD_MAX },
 	[XFRM_MSG_GETSPDINFO  - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo   },
+
+#ifdef CONFIG_XFRM_USER_LEGACY
+	[XFRM_MSG_ALLOCSPI_LEGACY  - XFRM_MSG_BASE] = {
+		.doit = xfrm_alloc_userspi_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_ACQUIRE_LEGACY   - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_acquire_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_EXPIRE_LEGACY    - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_expire_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_POLEXPIRE_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_pol_expire_legacy,
+		.legacy = true,
+	},
+
+	[XFRM_MSG_NEWSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_UPDSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_DELSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_del_sa_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_GETSA_LEGACY     - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_sa_legacy,
+		.dump = xfrm_dump_sa_legacy,
+		.done = xfrm_dump_sa_done_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_NEWPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_UPDPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_add_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_DELPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_policy_legacy,
+		.legacy = true,
+	},
+	[XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE] = {
+		.doit = xfrm_get_policy_legacy,
+		.dump = xfrm_dump_policy_legacy,
+		.done = xfrm_dump_policy_done_legacy,
+		.legacy = true,
+	},
+#endif /* CONFIG_XFRM_USER_LEGACY */
 };
 
 static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -2432,11 +2515,6 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 	const struct xfrm_link *link;
 	int type, err;
 
-#ifdef CONFIG_COMPAT
-	if (in_compat_syscall())
-		return -EOPNOTSUPP;
-#endif
-
 	type = nlh->nlmsg_type;
 	if (type > XFRM_MSG_MAX)
 		return -EINVAL;
@@ -2444,12 +2522,19 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 	type -= XFRM_MSG_BASE;
 	link = &xfrm_dispatch[type];
 
+#ifdef CONFIG_COMPAT
+	if (link->legacy && in_compat_syscall())
+		return -EOPNOTSUPP;
+#endif
+
 	/* All operations require privileges, even GET */
 	if (!netlink_net_capable(skb, CAP_NET_ADMIN))
 		return -EPERM;
 
 	if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
-	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) &&
+	     type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE) ||
+	     type == (XFRM_MSG_GETSA_LEGACY - XFRM_MSG_BASE) ||
+	     type == (XFRM_MSG_GETPOLICY_LEGACY - XFRM_MSG_BASE)) &&
 	    (nlh->nlmsg_flags & NLM_F_DUMP)) {
 		if (link->dump == NULL)
 			return -EINVAL;
@@ -2670,16 +2755,23 @@ static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 static int xfrm_send_state_notify(const struct xfrm_state *x,
 				  const struct km_event *c)
 {
+	int err;
 
 	switch (c->event) {
 	case XFRM_MSG_EXPIRE:
-		return xfrm_exp_state_notify(x, c);
+		err = xfrm_exp_state_notify(x, c);
+		if (err)
+			return err;
+		return xfrm_exp_state_notify_legacy(x, c);
 	case XFRM_MSG_NEWAE:
 		return xfrm_aevent_state_notify(x, c);
 	case XFRM_MSG_DELSA:
 	case XFRM_MSG_UPDSA:
 	case XFRM_MSG_NEWSA:
-		return xfrm_notify_sa(x, c);
+		err = xfrm_notify_sa(x, c);
+		if (err)
+			return err;
+		return xfrm_notify_sa_legacy(x, c);
 	case XFRM_MSG_FLUSHSA:
 		return xfrm_notify_sa_flush(c);
 	default:
@@ -2748,6 +2840,7 @@ static int xfrm_send_acquire(struct xfrm_state *x,
 {
 	struct net *net = xs_net(x);
 	struct sk_buff *skb;
+	int err;
 
 	skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
 	if (skb == NULL)
@@ -2756,7 +2849,11 @@ static int xfrm_send_acquire(struct xfrm_state *x,
 	if (build_acquire(skb, x, xt, xp) < 0)
 		BUG();
 
-	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+	err = xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+	if (err)
+		return err;
+
+	return xfrm_send_acquire_legacy(x, xt, xp);
 }
 
 /* User gives us xfrm_user_policy_info followed by an array of 0
@@ -2799,6 +2896,16 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
 		return NULL;
 
 	nr = ((len - sizeof(*p)) / sizeof(*ut));
+	if (len == (nr + 1) * sizeof(*ut) + sizeof(*p) - sizeof(u32)) {
+		/* The user passed a legacy xfrm_userpolicy_info struct whose
+		 * length is padded to 32 bits instead of 64, so the above
+		 * division had a remainder.  Adjust the start address and
+		 * count accordingly.
+		 */
+		ut = (void *)data + sizeof(*p) - sizeof(u32);
+		nr++;
+	}
+
 	if (validate_tmpl(nr, ut, p->sel.family))
 		return NULL;
 
@@ -2979,16 +3086,23 @@ static int xfrm_send_policy_notify(const struct xfrm_policy *xp,
 				   int dir,
 				   const struct km_event *c)
 {
+	int err;
 
 	switch (c->event) {
 	case XFRM_MSG_NEWPOLICY:
 	case XFRM_MSG_UPDPOLICY:
 	case XFRM_MSG_DELPOLICY:
-		return xfrm_notify_policy(xp, dir, c);
+		err = xfrm_notify_policy(xp, dir, c);
+		if (err)
+			return err;
+		return xfrm_notify_policy_legacy(xp, dir, c);
 	case XFRM_MSG_FLUSHPOLICY:
 		return xfrm_notify_policy_flush(c);
 	case XFRM_MSG_POLEXPIRE:
-		return xfrm_exp_policy_notify(xp, dir, c);
+		err = xfrm_exp_policy_notify(xp, dir, c);
+		if (err)
+			return err;
+		return xfrm_exp_policy_notify_legacy(xp, dir, c);
 	default:
 		printk(KERN_NOTICE "xfrm_user: Unknown Policy event %d\n",
 		       c->event);
diff --git a/net/xfrm/xfrm_user.h b/net/xfrm/xfrm_user.h
index 29bab2ebee83..78627d1c1cec 100644
--- a/net/xfrm/xfrm_user.h
+++ b/net/xfrm/xfrm_user.h
@@ -1,6 +1,7 @@
 #ifndef _XFRM_USER_H
 #define _XFRM_USER_H
 
+#include <linux/errno.h>
 #include <linux/netlink.h>
 #include <linux/skbuff.h>
 #include <linux/types.h>
@@ -87,4 +88,78 @@ static inline int copy_to_user_state_sec_ctx(const struct xfrm_state *x,
 	return 0;
 }
 
+/* Legacy functions */
+
+#ifdef CONFIG_XFRM_USER_LEGACY
+int xfrm_alloc_userspi_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs);
+int xfrm_add_pol_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			       struct nlattr **attrs);
+int xfrm_add_sa_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs);
+int xfrm_add_acquire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs);
+
+int xfrm_add_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_del_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_dump_sa_done_legacy(struct netlink_callback *cb);
+int xfrm_dump_sa_legacy(struct sk_buff *skb, struct netlink_callback *cb);
+int xfrm_get_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs);
+int xfrm_add_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs);
+int xfrm_dump_policy_done_legacy(struct netlink_callback *cb);
+int xfrm_dump_policy_legacy(struct sk_buff *skb, struct netlink_callback *cb);
+int xfrm_get_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs);
+
+int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
+				 const struct km_event *c);
+int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c);
+int xfrm_send_acquire_legacy(struct xfrm_state *x,
+			     const struct xfrm_tmpl *xt,
+			     const struct xfrm_policy *xp);
+int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
+				  int dir,
+				  const struct km_event *c);
+int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
+			      int dir,
+			      const struct km_event *c);
+#else /* CONFIG_XFRM_USER_LEGACY */
+static inline int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
+					       const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_notify_sa_legacy(const struct xfrm_state *x,
+					const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_send_acquire_legacy(struct xfrm_state *x,
+					   const struct xfrm_tmpl *xt,
+					   const struct xfrm_policy *xp)
+{
+	return 0;
+}
+
+static inline int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
+						int dir,
+						const struct km_event *c)
+{
+	return 0;
+}
+
+static inline int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
+					    int dir,
+					    const struct km_event *c)
+{
+	return 0;
+}
+#endif /* CONFIG_XFRM_USER_LEGACY */
+
 #endif /* _XFRM_USER_H */
diff --git a/net/xfrm/xfrm_user_legacy.c b/net/xfrm/xfrm_user_legacy.c
index 058accfefc83..aa48845b47fa 100644
--- a/net/xfrm/xfrm_user_legacy.c
+++ b/net/xfrm/xfrm_user_legacy.c
@@ -29,28 +29,33 @@
 #include <asm/unaligned.h>
 #include "xfrm_user.h"
 
-static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
-	const struct xfrm_usersa_info *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_info_legacy *p = nlmsg_data(nlh);
 	struct xfrm_state *x;
 	int err;
 	struct km_event c;
 
-	err = xfrm_verify_newsa_info(p, attrs);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newsa_info((const struct xfrm_usersa_info *)p, attrs);
 	if (err)
 		return err;
 
-	x = xfrm_state_construct(net, p, attrs, &err);
+	x = xfrm_state_construct(net, (const struct xfrm_usersa_info *)p,
+				 attrs, &err);
 	if (!x)
 		return err;
 
 	xfrm_state_hold(x);
-	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
+	if (nlh->nlmsg_type == XFRM_MSG_NEWSA_LEGACY) {
 		err = xfrm_state_add(x);
-	else
+		c.event = XFRM_MSG_NEWSA;
+	} else {
 		err = xfrm_state_update(x);
+		c.event = XFRM_MSG_UPDSA;
+	}
 
 	xfrm_audit_state_add(x, err ? 0 : 1, true);
 
@@ -62,7 +67,6 @@ static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
-	c.event = nlh->nlmsg_type;
 
 	km_state_notify(x, &c);
 out:
@@ -70,7 +74,7 @@ static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_del_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -98,7 +102,7 @@ static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
-	c.event = nlh->nlmsg_type;
+	c.event = XFRM_MSG_DELSA;
 	km_state_notify(x, &c);
 
 out:
@@ -108,7 +112,7 @@ static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 }
 
 static void copy_to_user_state(const struct xfrm_state *x,
-			       struct xfrm_usersa_info *p)
+			       struct xfrm_usersa_info_legacy *p)
 {
 	memset(p, 0, sizeof(*p));
 	memcpy(&p->id, &x->id, sizeof(p->id));
@@ -128,7 +132,7 @@ static void copy_to_user_state(const struct xfrm_state *x,
 }
 
 static int copy_to_user_state_extra(const struct xfrm_state *x,
-				    struct xfrm_usersa_info *p,
+				    struct xfrm_usersa_info_legacy *p,
 				    struct sk_buff *skb)
 {
 	int ret = 0;
@@ -209,12 +213,12 @@ static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
 	struct xfrm_dump_info *sp = ptr;
 	struct sk_buff *in_skb = sp->in_skb;
 	struct sk_buff *skb = sp->out_skb;
-	struct xfrm_usersa_info *p;
+	struct xfrm_usersa_info_legacy *p;
 	struct nlmsghdr *nlh;
 	int err;
 
 	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
-			XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);
+			XFRM_MSG_NEWSA_LEGACY, sizeof(*p), sp->nlmsg_flags);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -229,7 +233,7 @@ static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
 	return 0;
 }
 
-static int xfrm_dump_sa_done(struct netlink_callback *cb)
+int xfrm_dump_sa_done_legacy(struct netlink_callback *cb)
 {
 	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
 	struct sock *sk = cb->skb->sk;
@@ -241,7 +245,7 @@ static int xfrm_dump_sa_done(struct netlink_callback *cb)
 }
 
 static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
-static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
+int xfrm_dump_sa_legacy(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
@@ -311,7 +315,7 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
 	return skb;
 }
 
-static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_get_sa_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -335,12 +339,12 @@ static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_alloc_userspi_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			      struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
-	const struct xfrm_userspi_info *p;
+	const struct xfrm_userspi_info_legacy *p;
 	struct sk_buff *resp_skb;
 	const xfrm_address_t *daddr;
 	int family;
@@ -395,7 +399,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
 }
 
 static void copy_to_user_policy(const struct xfrm_policy *xp,
-				struct xfrm_userpolicy_info *p,
+				struct xfrm_userpolicy_info_legacy *p,
 				int dir)
 {
 	memset(p, 0, sizeof(*p));
@@ -411,24 +415,27 @@ static void copy_to_user_policy(const struct xfrm_policy *xp,
 	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
 }
 
-static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			   struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
-	const struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info_legacy *p = nlmsg_data(nlh);
 	struct xfrm_policy *xp;
 	struct km_event c;
 	int err;
 	int excl;
 
-	err = xfrm_verify_newpolicy_info(p);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newpolicy_info(
+		(const struct xfrm_userpolicy_info *)p);
 	if (err)
 		return err;
 	err = xfrm_verify_sec_ctx_len(attrs);
 	if (err)
 		return err;
 
-	xp = xfrm_policy_construct(net, p, attrs, &err);
+	xp = xfrm_policy_construct(net, (const struct xfrm_userpolicy_info *)p,
+				   attrs, &err);
 	if (!xp)
 		return err;
 
@@ -436,7 +443,13 @@ static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	 * Aha! this is anti-netlink really i.e  more pfkey derived
 	 * in netlink excl is a flag and you wouldnt need
 	 * a type XFRM_MSG_UPDPOLICY - JHS */
-	excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
+	if (nlh->nlmsg_type == XFRM_MSG_NEWPOLICY_LEGACY) {
+		excl = 1;
+		c.event = XFRM_MSG_NEWPOLICY;
+	} else {
+		excl = 0;
+		c.event = XFRM_MSG_UPDPOLICY;
+	}
 	err = xfrm_policy_insert(p->dir, xp, excl);
 	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
 
@@ -446,7 +459,6 @@ static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 		return err;
 	}
 
-	c.event = nlh->nlmsg_type;
 	c.seq = nlh->nlmsg_seq;
 	c.portid = nlh->nlmsg_pid;
 	km_policy_notify(xp, p->dir, &c);
@@ -471,14 +483,14 @@ static int dump_one_policy(const struct xfrm_policy *xp,
 			   void *ptr)
 {
 	struct xfrm_dump_info *sp = ptr;
-	struct xfrm_userpolicy_info *p;
+	struct xfrm_userpolicy_info_legacy *p;
 	struct sk_buff *in_skb = sp->in_skb;
 	struct sk_buff *skb = sp->out_skb;
 	struct nlmsghdr *nlh;
 	int err;
 
 	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
-			XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
+			XFRM_MSG_NEWPOLICY_LEGACY, sizeof(*p), sp->nlmsg_flags);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -499,7 +511,7 @@ static int dump_one_policy(const struct xfrm_policy *xp,
 	return 0;
 }
 
-static int xfrm_dump_policy_done(struct netlink_callback *cb)
+int xfrm_dump_policy_done_legacy(struct netlink_callback *cb)
 {
 	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
 	struct net *net = sock_net(cb->skb->sk);
@@ -508,7 +520,7 @@ static int xfrm_dump_policy_done(struct netlink_callback *cb)
 	return 0;
 }
 
-static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
+int xfrm_dump_policy_legacy(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
@@ -559,7 +571,7 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
 	return skb;
 }
 
-static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_get_policy_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			   struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -573,7 +585,13 @@ static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	u32 mark = xfrm_mark_get(attrs, &m);
 
 	p = nlmsg_data(nlh);
-	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
+	if (nlh->nlmsg_type == XFRM_MSG_DELPOLICY_LEGACY) {
+		delete = 1;
+		c.event = XFRM_MSG_DELPOLICY;
+	} else {
+		delete = 0;
+		c.event = XFRM_MSG_GETPOLICY;
+	}
 
 	err = xfrm_copy_from_user_policy_type(&type, attrs);
 	if (err)
@@ -625,7 +643,6 @@ static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			goto out;
 
 		c.data.byid = p->index;
-		c.event = nlh->nlmsg_type;
 		c.seq = nlh->nlmsg_seq;
 		c.portid = nlh->nlmsg_pid;
 		km_policy_notify(xp, p->dir, &c);
@@ -638,13 +655,13 @@ static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_pol_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_policy *xp;
-	const struct xfrm_user_polexpire *up = nlmsg_data(nlh);
-	const struct xfrm_userpolicy_info *p = &up->pol;
+	const struct xfrm_user_polexpire_legacy *up = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info_legacy *p = &up->pol;
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err = -ENOENT;
 	struct xfrm_mark m;
@@ -698,14 +715,14 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_sa_expire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			      struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
 	int err;
-	const struct xfrm_user_expire *ue = nlmsg_data(nlh);
-	const struct xfrm_usersa_info *p = &ue->state;
+	const struct xfrm_user_expire_legacy *ue = nlmsg_data(nlh);
+	const struct xfrm_usersa_info_legacy *p = &ue->state;
 	struct xfrm_mark m;
 	u32 mark = xfrm_mark_get(attrs, &m);
 
@@ -732,7 +749,7 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+int xfrm_add_acquire_legacy(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -742,7 +759,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 	struct nlattr *rt = attrs[XFRMA_TMPL];
 	struct xfrm_mark mark;
 
-	const struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+	const struct xfrm_user_acquire_legacy *ua = nlmsg_data(nlh);
 	struct xfrm_state *x = xfrm_state_alloc(net);
 	int err = -ENOMEM;
 
@@ -751,12 +768,15 @@ static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 	xfrm_mark_get(attrs, &mark);
 
-	err = xfrm_verify_newpolicy_info(&ua->policy);
+	/* This cast is safe because the only difference is end padding. */
+	err = xfrm_verify_newpolicy_info(
+		(const struct xfrm_userpolicy_info *)&ua->policy);
 	if (err)
 		goto free_state;
 
 	/*   build an XP */
-	xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+	xp = xfrm_policy_construct(net, (const struct xfrm_userpolicy_info *)
+				   &ua->policy, attrs, &err);
 	if (!xp)
 		goto free_state;
 
@@ -793,7 +813,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
 
 static inline size_t xfrm_expire_msgsize(void)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire_legacy))
 	       + nla_total_size(sizeof(struct xfrm_mark));
 }
 
@@ -801,11 +821,12 @@ static int build_expire(struct sk_buff *skb,
 			const struct xfrm_state *x,
 			const struct km_event *c)
 {
-	struct xfrm_user_expire *ue;
+	struct xfrm_user_expire_legacy *ue;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE_LEGACY,
+			sizeof(*ue), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -821,7 +842,7 @@ static int build_expire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_exp_state_notify(const struct xfrm_state *x,
+int xfrm_exp_state_notify_legacy(const struct xfrm_state *x,
 				 const struct km_event *c)
 {
 	struct net *net = xs_net(x);
@@ -839,15 +860,16 @@ static int xfrm_exp_state_notify(const struct xfrm_state *x,
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
+int xfrm_notify_sa_legacy(const struct xfrm_state *x, const struct km_event *c)
 {
 	struct net *net = xs_net(x);
-	struct xfrm_usersa_info *p;
+	struct xfrm_usersa_info_legacy *p;
 	struct xfrm_usersa_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
 	int len = xfrm_sa_len(x);
 	int headlen, err;
+	u32 event = 0;
 
 	headlen = sizeof(*p);
 	if (c->event == XFRM_MSG_DELSA) {
@@ -861,7 +883,19 @@ static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 	if (skb == NULL)
 		return -ENOMEM;
 
-	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	switch (c->event) {
+	case XFRM_MSG_NEWSA:
+		event = XFRM_MSG_NEWSA_LEGACY;
+		break;
+	case XFRM_MSG_UPDSA:
+		event = XFRM_MSG_UPDSA_LEGACY;
+		break;
+	case XFRM_MSG_DELSA:
+		event = XFRM_MSG_DELSA_LEGACY;
+		break;
+	}
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, event, headlen, 0);
 	err = -EMSGSIZE;
 	if (nlh == NULL)
 		goto out_free_skb;
@@ -899,7 +933,7 @@ static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 static inline size_t xfrm_acquire_msgsize(const struct xfrm_state *x,
 					  const struct xfrm_policy *xp)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire_legacy))
 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
 	       + nla_total_size(sizeof(struct xfrm_mark))
 	       + nla_total_size(xfrm_user_sec_ctx_size(x->security))
@@ -912,11 +946,11 @@ static int build_acquire(struct sk_buff *skb,
 			 const struct xfrm_policy *xp)
 {
 	__u32 seq = xfrm_get_acqseq();
-	struct xfrm_user_acquire *ua;
+	struct xfrm_user_acquire_legacy *ua;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
+	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE_LEGACY, sizeof(*ua), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -946,7 +980,7 @@ static int build_acquire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_send_acquire(struct xfrm_state *x,
+int xfrm_send_acquire_legacy(struct xfrm_state *x,
 			     const struct xfrm_tmpl *xt,
 			     const struct xfrm_policy *xp)
 {
@@ -965,7 +999,7 @@ static int xfrm_send_acquire(struct xfrm_state *x,
 
 static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
 {
-	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire_legacy))
 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
 	       + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
 	       + nla_total_size(sizeof(struct xfrm_mark))
@@ -977,12 +1011,13 @@ static int build_polexpire(struct sk_buff *skb,
 			   int dir,
 			   const struct km_event *c)
 {
-	struct xfrm_user_polexpire *upe;
+	struct xfrm_user_polexpire_legacy *upe;
 	int hard = c->data.hard;
 	struct nlmsghdr *nlh;
 	int err;
 
-	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE_LEGACY,
+			sizeof(*upe), 0);
 	if (nlh == NULL)
 		return -EMSGSIZE;
 
@@ -1005,7 +1040,7 @@ static int build_polexpire(struct sk_buff *skb,
 	return 0;
 }
 
-static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
+int xfrm_exp_policy_notify_legacy(const struct xfrm_policy *xp,
 				  int dir,
 				  const struct km_event *c)
 {
@@ -1022,19 +1057,21 @@ static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_notify_policy(const struct xfrm_policy *xp,
+int xfrm_notify_policy_legacy(const struct xfrm_policy *xp,
 			      int dir,
 			      const struct km_event *c)
 {
 	int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
 	struct net *net = xp_net(xp);
-	struct xfrm_userpolicy_info *p;
+	struct xfrm_userpolicy_info_legacy *p;
 	struct xfrm_userpolicy_id *id;
 	struct nlmsghdr *nlh;
 	struct sk_buff *skb;
 	int headlen, err;
+	u32 event = 0;
 
 	headlen = sizeof(*p);
+
 	if (c->event == XFRM_MSG_DELPOLICY) {
 		len += nla_total_size(headlen);
 		headlen = sizeof(*id);
@@ -1047,7 +1084,19 @@ static int xfrm_notify_policy(const struct xfrm_policy *xp,
 	if (skb == NULL)
 		return -ENOMEM;
 
-	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	switch (c->event) {
+	case XFRM_MSG_NEWPOLICY:
+		event = XFRM_MSG_NEWPOLICY_LEGACY;
+		break;
+	case XFRM_MSG_UPDPOLICY:
+		event = XFRM_MSG_UPDPOLICY_LEGACY;
+		break;
+	case XFRM_MSG_DELPOLICY:
+		event = XFRM_MSG_DELPOLICY_LEGACY;
+		break;
+	}
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, event, headlen, 0);
 	err = -EMSGSIZE;
 	if (nlh == NULL)
 		goto out_free_skb;
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 2ca9cde939d4..15e8b1381c13 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -90,29 +90,42 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
 
 static struct nlmsg_perm nlmsg_xfrm_perms[] =
 {
-	{ XFRM_MSG_NEWSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_DELSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETSA,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_NEWPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_DELPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_ALLOCSPI,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_ACQUIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_EXPIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_UPDPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_UPDSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_POLEXPIRE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_FLUSHSA,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_FLUSHPOLICY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_NEWAE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETAE,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_REPORT,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_MIGRATE,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_NEWSADINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_GETSADINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_NEWSPDINFO,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
-	{ XFRM_MSG_GETSPDINFO,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
-	{ XFRM_MSG_MAPPING,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_ALLOCSPI_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_ACQUIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_EXPIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDPOLICY_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDSA_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_POLEXPIRE_LEGACY,	NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_FLUSHSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_FLUSHPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWAE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETAE,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_REPORT,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_MIGRATE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSADINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_GETSADINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWSPDINFO,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSPDINFO,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_MAPPING,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_ALLOCSPI,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_ACQUIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_EXPIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_POLEXPIRE,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_NEWSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELSA,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETSA,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
+	{ XFRM_MSG_NEWPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_UPDPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_DELPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+	{ XFRM_MSG_GETPOLICY,		NETLINK_XFRM_SOCKET__NLMSG_READ  },
 };
 
 static struct nlmsg_perm nlmsg_audit_perms[] =
@@ -168,7 +181,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
 		break;
 
 	case SECCLASS_NETLINK_XFRM_SOCKET:
-		BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_MAPPING);
+		BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETPOLICY);
 		err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
 				 sizeof(nlmsg_xfrm_perms));
 		break;
-- 
2.11.0.483.g087da7b7c-goog

^ permalink raw reply related

* [PATCH 3/4] xfrm_user: Initial commit of xfrm_user_legacy.c
From: Kevin Cernekee @ 2017-01-21  0:05 UTC (permalink / raw)
  To: steffen.klassert, herbert, davem, paul, sds, eparis
  Cc: linux-kernel, netdev, selinux, fw, fan.du, dianders, dtor
In-Reply-To: <20170121000507.34381-1-cernekee@chromium.org>

Several xfrm_* structs are incompatible between 32bit and 64bit builds:

    xfrm_usersa_info 220 bytes on i386 -> 224 bytes on amd64
    xfrm_userpolicy_info 164 -> 168
    xfrm_userspi_info 228 -> 232, offset mismatch on min
    xfrm_user_acquire 276 -> 280, offset mismatch on aalgos
    xfrm_user_expire 224 -> 232, offset mismatch on hard
    xfrm_user_polexpire 168 -> 176, offset mismatch on hard

Fork all of the functions that handle these structs into a new file so
that it is possible to support both legacy + new layouts.

This commit contains an exact copy of the necessary functions from
xfrm_user.c, for ease of reviewing.  The next commit will contain all
of the changes needed to make these functions handle legacy messages
correctly.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
---
 net/xfrm/Kconfig            |   14 +
 net/xfrm/Makefile           |    8 +-
 net/xfrm/xfrm_user_legacy.c | 1091 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1112 insertions(+), 1 deletion(-)
 create mode 100644 net/xfrm/xfrm_user_legacy.c

diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
index bda1a13628a8..317dcc411345 100644
--- a/net/xfrm/Kconfig
+++ b/net/xfrm/Kconfig
@@ -20,6 +20,20 @@ config XFRM_USER
 
 	  If unsure, say Y.
 
+config XFRM_USER_LEGACY
+	tristate "Legacy transformation user configuration interface"
+	depends on XFRM_USER
+	default y
+	---help---
+	  The original Transformation(XFRM) netlink messages were not
+	  compatible between 32-bit programs and 64-bit kernels, so they
+	  have been deprecated.  Enable this option if you have existing
+	  binaries that rely on the old format messages.  Disable this
+	  option if you know that all users of the interface have been
+	  built against recent kernel headers.
+
+	  If unsure, say Y.
+
 config XFRM_SUB_POLICY
 	bool "Transformation sub policy support"
 	depends on XFRM
diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile
index c0e961983f17..6cf6f8da3dc8 100644
--- a/net/xfrm/Makefile
+++ b/net/xfrm/Makefile
@@ -7,5 +7,11 @@ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
 		      xfrm_sysctl.o xfrm_replay.o
 obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
 obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
-obj-$(CONFIG_XFRM_USER) += xfrm_user.o
+
+xfrm-user-objs := xfrm_user.o
+ifneq ($(CONFIG_XFRM_USER_LEGACY),)
+xfrm-user-objs += xfrm_user_legacy.o
+endif
+obj-$(CONFIG_XFRM_USER) += xfrm-user.o
+
 obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
diff --git a/net/xfrm/xfrm_user_legacy.c b/net/xfrm/xfrm_user_legacy.c
new file mode 100644
index 000000000000..058accfefc83
--- /dev/null
+++ b/net/xfrm/xfrm_user_legacy.c
@@ -0,0 +1,1091 @@
+/* xfrm_user.c: User interface to configure xfrm engine.
+ *
+ * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ *
+ * Changes:
+ *	Mitsuru KANDA @USAGI
+ * 	Kazunori MIYAZAWA @USAGI
+ * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com>
+ * 		IPv6 support
+ *
+ */
+
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/pfkeyv2.h>
+#include <linux/ipsec.h>
+#include <linux/security.h>
+#include <net/sock.h>
+#include <net/xfrm.h>
+#include <net/netlink.h>
+#include <net/ah.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+#include "xfrm_user.h"
+
+static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	const struct xfrm_usersa_info *p = nlmsg_data(nlh);
+	struct xfrm_state *x;
+	int err;
+	struct km_event c;
+
+	err = xfrm_verify_newsa_info(p, attrs);
+	if (err)
+		return err;
+
+	x = xfrm_state_construct(net, p, attrs, &err);
+	if (!x)
+		return err;
+
+	xfrm_state_hold(x);
+	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)
+		err = xfrm_state_add(x);
+	else
+		err = xfrm_state_update(x);
+
+	xfrm_audit_state_add(x, err ? 0 : 1, true);
+
+	if (err < 0) {
+		x->km.state = XFRM_STATE_DEAD;
+		__xfrm_state_put(x);
+		goto out;
+	}
+
+	c.seq = nlh->nlmsg_seq;
+	c.portid = nlh->nlmsg_pid;
+	c.event = nlh->nlmsg_type;
+
+	km_state_notify(x, &c);
+out:
+	xfrm_state_put(x);
+	return err;
+}
+
+static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_state *x;
+	int err = -ESRCH;
+	struct km_event c;
+	const struct xfrm_usersa_id *p = nlmsg_data(nlh);
+
+	x = xfrm_user_state_lookup(net, p, attrs, &err);
+	if (x == NULL)
+		return err;
+
+	if ((err = security_xfrm_state_delete(x)) != 0)
+		goto out;
+
+	if (xfrm_state_kern(x)) {
+		err = -EPERM;
+		goto out;
+	}
+
+	err = xfrm_state_delete(x);
+
+	if (err < 0)
+		goto out;
+
+	c.seq = nlh->nlmsg_seq;
+	c.portid = nlh->nlmsg_pid;
+	c.event = nlh->nlmsg_type;
+	km_state_notify(x, &c);
+
+out:
+	xfrm_audit_state_delete(x, err ? 0 : 1, true);
+	xfrm_state_put(x);
+	return err;
+}
+
+static void copy_to_user_state(const struct xfrm_state *x,
+			       struct xfrm_usersa_info *p)
+{
+	memset(p, 0, sizeof(*p));
+	memcpy(&p->id, &x->id, sizeof(p->id));
+	memcpy(&p->sel, &x->sel, sizeof(p->sel));
+	memcpy(&p->lft, &x->lft, sizeof(p->lft));
+	memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));
+	put_unaligned(x->stats.replay_window, &p->stats.replay_window);
+	put_unaligned(x->stats.replay, &p->stats.replay);
+	put_unaligned(x->stats.integrity_failed, &p->stats.integrity_failed);
+	memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));
+	p->mode = x->props.mode;
+	p->replay_window = x->props.replay_window;
+	p->reqid = x->props.reqid;
+	p->family = x->props.family;
+	p->flags = x->props.flags;
+	p->seq = x->km.seq;
+}
+
+static int copy_to_user_state_extra(const struct xfrm_state *x,
+				    struct xfrm_usersa_info *p,
+				    struct sk_buff *skb)
+{
+	int ret = 0;
+
+	copy_to_user_state(x, p);
+
+	if (x->props.extra_flags) {
+		ret = nla_put_u32(skb, XFRMA_SA_EXTRA_FLAGS,
+				  x->props.extra_flags);
+		if (ret)
+			goto out;
+	}
+
+	if (x->coaddr) {
+		ret = nla_put(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);
+		if (ret)
+			goto out;
+	}
+	if (x->lastused) {
+		ret = nla_put_u64_64bit(skb, XFRMA_LASTUSED, x->lastused,
+					XFRMA_PAD);
+		if (ret)
+			goto out;
+	}
+	if (x->aead) {
+		ret = nla_put(skb, XFRMA_ALG_AEAD, aead_len(x->aead), x->aead);
+		if (ret)
+			goto out;
+	}
+	if (x->aalg) {
+		ret = xfrm_copy_to_user_auth(x->aalg, skb);
+		if (!ret)
+			ret = nla_put(skb, XFRMA_ALG_AUTH_TRUNC,
+				      xfrm_alg_auth_len(x->aalg), x->aalg);
+		if (ret)
+			goto out;
+	}
+	if (x->ealg) {
+		ret = nla_put(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg);
+		if (ret)
+			goto out;
+	}
+	if (x->calg) {
+		ret = nla_put(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg);
+		if (ret)
+			goto out;
+	}
+	if (x->encap) {
+		ret = nla_put(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);
+		if (ret)
+			goto out;
+	}
+	if (x->tfcpad) {
+		ret = nla_put_u32(skb, XFRMA_TFCPAD, x->tfcpad);
+		if (ret)
+			goto out;
+	}
+	ret = xfrm_mark_put(skb, &x->mark);
+	if (ret)
+		goto out;
+	if (x->replay_esn)
+		ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
+			      xfrm_replay_state_esn_len(x->replay_esn),
+			      x->replay_esn);
+	else
+		ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
+			      &x->replay);
+	if (ret)
+		goto out;
+	if (x->security)
+		ret = xfrm_copy_sec_ctx(x->security, skb);
+out:
+	return ret;
+}
+
+static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
+{
+	struct xfrm_dump_info *sp = ptr;
+	struct sk_buff *in_skb = sp->in_skb;
+	struct sk_buff *skb = sp->out_skb;
+	struct xfrm_usersa_info *p;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
+			XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	p = nlmsg_data(nlh);
+
+	err = copy_to_user_state_extra(x, p, skb);
+	if (err) {
+		nlmsg_cancel(skb, nlh);
+		return err;
+	}
+	nlmsg_end(skb, nlh);
+	return 0;
+}
+
+static int xfrm_dump_sa_done(struct netlink_callback *cb)
+{
+	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
+	struct sock *sk = cb->skb->sk;
+	struct net *net = sock_net(sk);
+
+	if (cb->args[0])
+		xfrm_state_walk_done(walk, net);
+	return 0;
+}
+
+static const struct nla_policy xfrma_policy[XFRMA_MAX+1];
+static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_state_walk *walk = (struct xfrm_state_walk *) &cb->args[1];
+	struct xfrm_dump_info info;
+
+	BUILD_BUG_ON(sizeof(struct xfrm_state_walk) >
+		     sizeof(cb->args) - sizeof(cb->args[0]));
+
+	info.in_skb = cb->skb;
+	info.out_skb = skb;
+	info.nlmsg_seq = cb->nlh->nlmsg_seq;
+	info.nlmsg_flags = NLM_F_MULTI;
+
+	if (!cb->args[0]) {
+		struct nlattr *attrs[XFRMA_MAX+1];
+		struct xfrm_address_filter *filter = NULL;
+		u8 proto = 0;
+		int err;
+
+		err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX,
+				  xfrma_policy);
+		if (err < 0)
+			return err;
+
+		if (attrs[XFRMA_ADDRESS_FILTER]) {
+			filter = kmemdup(nla_data(attrs[XFRMA_ADDRESS_FILTER]),
+					 sizeof(*filter), GFP_KERNEL);
+			if (filter == NULL)
+				return -ENOMEM;
+		}
+
+		if (attrs[XFRMA_PROTO])
+			proto = nla_get_u8(attrs[XFRMA_PROTO]);
+
+		xfrm_state_walk_init(walk, proto, filter);
+		cb->args[0] = 1;
+	}
+
+	(void) xfrm_state_walk(net, walk, dump_one_state, &info);
+
+	return skb->len;
+}
+
+static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
+					  const struct xfrm_state *x,
+					  u32 seq)
+{
+	struct xfrm_dump_info info;
+	struct sk_buff *skb;
+	int err;
+
+	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	info.in_skb = in_skb;
+	info.out_skb = skb;
+	info.nlmsg_seq = seq;
+	info.nlmsg_flags = 0;
+
+	err = dump_one_state(x, 0, &info);
+	if (err) {
+		kfree_skb(skb);
+		return ERR_PTR(err);
+	}
+
+	return skb;
+}
+
+static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	const struct xfrm_usersa_id *p = nlmsg_data(nlh);
+	struct xfrm_state *x;
+	struct sk_buff *resp_skb;
+	int err = -ESRCH;
+
+	x = xfrm_user_state_lookup(net, p, attrs, &err);
+	if (x == NULL)
+		goto out_noput;
+
+	resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
+	if (IS_ERR(resp_skb)) {
+		err = PTR_ERR(resp_skb);
+	} else {
+		err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid);
+	}
+	xfrm_state_put(x);
+out_noput:
+	return err;
+}
+
+static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_state *x;
+	const struct xfrm_userspi_info *p;
+	struct sk_buff *resp_skb;
+	const xfrm_address_t *daddr;
+	int family;
+	int err;
+	u32 mark;
+	struct xfrm_mark m;
+
+	p = nlmsg_data(nlh);
+	err = verify_spi_info(p->info.id.proto, p->min, p->max);
+	if (err)
+		goto out_noput;
+
+	family = p->info.family;
+	daddr = &p->info.id.daddr;
+
+	x = NULL;
+
+	mark = xfrm_mark_get(attrs, &m);
+	if (p->info.seq) {
+		x = xfrm_find_acq_byseq(net, mark, p->info.seq);
+		if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
+			xfrm_state_put(x);
+			x = NULL;
+		}
+	}
+
+	if (!x)
+		x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
+				  p->info.id.proto, daddr,
+				  &p->info.saddr, 1,
+				  family);
+	err = -ENOENT;
+	if (x == NULL)
+		goto out_noput;
+
+	err = xfrm_alloc_spi(x, p->min, p->max);
+	if (err)
+		goto out;
+
+	resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq);
+	if (IS_ERR(resp_skb)) {
+		err = PTR_ERR(resp_skb);
+		goto out;
+	}
+
+	err = nlmsg_unicast(net->xfrm.nlsk, resp_skb, NETLINK_CB(skb).portid);
+
+out:
+	xfrm_state_put(x);
+out_noput:
+	return err;
+}
+
+static void copy_to_user_policy(const struct xfrm_policy *xp,
+				struct xfrm_userpolicy_info *p,
+				int dir)
+{
+	memset(p, 0, sizeof(*p));
+	memcpy(&p->sel, &xp->selector, sizeof(p->sel));
+	memcpy(&p->lft, &xp->lft, sizeof(p->lft));
+	memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
+	p->priority = xp->priority;
+	p->index = xp->index;
+	p->sel.family = xp->family;
+	p->dir = dir;
+	p->action = xp->action;
+	p->flags = xp->flags;
+	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
+}
+
+static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	const struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
+	struct xfrm_policy *xp;
+	struct km_event c;
+	int err;
+	int excl;
+
+	err = xfrm_verify_newpolicy_info(p);
+	if (err)
+		return err;
+	err = xfrm_verify_sec_ctx_len(attrs);
+	if (err)
+		return err;
+
+	xp = xfrm_policy_construct(net, p, attrs, &err);
+	if (!xp)
+		return err;
+
+	/* shouldn't excl be based on nlh flags??
+	 * Aha! this is anti-netlink really i.e  more pfkey derived
+	 * in netlink excl is a flag and you wouldnt need
+	 * a type XFRM_MSG_UPDPOLICY - JHS */
+	excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY;
+	err = xfrm_policy_insert(p->dir, xp, excl);
+	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
+
+	if (err) {
+		security_xfrm_policy_free(xp->security);
+		kfree(xp);
+		return err;
+	}
+
+	c.event = nlh->nlmsg_type;
+	c.seq = nlh->nlmsg_seq;
+	c.portid = nlh->nlmsg_pid;
+	km_policy_notify(xp, p->dir, &c);
+
+	xfrm_pol_put(xp);
+
+	return 0;
+}
+
+static inline size_t userpolicy_type_attrsize(void)
+{
+#ifdef CONFIG_XFRM_SUB_POLICY
+	return nla_total_size(sizeof(struct xfrm_userpolicy_type));
+#else
+	return 0;
+#endif
+}
+
+static int dump_one_policy(const struct xfrm_policy *xp,
+			   int dir,
+			   int count,
+			   void *ptr)
+{
+	struct xfrm_dump_info *sp = ptr;
+	struct xfrm_userpolicy_info *p;
+	struct sk_buff *in_skb = sp->in_skb;
+	struct sk_buff *skb = sp->out_skb;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).portid, sp->nlmsg_seq,
+			XFRM_MSG_NEWPOLICY, sizeof(*p), sp->nlmsg_flags);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	p = nlmsg_data(nlh);
+	copy_to_user_policy(xp, p, dir);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
+	if (!err)
+		err = copy_to_user_sec_ctx(xp, skb);
+	if (!err)
+		err = copy_to_user_policy_type(xp->type, skb);
+	if (!err)
+		err = xfrm_mark_put(skb, &xp->mark);
+	if (err) {
+		nlmsg_cancel(skb, nlh);
+		return err;
+	}
+	nlmsg_end(skb, nlh);
+	return 0;
+}
+
+static int xfrm_dump_policy_done(struct netlink_callback *cb)
+{
+	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+	struct net *net = sock_net(cb->skb->sk);
+
+	xfrm_policy_walk_done(walk, net);
+	return 0;
+}
+
+static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+	struct xfrm_dump_info info;
+
+	BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) >
+		     sizeof(cb->args) - sizeof(cb->args[0]));
+
+	info.in_skb = cb->skb;
+	info.out_skb = skb;
+	info.nlmsg_seq = cb->nlh->nlmsg_seq;
+	info.nlmsg_flags = NLM_F_MULTI;
+
+	if (!cb->args[0]) {
+		cb->args[0] = 1;
+		xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
+	}
+
+	(void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
+
+	return skb->len;
+}
+
+static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
+					   const struct xfrm_policy *xp,
+					   int dir,
+					   u32 seq)
+{
+	struct xfrm_dump_info info;
+	struct sk_buff *skb;
+	int err;
+
+	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	info.in_skb = in_skb;
+	info.out_skb = skb;
+	info.nlmsg_seq = seq;
+	info.nlmsg_flags = 0;
+
+	err = dump_one_policy(xp, dir, 0, &info);
+	if (err) {
+		kfree_skb(skb);
+		return ERR_PTR(err);
+	}
+
+	return skb;
+}
+
+static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_policy *xp;
+	const struct xfrm_userpolicy_id *p;
+	u8 type = XFRM_POLICY_TYPE_MAIN;
+	int err;
+	struct km_event c;
+	int delete;
+	struct xfrm_mark m;
+	u32 mark = xfrm_mark_get(attrs, &m);
+
+	p = nlmsg_data(nlh);
+	delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
+
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
+	if (err)
+		return err;
+
+	err = xfrm_verify_policy_dir(p->dir);
+	if (err)
+		return err;
+
+	if (p->index)
+		xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err);
+	else {
+		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
+		struct xfrm_sec_ctx *ctx;
+
+		err = xfrm_verify_sec_ctx_len(attrs);
+		if (err)
+			return err;
+
+		ctx = NULL;
+		if (rt) {
+			struct xfrm_user_sec_ctx *uctx = nla_data(rt);
+
+			err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);
+			if (err)
+				return err;
+		}
+		xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel,
+					   ctx, delete, &err);
+		security_xfrm_policy_free(ctx);
+	}
+	if (xp == NULL)
+		return -ENOENT;
+
+	if (!delete) {
+		struct sk_buff *resp_skb;
+
+		resp_skb = xfrm_policy_netlink(skb, xp, p->dir, nlh->nlmsg_seq);
+		if (IS_ERR(resp_skb)) {
+			err = PTR_ERR(resp_skb);
+		} else {
+			err = nlmsg_unicast(net->xfrm.nlsk, resp_skb,
+					    NETLINK_CB(skb).portid);
+		}
+	} else {
+		xfrm_audit_policy_delete(xp, err ? 0 : 1, true);
+
+		if (err != 0)
+			goto out;
+
+		c.data.byid = p->index;
+		c.event = nlh->nlmsg_type;
+		c.seq = nlh->nlmsg_seq;
+		c.portid = nlh->nlmsg_pid;
+		km_policy_notify(xp, p->dir, &c);
+	}
+
+out:
+	xfrm_pol_put(xp);
+	if (delete && err == 0)
+		xfrm_garbage_collect(net);
+	return err;
+}
+
+static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			       struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_policy *xp;
+	const struct xfrm_user_polexpire *up = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info *p = &up->pol;
+	u8 type = XFRM_POLICY_TYPE_MAIN;
+	int err = -ENOENT;
+	struct xfrm_mark m;
+	u32 mark = xfrm_mark_get(attrs, &m);
+
+	err = xfrm_copy_from_user_policy_type(&type, attrs);
+	if (err)
+		return err;
+
+	err = xfrm_verify_policy_dir(p->dir);
+	if (err)
+		return err;
+
+	if (p->index)
+		xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err);
+	else {
+		struct nlattr *rt = attrs[XFRMA_SEC_CTX];
+		struct xfrm_sec_ctx *ctx;
+
+		err = xfrm_verify_sec_ctx_len(attrs);
+		if (err)
+			return err;
+
+		ctx = NULL;
+		if (rt) {
+			struct xfrm_user_sec_ctx *uctx = nla_data(rt);
+
+			err = security_xfrm_policy_alloc(&ctx, uctx, GFP_KERNEL);
+			if (err)
+				return err;
+		}
+		xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir,
+					   &p->sel, ctx, 0, &err);
+		security_xfrm_policy_free(ctx);
+	}
+	if (xp == NULL)
+		return -ENOENT;
+
+	if (unlikely(xp->walk.dead))
+		goto out;
+
+	err = 0;
+	if (up->hard) {
+		xfrm_policy_delete(xp, p->dir);
+		xfrm_audit_policy_delete(xp, 1, true);
+	}
+	km_policy_expired(xp, p->dir, up->hard, nlh->nlmsg_pid);
+
+out:
+	xfrm_pol_put(xp);
+	return err;
+}
+
+static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_state *x;
+	int err;
+	const struct xfrm_user_expire *ue = nlmsg_data(nlh);
+	const struct xfrm_usersa_info *p = &ue->state;
+	struct xfrm_mark m;
+	u32 mark = xfrm_mark_get(attrs, &m);
+
+	x = xfrm_state_lookup(net, mark, &p->id.daddr, p->id.spi, p->id.proto, p->family);
+
+	err = -ENOENT;
+	if (x == NULL)
+		return err;
+
+	spin_lock_bh(&x->lock);
+	err = -EINVAL;
+	if (x->km.state != XFRM_STATE_VALID)
+		goto out;
+	km_state_expired(x, ue->hard, nlh->nlmsg_pid);
+
+	if (ue->hard) {
+		__xfrm_state_delete(x);
+		xfrm_audit_state_delete(x, 1, true);
+	}
+	err = 0;
+out:
+	spin_unlock_bh(&x->lock);
+	xfrm_state_put(x);
+	return err;
+}
+
+static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs)
+{
+	struct net *net = sock_net(skb->sk);
+	struct xfrm_policy *xp;
+	struct xfrm_user_tmpl *ut;
+	int i;
+	struct nlattr *rt = attrs[XFRMA_TMPL];
+	struct xfrm_mark mark;
+
+	const struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+	struct xfrm_state *x = xfrm_state_alloc(net);
+	int err = -ENOMEM;
+
+	if (!x)
+		goto nomem;
+
+	xfrm_mark_get(attrs, &mark);
+
+	err = xfrm_verify_newpolicy_info(&ua->policy);
+	if (err)
+		goto free_state;
+
+	/*   build an XP */
+	xp = xfrm_policy_construct(net, &ua->policy, attrs, &err);
+	if (!xp)
+		goto free_state;
+
+	memcpy(&x->id, &ua->id, sizeof(ua->id));
+	memcpy(&x->props.saddr, &ua->saddr, sizeof(ua->saddr));
+	memcpy(&x->sel, &ua->sel, sizeof(ua->sel));
+	xp->mark.m = x->mark.m = mark.m;
+	xp->mark.v = x->mark.v = mark.v;
+	ut = nla_data(rt);
+	/* extract the templates and for each call km_key */
+	for (i = 0; i < xp->xfrm_nr; i++, ut++) {
+		struct xfrm_tmpl *t = &xp->xfrm_vec[i];
+		memcpy(&x->id, &t->id, sizeof(x->id));
+		x->props.mode = t->mode;
+		x->props.reqid = t->reqid;
+		x->props.family = ut->family;
+		t->aalgos = ua->aalgos;
+		t->ealgos = ua->ealgos;
+		t->calgos = ua->calgos;
+		err = km_query(x, t, xp);
+
+	}
+
+	kfree(x);
+	kfree(xp);
+
+	return 0;
+
+free_state:
+	kfree(x);
+nomem:
+	return err;
+}
+
+static inline size_t xfrm_expire_msgsize(void)
+{
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_expire))
+	       + nla_total_size(sizeof(struct xfrm_mark));
+}
+
+static int build_expire(struct sk_buff *skb,
+			const struct xfrm_state *x,
+			const struct km_event *c)
+{
+	struct xfrm_user_expire *ue;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_EXPIRE, sizeof(*ue), 0);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	ue = nlmsg_data(nlh);
+	copy_to_user_state(x, &ue->state);
+	ue->hard = (c->data.hard != 0) ? 1 : 0;
+
+	err = xfrm_mark_put(skb, &x->mark);
+	if (err)
+		return err;
+
+	nlmsg_end(skb, nlh);
+	return 0;
+}
+
+static int xfrm_exp_state_notify(const struct xfrm_state *x,
+				 const struct km_event *c)
+{
+	struct net *net = xs_net(x);
+	struct sk_buff *skb;
+
+	skb = nlmsg_new(xfrm_expire_msgsize(), GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	if (build_expire(skb, x, c) < 0) {
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
+	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
+}
+
+static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
+{
+	struct net *net = xs_net(x);
+	struct xfrm_usersa_info *p;
+	struct xfrm_usersa_id *id;
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	int len = xfrm_sa_len(x);
+	int headlen, err;
+
+	headlen = sizeof(*p);
+	if (c->event == XFRM_MSG_DELSA) {
+		len += nla_total_size(headlen);
+		headlen = sizeof(*id);
+		len += nla_total_size(sizeof(struct xfrm_mark));
+	}
+	len += NLMSG_ALIGN(headlen);
+
+	skb = nlmsg_new(len, GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	err = -EMSGSIZE;
+	if (nlh == NULL)
+		goto out_free_skb;
+
+	p = nlmsg_data(nlh);
+	if (c->event == XFRM_MSG_DELSA) {
+		struct nlattr *attr;
+
+		id = nlmsg_data(nlh);
+		memcpy(&id->daddr, &x->id.daddr, sizeof(id->daddr));
+		id->spi = x->id.spi;
+		id->family = x->props.family;
+		id->proto = x->id.proto;
+
+		attr = nla_reserve(skb, XFRMA_SA, sizeof(*p));
+		err = -EMSGSIZE;
+		if (attr == NULL)
+			goto out_free_skb;
+
+		p = nla_data(attr);
+	}
+	err = copy_to_user_state_extra(x, p, skb);
+	if (err)
+		goto out_free_skb;
+
+	nlmsg_end(skb, nlh);
+
+	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
+
+out_free_skb:
+	kfree_skb(skb);
+	return err;
+}
+
+static inline size_t xfrm_acquire_msgsize(const struct xfrm_state *x,
+					  const struct xfrm_policy *xp)
+{
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
+	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
+	       + nla_total_size(sizeof(struct xfrm_mark))
+	       + nla_total_size(xfrm_user_sec_ctx_size(x->security))
+	       + userpolicy_type_attrsize();
+}
+
+static int build_acquire(struct sk_buff *skb,
+			 struct xfrm_state *x,
+			 const struct xfrm_tmpl *xt,
+			 const struct xfrm_policy *xp)
+{
+	__u32 seq = xfrm_get_acqseq();
+	struct xfrm_user_acquire *ua;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = nlmsg_put(skb, 0, 0, XFRM_MSG_ACQUIRE, sizeof(*ua), 0);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	ua = nlmsg_data(nlh);
+	memcpy(&ua->id, &x->id, sizeof(ua->id));
+	memcpy(&ua->saddr, &x->props.saddr, sizeof(ua->saddr));
+	memcpy(&ua->sel, &x->sel, sizeof(ua->sel));
+	copy_to_user_policy(xp, &ua->policy, XFRM_POLICY_OUT);
+	ua->aalgos = xt->aalgos;
+	ua->ealgos = xt->ealgos;
+	ua->calgos = xt->calgos;
+	ua->seq = x->km.seq = seq;
+
+	err = xfrm_copy_to_user_tmpl(xp, skb);
+	if (!err)
+		err = copy_to_user_state_sec_ctx(x, skb);
+	if (!err)
+		err = copy_to_user_policy_type(xp->type, skb);
+	if (!err)
+		err = xfrm_mark_put(skb, &xp->mark);
+	if (err) {
+		nlmsg_cancel(skb, nlh);
+		return err;
+	}
+
+	nlmsg_end(skb, nlh);
+	return 0;
+}
+
+static int xfrm_send_acquire(struct xfrm_state *x,
+			     const struct xfrm_tmpl *xt,
+			     const struct xfrm_policy *xp)
+{
+	struct net *net = xs_net(x);
+	struct sk_buff *skb;
+
+	skb = nlmsg_new(xfrm_acquire_msgsize(x, xp), GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	if (build_acquire(skb, x, xt, xp) < 0)
+		BUG();
+
+	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_ACQUIRE);
+}
+
+static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
+{
+	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
+	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
+	       + nla_total_size(xfrm_user_sec_ctx_size(xp->security))
+	       + nla_total_size(sizeof(struct xfrm_mark))
+	       + userpolicy_type_attrsize();
+}
+
+static int build_polexpire(struct sk_buff *skb,
+			   const struct xfrm_policy *xp,
+			   int dir,
+			   const struct km_event *c)
+{
+	struct xfrm_user_polexpire *upe;
+	int hard = c->data.hard;
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = nlmsg_put(skb, c->portid, 0, XFRM_MSG_POLEXPIRE, sizeof(*upe), 0);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	upe = nlmsg_data(nlh);
+	copy_to_user_policy(xp, &upe->pol, dir);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
+	if (!err)
+		err = copy_to_user_sec_ctx(xp, skb);
+	if (!err)
+		err = copy_to_user_policy_type(xp->type, skb);
+	if (!err)
+		err = xfrm_mark_put(skb, &xp->mark);
+	if (err) {
+		nlmsg_cancel(skb, nlh);
+		return err;
+	}
+	upe->hard = !!hard;
+
+	nlmsg_end(skb, nlh);
+	return 0;
+}
+
+static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
+				  int dir,
+				  const struct km_event *c)
+{
+	struct net *net = xp_net(xp);
+	struct sk_buff *skb;
+
+	skb = nlmsg_new(xfrm_polexpire_msgsize(xp), GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	if (build_polexpire(skb, xp, dir, c) < 0)
+		BUG();
+
+	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
+}
+
+static int xfrm_notify_policy(const struct xfrm_policy *xp,
+			      int dir,
+			      const struct km_event *c)
+{
+	int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
+	struct net *net = xp_net(xp);
+	struct xfrm_userpolicy_info *p;
+	struct xfrm_userpolicy_id *id;
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	int headlen, err;
+
+	headlen = sizeof(*p);
+	if (c->event == XFRM_MSG_DELPOLICY) {
+		len += nla_total_size(headlen);
+		headlen = sizeof(*id);
+	}
+	len += userpolicy_type_attrsize();
+	len += nla_total_size(sizeof(struct xfrm_mark));
+	len += NLMSG_ALIGN(headlen);
+
+	skb = nlmsg_new(len, GFP_ATOMIC);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	nlh = nlmsg_put(skb, c->portid, c->seq, c->event, headlen, 0);
+	err = -EMSGSIZE;
+	if (nlh == NULL)
+		goto out_free_skb;
+
+	p = nlmsg_data(nlh);
+	if (c->event == XFRM_MSG_DELPOLICY) {
+		struct nlattr *attr;
+
+		id = nlmsg_data(nlh);
+		memset(id, 0, sizeof(*id));
+		id->dir = dir;
+		if (c->data.byid)
+			id->index = xp->index;
+		else
+			memcpy(&id->sel, &xp->selector, sizeof(id->sel));
+
+		attr = nla_reserve(skb, XFRMA_POLICY, sizeof(*p));
+		err = -EMSGSIZE;
+		if (attr == NULL)
+			goto out_free_skb;
+
+		p = nla_data(attr);
+	}
+
+	copy_to_user_policy(xp, p, dir);
+	err = xfrm_copy_to_user_tmpl(xp, skb);
+	if (!err)
+		err = copy_to_user_policy_type(xp->type, skb);
+	if (!err)
+		err = xfrm_mark_put(skb, &xp->mark);
+	if (err)
+		goto out_free_skb;
+
+	nlmsg_end(skb, nlh);
+
+	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_POLICY);
+
+out_free_skb:
+	kfree_skb(skb);
+	return err;
+}
-- 
2.11.0.483.g087da7b7c-goog

^ permalink raw reply related

* [PATCH 1/4] xfrm: Constify xfrm_user arguments and xfrm_mgr callback APIs
From: Kevin Cernekee @ 2017-01-21  0:05 UTC (permalink / raw)
  To: steffen.klassert, herbert, davem, paul, sds, eparis
  Cc: linux-kernel, netdev, selinux, fw, fan.du, dianders, dtor
In-Reply-To: <20170121000507.34381-1-cernekee@chromium.org>

This provides a better sense of the data flow and inputs/outputs.  No
change to code size or functionality.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
---
 include/net/xfrm.h     |  36 ++++--
 net/key/af_key.c       |  34 +++--
 net/xfrm/xfrm_policy.c |   8 +-
 net/xfrm/xfrm_state.c  |   2 +-
 net/xfrm/xfrm_user.c   | 342 +++++++++++++++++++++++++++++--------------------
 5 files changed, 253 insertions(+), 169 deletions(-)

diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 31947b9c21d6..34298d78ba45 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -228,7 +228,7 @@ struct xfrm_state {
 	void			*data;
 };
 
-static inline struct net *xs_net(struct xfrm_state *x)
+static inline struct net *xs_net(const struct xfrm_state *x)
 {
 	return read_pnet(&x->xs_net);
 }
@@ -587,12 +587,23 @@ struct xfrm_migrate {
 struct xfrm_mgr {
 	struct list_head	list;
 	char			*id;
-	int			(*notify)(struct xfrm_state *x, const struct km_event *c);
-	int			(*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp);
-	struct xfrm_policy	*(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir);
-	int			(*new_mapping)(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport);
-	int			(*notify_policy)(struct xfrm_policy *x, int dir, const struct km_event *c);
-	int			(*report)(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr);
+	int			(*notify)(const struct xfrm_state *x,
+					  const struct km_event *c);
+	int			(*acquire)(struct xfrm_state *x,
+					   const struct xfrm_tmpl *,
+					   const struct xfrm_policy *xp);
+	struct xfrm_policy	*(*compile_policy)(struct sock *sk,
+						   int opt, u8 *data,
+						   int len, int *dir);
+	int			(*new_mapping)(struct xfrm_state *x,
+					       const xfrm_address_t *ipaddr,
+					       __be16 sport);
+	int			(*notify_policy)(const struct xfrm_policy *x,
+						 int dir,
+						 const struct km_event *c);
+	int			(*report)(struct net *net, u8 proto,
+					  const struct xfrm_selector *sel,
+					  const xfrm_address_t *addr);
 	int			(*migrate)(const struct xfrm_selector *sel,
 					   u8 dir, u8 type,
 					   const struct xfrm_migrate *m,
@@ -1432,7 +1443,7 @@ static inline void xfrm_sysctl_fini(struct net *net)
 void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto,
 			  struct xfrm_address_filter *filter);
 int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
-		    int (*func)(struct xfrm_state *, int, void*), void *);
+		    int (*func)(const struct xfrm_state *, int, void*), void *);
 void xfrm_state_walk_done(struct xfrm_state_walk *walk, struct net *net);
 struct xfrm_state *xfrm_state_alloc(struct net *net);
 struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
@@ -1584,13 +1595,13 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp);
 
 void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
 int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
-		     int (*func)(struct xfrm_policy *, int, int, void*),
+		     int (*func)(const struct xfrm_policy *, int, int, void*),
 		     void *);
 void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net);
 int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
 struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
 					  u8 type, int dir,
-					  struct xfrm_selector *sel,
+					  const struct xfrm_selector *sel,
 					  struct xfrm_sec_ctx *ctx, int delete,
 					  int *err);
 struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
@@ -1695,7 +1706,7 @@ static inline int xfrm_acquire_is_on(struct net *net)
 }
 #endif
 
-static inline int aead_len(struct xfrm_algo_aead *alg)
+static inline int aead_len(const struct xfrm_algo_aead *alg)
 {
 	return sizeof(*alg) + ((alg->alg_key_len + 7) / 8);
 }
@@ -1710,7 +1721,8 @@ static inline int xfrm_alg_auth_len(const struct xfrm_algo_auth *alg)
 	return sizeof(*alg) + ((alg->alg_key_len + 7) / 8);
 }
 
-static inline int xfrm_replay_state_esn_len(struct xfrm_replay_state_esn *replay_esn)
+static inline int xfrm_replay_state_esn_len(
+		const struct xfrm_replay_state_esn *replay_esn)
 {
 	return sizeof(*replay_esn) + replay_esn->bmp_len * sizeof(__u32);
 }
diff --git a/net/key/af_key.c b/net/key/af_key.c
index c6252ed42c1d..b09a48c4c1fb 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1433,7 +1433,7 @@ static inline int event2keytype(int event)
 }
 
 /* ADD/UPD/DEL */
-static int key_notify_sa(struct xfrm_state *x, const struct km_event *c)
+static int key_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 {
 	struct sk_buff *skb;
 	struct sadb_msg *hdr;
@@ -1744,7 +1744,7 @@ static int pfkey_flush(struct sock *sk, struct sk_buff *skb, const struct sadb_m
 	return 0;
 }
 
-static int dump_sa(struct xfrm_state *x, int count, void *ptr)
+static int dump_sa(const struct xfrm_state *x, int count, void *ptr)
 {
 	struct pfkey_sock *pfk = ptr;
 	struct sk_buff *out_skb;
@@ -1851,7 +1851,8 @@ static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb
 	return 0;
 }
 
-static int check_reqid(struct xfrm_policy *xp, int dir, int count, void *ptr)
+static int check_reqid(const struct xfrm_policy *xp, int dir, int count,
+		       void *ptr)
 {
 	int i;
 	u32 reqid = *(u32*)ptr;
@@ -2157,7 +2158,9 @@ static int pfkey_xfrm_policy2msg(struct sk_buff *skb, const struct xfrm_policy *
 	return 0;
 }
 
-static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
+static int key_notify_policy(const struct xfrm_policy *xp,
+			     int dir,
+			     const struct km_event *c)
 {
 	struct sk_buff *out_skb;
 	struct sadb_msg *out_hdr;
@@ -2628,7 +2631,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_
 	return err;
 }
 
-static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
+static int dump_sp(const struct xfrm_policy *xp, int dir, int count, void *ptr)
 {
 	struct pfkey_sock *pfk = ptr;
 	struct sk_buff *out_skb;
@@ -2961,12 +2964,14 @@ static void dump_esp_combs(struct sk_buff *skb, const struct xfrm_tmpl *t)
 	}
 }
 
-static int key_notify_policy_expire(struct xfrm_policy *xp, const struct km_event *c)
+static int key_notify_policy_expire(const struct xfrm_policy *xp,
+				    const struct km_event *c)
 {
 	return 0;
 }
 
-static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
+static int key_notify_sa_expire(const struct xfrm_state *x,
+				const struct km_event *c)
 {
 	struct sk_buff *out_skb;
 	struct sadb_msg *out_hdr;
@@ -2996,7 +3001,8 @@ static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c)
 	return 0;
 }
 
-static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
+static int pfkey_send_notify(const struct xfrm_state *x,
+			     const struct km_event *c)
 {
 	struct net *net = x ? xs_net(x) : c->net;
 	struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id);
@@ -3023,7 +3029,9 @@ static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c)
 	return 0;
 }
 
-static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
+static int pfkey_send_policy_notify(const struct xfrm_policy *xp,
+				    int dir,
+				    const struct km_event *c)
 {
 	if (xp && xp->type != XFRM_POLICY_TYPE_MAIN)
 		return 0;
@@ -3076,7 +3084,9 @@ static bool pfkey_is_alive(const struct km_event *c)
 	return is_alive;
 }
 
-static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
+static int pfkey_send_acquire(struct xfrm_state *x,
+			      const struct xfrm_tmpl *t,
+			      const struct xfrm_policy *xp)
 {
 	struct sk_buff *skb;
 	struct sadb_msg *hdr;
@@ -3273,7 +3283,9 @@ static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
 	return NULL;
 }
 
-static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
+static int pfkey_send_new_mapping(struct xfrm_state *x,
+				  const xfrm_address_t *ipaddr,
+				  __be16 sport)
 {
 	struct sk_buff *skb;
 	struct sadb_msg *hdr;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 177e208e8ff5..dc51dec54043 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -709,7 +709,8 @@ static u32 xfrm_gen_index(struct net *net, int dir, u32 index)
 	}
 }
 
-static inline int selector_cmp(struct xfrm_selector *s1, struct xfrm_selector *s2)
+static inline int selector_cmp(const struct xfrm_selector *s1,
+			       const struct xfrm_selector *s2)
 {
 	u32 *p1 = (u32 *) s1;
 	u32 *p2 = (u32 *) s2;
@@ -833,7 +834,8 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
 EXPORT_SYMBOL(xfrm_policy_insert);
 
 struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
-					  int dir, struct xfrm_selector *sel,
+					  int dir,
+					  const struct xfrm_selector *sel,
 					  struct xfrm_sec_ctx *ctx, int delete,
 					  int *err)
 {
@@ -1016,7 +1018,7 @@ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
 EXPORT_SYMBOL(xfrm_policy_flush);
 
 int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
-		     int (*func)(struct xfrm_policy *, int, int, void*),
+		     int (*func)(const struct xfrm_policy *, int, int, void*),
 		     void *data)
 {
 	struct xfrm_policy *pol;
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 64e3c82eedf6..4513a621b0d5 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1642,7 +1642,7 @@ static bool __xfrm_state_filter_match(struct xfrm_state *x,
 }
 
 int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk,
-		    int (*func)(struct xfrm_state *, int, void*),
+		    int (*func)(const struct xfrm_state *, int, void*),
 		    void *data)
 {
 	struct xfrm_state *state;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 9705c279494b..ed389aad4994 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -115,7 +115,7 @@ static inline int verify_sec_ctx_len(struct nlattr **attrs)
 	return 0;
 }
 
-static inline int verify_replay(struct xfrm_usersa_info *p,
+static inline int verify_replay(const struct xfrm_usersa_info *p,
 				struct nlattr **attrs)
 {
 	struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
@@ -148,7 +148,7 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
 	return 0;
 }
 
-static int verify_newsa_info(struct xfrm_usersa_info *p,
+static int verify_newsa_info(const struct xfrm_usersa_info *p,
 			     struct nlattr **attrs)
 {
 	int err;
@@ -264,11 +264,14 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
 	return err;
 }
 
-static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
-			   struct xfrm_algo_desc *(*get_byname)(const char *, int),
-			   struct nlattr *rta)
+static int attach_one_algo(
+	struct xfrm_algo **algpp,
+	u8 *props,
+	struct xfrm_algo_desc *(*get_byname)(const char *, int),
+	const struct nlattr *rta)
 {
-	struct xfrm_algo *p, *ualg;
+	const struct xfrm_algo *ualg;
+	struct xfrm_algo *p;
 	struct xfrm_algo_desc *algo;
 
 	if (!rta)
@@ -290,9 +293,10 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
 	return 0;
 }
 
-static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
+static int attach_crypt(struct xfrm_state *x, const struct nlattr *rta)
 {
-	struct xfrm_algo *p, *ualg;
+	const struct xfrm_algo *ualg;
+	struct xfrm_algo *p;
 	struct xfrm_algo_desc *algo;
 
 	if (!rta)
@@ -316,9 +320,9 @@ static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
 }
 
 static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
-		       struct nlattr *rta)
+		       const struct nlattr *rta)
 {
-	struct xfrm_algo *ualg;
+	const struct xfrm_algo *ualg;
 	struct xfrm_algo_auth *p;
 	struct xfrm_algo_desc *algo;
 
@@ -346,9 +350,10 @@ static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
 }
 
 static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
-			     struct nlattr *rta)
+			     const struct nlattr *rta)
 {
-	struct xfrm_algo_auth *p, *ualg;
+	const struct xfrm_algo_auth *ualg;
+	struct xfrm_algo_auth *p;
 	struct xfrm_algo_desc *algo;
 
 	if (!rta)
@@ -375,9 +380,10 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
 	return 0;
 }
 
-static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
+static int attach_aead(struct xfrm_state *x, const struct nlattr *rta)
 {
-	struct xfrm_algo_aead *p, *ualg;
+	const struct xfrm_algo_aead *ualg;
+	struct xfrm_algo_aead *p;
 	struct xfrm_algo_desc *algo;
 
 	if (!rta)
@@ -400,10 +406,11 @@ static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
 	return 0;
 }
 
-static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_esn,
-					 struct nlattr *rp)
+static inline int xfrm_replay_verify_len(
+	const struct xfrm_replay_state_esn *replay_esn,
+	const struct nlattr *rp)
 {
-	struct xfrm_replay_state_esn *up;
+	const struct xfrm_replay_state_esn *up;
 	int ulen;
 
 	if (!replay_esn || !rp)
@@ -418,11 +425,13 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
 	return 0;
 }
 
-static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn,
-				       struct xfrm_replay_state_esn **preplay_esn,
-				       struct nlattr *rta)
+static int xfrm_alloc_replay_state_esn(
+	struct xfrm_replay_state_esn **replay_esn,
+	struct xfrm_replay_state_esn **preplay_esn,
+	const struct nlattr *rta)
 {
-	struct xfrm_replay_state_esn *p, *pp, *up;
+	struct xfrm_replay_state_esn *p, *pp;
+	const struct xfrm_replay_state_esn *up;
 	int klen, ulen;
 
 	if (!rta)
@@ -451,7 +460,7 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
 	return 0;
 }
 
-static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
+static inline int xfrm_user_sec_ctx_size(const struct xfrm_sec_ctx *xfrm_ctx)
 {
 	int len = 0;
 
@@ -462,7 +471,8 @@ static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx)
 	return len;
 }
 
-static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
+static void copy_from_user_state(struct xfrm_state *x,
+				 const struct xfrm_usersa_info *p)
 {
 	memcpy(&x->id, &p->id, sizeof(x->id));
 	memcpy(&x->sel, &p->sel, sizeof(x->sel));
@@ -484,17 +494,19 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
  * somehow made shareable and move it to xfrm_state.c - JHS
  *
 */
-static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
+static void xfrm_update_ae_params(struct xfrm_state *x,
+				  struct nlattr **attrs,
 				  int update_esn)
 {
-	struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
-	struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
-	struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
-	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
-	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
+	const struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
+	const struct nlattr *re =
+		update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
+	const struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
+	const struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
+	const struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
 
 	if (re) {
-		struct xfrm_replay_state_esn *replay_esn;
+		const struct xfrm_replay_state_esn *replay_esn;
 		replay_esn = nla_data(re);
 		memcpy(x->replay_esn, replay_esn,
 		       xfrm_replay_state_esn_len(replay_esn));
@@ -503,14 +515,14 @@ static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
 	}
 
 	if (rp) {
-		struct xfrm_replay_state *replay;
+		const struct xfrm_replay_state *replay;
 		replay = nla_data(rp);
 		memcpy(&x->replay, replay, sizeof(*replay));
 		memcpy(&x->preplay, replay, sizeof(*replay));
 	}
 
 	if (lt) {
-		struct xfrm_lifetime_cur *ltime;
+		const struct xfrm_lifetime_cur *ltime;
 		ltime = nla_data(lt);
 		x->curlft.bytes = ltime->bytes;
 		x->curlft.packets = ltime->packets;
@@ -526,7 +538,7 @@ static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
 }
 
 static struct xfrm_state *xfrm_state_construct(struct net *net,
-					       struct xfrm_usersa_info *p,
+					       const struct xfrm_usersa_info *p,
 					       struct nlattr **attrs,
 					       int *errp)
 {
@@ -613,11 +625,11 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
 	return NULL;
 }
 
-static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_add_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
-	struct xfrm_usersa_info *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_info *p = nlmsg_data(nlh);
 	struct xfrm_state *x;
 	int err;
 	struct km_event c;
@@ -655,7 +667,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 }
 
 static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
-						 struct xfrm_usersa_id *p,
+						 const struct xfrm_usersa_id *p,
 						 struct nlattr **attrs,
 						 int *errp)
 {
@@ -688,14 +700,14 @@ static struct xfrm_state *xfrm_user_state_lookup(struct net *net,
 	return x;
 }
 
-static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_del_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
 	int err = -ESRCH;
 	struct km_event c;
-	struct xfrm_usersa_id *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_id *p = nlmsg_data(nlh);
 
 	x = xfrm_user_state_lookup(net, p, attrs, &err);
 	if (x == NULL)
@@ -725,7 +737,8 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
+static void copy_to_user_state(const struct xfrm_state *x,
+			       struct xfrm_usersa_info *p)
 {
 	memset(p, 0, sizeof(*p));
 	memcpy(&p->id, &x->id, sizeof(p->id));
@@ -751,7 +764,7 @@ struct xfrm_dump_info {
 	u16 nlmsg_flags;
 };
 
-static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
+static int copy_sec_ctx(const struct xfrm_sec_ctx *s, struct sk_buff *skb)
 {
 	struct xfrm_user_sec_ctx *uctx;
 	struct nlattr *attr;
@@ -772,7 +785,8 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb)
 	return 0;
 }
 
-static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
+static int copy_to_user_auth(const struct xfrm_algo_auth *auth,
+			     struct sk_buff *skb)
 {
 	struct xfrm_algo *algo;
 	struct nlattr *nla;
@@ -791,7 +805,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
 }
 
 /* Don't change this without updating xfrm_sa_len! */
-static int copy_to_user_state_extra(struct xfrm_state *x,
+static int copy_to_user_state_extra(const struct xfrm_state *x,
 				    struct xfrm_usersa_info *p,
 				    struct sk_buff *skb)
 {
@@ -868,7 +882,7 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
 	return ret;
 }
 
-static int dump_one_state(struct xfrm_state *x, int count, void *ptr)
+static int dump_one_state(const struct xfrm_state *x, int count, void *ptr)
 {
 	struct xfrm_dump_info *sp = ptr;
 	struct sk_buff *in_skb = sp->in_skb;
@@ -950,7 +964,8 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb)
 }
 
 static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
-					  struct xfrm_state *x, u32 seq)
+					  const struct xfrm_state *x,
+					  u32 seq)
 {
 	struct xfrm_dump_info info;
 	struct sk_buff *skb;
@@ -1050,7 +1065,7 @@ static int build_spdinfo(struct sk_buff *skb, struct net *net,
 	return 0;
 }
 
-static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
+static int xfrm_set_spdinfo(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
@@ -1095,12 +1110,12 @@ static int xfrm_set_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return 0;
 }
 
-static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_get_spdinfo(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct sk_buff *r_skb;
-	u32 *flags = nlmsg_data(nlh);
+	const u32 *flags = nlmsg_data(nlh);
 	u32 sportid = NETLINK_CB(skb).portid;
 	u32 seq = nlh->nlmsg_seq;
 
@@ -1153,12 +1168,12 @@ static int build_sadinfo(struct sk_buff *skb, struct net *net,
 	return 0;
 }
 
-static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_get_sadinfo(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct sk_buff *r_skb;
-	u32 *flags = nlmsg_data(nlh);
+	const u32 *flags = nlmsg_data(nlh);
 	u32 sportid = NETLINK_CB(skb).portid;
 	u32 seq = nlh->nlmsg_seq;
 
@@ -1172,11 +1187,11 @@ static int xfrm_get_sadinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return nlmsg_unicast(net->xfrm.nlsk, r_skb, sportid);
 }
 
-static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_get_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
-	struct xfrm_usersa_id *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_id *p = nlmsg_data(nlh);
 	struct xfrm_state *x;
 	struct sk_buff *resp_skb;
 	int err = -ESRCH;
@@ -1196,14 +1211,14 @@ static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_alloc_userspi(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
-	struct xfrm_userspi_info *p;
+	const struct xfrm_userspi_info *p;
 	struct sk_buff *resp_skb;
-	xfrm_address_t *daddr;
+	const xfrm_address_t *daddr;
 	int family;
 	int err;
 	u32 mark;
@@ -1286,7 +1301,7 @@ static int verify_policy_type(u8 type)
 	return 0;
 }
 
-static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
+static int verify_newpolicy_info(const struct xfrm_userpolicy_info *p)
 {
 	int ret;
 
@@ -1346,7 +1361,8 @@ static int copy_from_user_sec_ctx(struct xfrm_policy *pol, struct nlattr **attrs
 	return security_xfrm_policy_alloc(&pol->security, uctx, GFP_KERNEL);
 }
 
-static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut,
+static void copy_templates(struct xfrm_policy *xp,
+			   const struct xfrm_user_tmpl *ut,
 			   int nr)
 {
 	int i;
@@ -1444,7 +1460,8 @@ static int copy_from_user_policy_type(u8 *tp, struct nlattr **attrs)
 	return 0;
 }
 
-static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p)
+static void copy_from_user_policy(struct xfrm_policy *xp,
+				  const struct xfrm_userpolicy_info *p)
 {
 	xp->priority = p->priority;
 	xp->index = p->index;
@@ -1456,7 +1473,9 @@ static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy
 	/* XXX xp->share = p->share; */
 }
 
-static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
+static void copy_to_user_policy(const struct xfrm_policy *xp,
+				struct xfrm_userpolicy_info *p,
+				int dir)
 {
 	memset(p, 0, sizeof(*p));
 	memcpy(&p->sel, &xp->selector, sizeof(p->sel));
@@ -1471,7 +1490,11 @@ static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_i
 	p->share = XFRM_SHARE_ANY; /* XXX xp->share */
 }
 
-static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_userpolicy_info *p, struct nlattr **attrs, int *errp)
+static struct xfrm_policy *xfrm_policy_construct(
+	struct net *net,
+	const struct xfrm_userpolicy_info *p,
+	struct nlattr **attrs,
+	int *errp)
 {
 	struct xfrm_policy *xp = xfrm_policy_alloc(net, GFP_KERNEL);
 	int err;
@@ -1502,11 +1525,11 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
 	return NULL;
 }
 
-static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_add_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
-	struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info *p = nlmsg_data(nlh);
 	struct xfrm_policy *xp;
 	struct km_event c;
 	int err;
@@ -1547,7 +1570,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return 0;
 }
 
-static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
+static int copy_to_user_tmpl(const struct xfrm_policy *xp, struct sk_buff *skb)
 {
 	struct xfrm_user_tmpl vec[XFRM_MAX_DEPTH];
 	int i;
@@ -1557,7 +1580,7 @@ static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
 
 	for (i = 0; i < xp->xfrm_nr; i++) {
 		struct xfrm_user_tmpl *up = &vec[i];
-		struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
+		const struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
 
 		memset(up, 0, sizeof(*up));
 		memcpy(&up->id, &kp->id, sizeof(up->id));
@@ -1576,7 +1599,8 @@ static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
 		       sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr, vec);
 }
 
-static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buff *skb)
+static inline int copy_to_user_state_sec_ctx(const struct xfrm_state *x,
+					     struct sk_buff *skb)
 {
 	if (x->security) {
 		return copy_sec_ctx(x->security, skb);
@@ -1584,7 +1608,8 @@ static inline int copy_to_user_state_sec_ctx(struct xfrm_state *x, struct sk_buf
 	return 0;
 }
 
-static inline int copy_to_user_sec_ctx(struct xfrm_policy *xp, struct sk_buff *skb)
+static inline int copy_to_user_sec_ctx(const struct xfrm_policy *xp,
+				       struct sk_buff *skb)
 {
 	if (xp->security)
 		return copy_sec_ctx(xp->security, skb);
@@ -1616,7 +1641,10 @@ static inline int copy_to_user_policy_type(u8 type, struct sk_buff *skb)
 }
 #endif
 
-static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr)
+static int dump_one_policy(const struct xfrm_policy *xp,
+			   int dir,
+			   int count,
+			   void *ptr)
 {
 	struct xfrm_dump_info *sp = ptr;
 	struct xfrm_userpolicy_info *p;
@@ -1681,8 +1709,9 @@ static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
 }
 
 static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
-					  struct xfrm_policy *xp,
-					  int dir, u32 seq)
+					   const struct xfrm_policy *xp,
+					   int dir,
+					   u32 seq)
 {
 	struct xfrm_dump_info info;
 	struct sk_buff *skb;
@@ -1706,12 +1735,12 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
 	return skb;
 }
 
-static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_get_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			   struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_policy *xp;
-	struct xfrm_userpolicy_id *p;
+	const struct xfrm_userpolicy_id *p;
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err;
 	struct km_event c;
@@ -1785,12 +1814,12 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_flush_sa(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			 struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct km_event c;
-	struct xfrm_usersa_flush *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_flush *p = nlmsg_data(nlh);
 	int err;
 
 	err = xfrm_state_flush(net, p->proto, true);
@@ -1809,7 +1838,7 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return 0;
 }
 
-static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x)
+static inline size_t xfrm_aevent_msgsize(const struct xfrm_state *x)
 {
 	size_t replay_size = x->replay_esn ?
 			      xfrm_replay_state_esn_len(x->replay_esn) :
@@ -1823,7 +1852,9 @@ static inline size_t xfrm_aevent_msgsize(struct xfrm_state *x)
 	       + nla_total_size(4); /* XFRM_AE_ETHR */
 }
 
-static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
+static int build_aevent(struct sk_buff *skb,
+			const struct xfrm_state *x,
+			const struct km_event *c)
 {
 	struct xfrm_aevent_id *id;
 	struct nlmsghdr *nlh;
@@ -1880,8 +1911,8 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
 	return err;
 }
 
-static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_get_ae(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
@@ -1890,8 +1921,8 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
 	struct km_event c;
 	u32 mark;
 	struct xfrm_mark m;
-	struct xfrm_aevent_id *p = nlmsg_data(nlh);
-	struct xfrm_usersa_id *id = &p->sa_id;
+	const struct xfrm_aevent_id *p = nlmsg_data(nlh);
+	const struct xfrm_usersa_id *id = &p->sa_id;
 
 	mark = xfrm_mark_get(attrs, &m);
 
@@ -1923,8 +1954,8 @@ static int xfrm_get_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_new_ae(struct sk_buff *skb, const struct nlmsghdr *nlh,
+		       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
@@ -1932,12 +1963,12 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
 	int err = -EINVAL;
 	u32 mark = 0;
 	struct xfrm_mark m;
-	struct xfrm_aevent_id *p = nlmsg_data(nlh);
-	struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
-	struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
-	struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
-	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
-	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
+	const struct xfrm_aevent_id *p = nlmsg_data(nlh);
+	const struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
+	const struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
+	const struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
+	const struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
+	const struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
 
 	if (!lt && !rp && !re && !et && !rt)
 		return err;
@@ -1974,8 +2005,8 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_flush_policy(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			     struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct km_event c;
@@ -2002,13 +2033,13 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return 0;
 }
 
-static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_add_pol_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			       struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_policy *xp;
-	struct xfrm_user_polexpire *up = nlmsg_data(nlh);
-	struct xfrm_userpolicy_info *p = &up->pol;
+	const struct xfrm_user_polexpire *up = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_info *p = &up->pol;
 	u8 type = XFRM_POLICY_TYPE_MAIN;
 	int err = -ENOENT;
 	struct xfrm_mark m;
@@ -2062,14 +2093,14 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_add_sa_expire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			      struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_state *x;
 	int err;
-	struct xfrm_user_expire *ue = nlmsg_data(nlh);
-	struct xfrm_usersa_info *p = &ue->state;
+	const struct xfrm_user_expire *ue = nlmsg_data(nlh);
+	const struct xfrm_usersa_info *p = &ue->state;
 	struct xfrm_mark m;
 	u32 mark = xfrm_mark_get(attrs, &m);
 
@@ -2096,8 +2127,8 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return err;
 }
 
-static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
-		struct nlattr **attrs)
+static int xfrm_add_acquire(struct sk_buff *skb, const struct nlmsghdr *nlh,
+			    struct nlattr **attrs)
 {
 	struct net *net = sock_net(skb->sk);
 	struct xfrm_policy *xp;
@@ -2106,7 +2137,7 @@ static int xfrm_add_acquire(struct sk_buff *skb, struct nlmsghdr *nlh,
 	struct nlattr *rt = attrs[XFRMA_TMPL];
 	struct xfrm_mark mark;
 
-	struct xfrm_user_acquire *ua = nlmsg_data(nlh);
+	const struct xfrm_user_acquire *ua = nlmsg_data(nlh);
 	struct xfrm_state *x = xfrm_state_alloc(net);
 	int err = -ENOMEM;
 
@@ -2160,12 +2191,12 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
 				  struct xfrm_kmaddress *k,
 				  struct nlattr **attrs, int *num)
 {
-	struct nlattr *rt = attrs[XFRMA_MIGRATE];
-	struct xfrm_user_migrate *um;
+	const struct nlattr *rt = attrs[XFRMA_MIGRATE];
+	const struct xfrm_user_migrate *um;
 	int i, num_migrate;
 
 	if (k != NULL) {
-		struct xfrm_user_kmaddress *uk;
+		const struct xfrm_user_kmaddress *uk;
 
 		uk = nla_data(attrs[XFRMA_KMADDRESS]);
 		memcpy(&k->local, &uk->local, sizeof(k->local));
@@ -2198,10 +2229,10 @@ static int copy_from_user_migrate(struct xfrm_migrate *ma,
 	return 0;
 }
 
-static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
+static int xfrm_do_migrate(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			   struct nlattr **attrs)
 {
-	struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
+	const struct xfrm_userpolicy_id *pi = nlmsg_data(nlh);
 	struct xfrm_migrate m[XFRM_MAX_DEPTH];
 	struct xfrm_kmaddress km, *kmp;
 	u8 type;
@@ -2230,7 +2261,7 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
 	return 0;
 }
 #else
-static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
+static int xfrm_do_migrate(struct sk_buff *skb, const struct nlmsghdr *nlh,
 			   struct nlattr **attrs)
 {
 	return -ENOPROTOOPT;
@@ -2238,7 +2269,8 @@ static int xfrm_do_migrate(struct sk_buff *skb, struct nlmsghdr *nlh,
 #endif
 
 #ifdef CONFIG_XFRM_MIGRATE
-static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *skb)
+static int copy_to_user_migrate(const struct xfrm_migrate *m,
+				struct sk_buff *skb)
 {
 	struct xfrm_user_migrate um;
 
@@ -2256,7 +2288,8 @@ static int copy_to_user_migrate(const struct xfrm_migrate *m, struct sk_buff *sk
 	return nla_put(skb, XFRMA_MIGRATE, sizeof(um), &um);
 }
 
-static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k, struct sk_buff *skb)
+static int copy_to_user_kmaddress(const struct xfrm_kmaddress *k,
+				  struct sk_buff *skb)
 {
 	struct xfrm_user_kmaddress uk;
 
@@ -2407,7 +2440,9 @@ static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
 };
 
 static const struct xfrm_link {
-	int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
+	int (*doit)(struct sk_buff *,
+		    const struct nlmsghdr *,
+		    struct nlattr **);
 	int (*dump)(struct sk_buff *, struct netlink_callback *);
 	int (*done)(struct netlink_callback *);
 	const struct nla_policy *nla_pol;
@@ -2506,7 +2541,9 @@ static inline size_t xfrm_expire_msgsize(void)
 	       + nla_total_size(sizeof(struct xfrm_mark));
 }
 
-static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct km_event *c)
+static int build_expire(struct sk_buff *skb,
+			const struct xfrm_state *x,
+			const struct km_event *c)
 {
 	struct xfrm_user_expire *ue;
 	struct nlmsghdr *nlh;
@@ -2528,7 +2565,8 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
 	return 0;
 }
 
-static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
+static int xfrm_exp_state_notify(const struct xfrm_state *x,
+				 const struct km_event *c)
 {
 	struct net *net = xs_net(x);
 	struct sk_buff *skb;
@@ -2545,7 +2583,8 @@ static int xfrm_exp_state_notify(struct xfrm_state *x, const struct km_event *c)
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_aevent_state_notify(struct xfrm_state *x, const struct km_event *c)
+static int xfrm_aevent_state_notify(const struct xfrm_state *x,
+				    const struct km_event *c)
 {
 	struct net *net = xs_net(x);
 	struct sk_buff *skb;
@@ -2586,7 +2625,7 @@ static int xfrm_notify_sa_flush(const struct km_event *c)
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_SA);
 }
 
-static inline size_t xfrm_sa_len(struct xfrm_state *x)
+static inline size_t xfrm_sa_len(const struct xfrm_state *x)
 {
 	size_t l = 0;
 	if (x->aead)
@@ -2622,7 +2661,7 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
 	return l;
 }
 
-static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
+static int xfrm_notify_sa(const struct xfrm_state *x, const struct km_event *c)
 {
 	struct net *net = xs_net(x);
 	struct xfrm_usersa_info *p;
@@ -2679,7 +2718,8 @@ static int xfrm_notify_sa(struct xfrm_state *x, const struct km_event *c)
 	return err;
 }
 
-static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c)
+static int xfrm_send_state_notify(const struct xfrm_state *x,
+				  const struct km_event *c)
 {
 
 	switch (c->event) {
@@ -2703,8 +2743,8 @@ static int xfrm_send_state_notify(struct xfrm_state *x, const struct km_event *c
 
 }
 
-static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x,
-					  struct xfrm_policy *xp)
+static inline size_t xfrm_acquire_msgsize(const struct xfrm_state *x,
+					  const struct xfrm_policy *xp)
 {
 	return NLMSG_ALIGN(sizeof(struct xfrm_user_acquire))
 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
@@ -2713,8 +2753,10 @@ static inline size_t xfrm_acquire_msgsize(struct xfrm_state *x,
 	       + userpolicy_type_attrsize();
 }
 
-static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
-			 struct xfrm_tmpl *xt, struct xfrm_policy *xp)
+static int build_acquire(struct sk_buff *skb,
+			 struct xfrm_state *x,
+			 const struct xfrm_tmpl *xt,
+			 const struct xfrm_policy *xp)
 {
 	__u32 seq = xfrm_get_acqseq();
 	struct xfrm_user_acquire *ua;
@@ -2751,8 +2793,9 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
 	return 0;
 }
 
-static int xfrm_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *xt,
-			     struct xfrm_policy *xp)
+static int xfrm_send_acquire(struct xfrm_state *x,
+			     const struct xfrm_tmpl *xt,
+			     const struct xfrm_policy *xp)
 {
 	struct net *net = xs_net(x);
 	struct sk_buff *skb;
@@ -2774,7 +2817,8 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
 					       u8 *data, int len, int *dir)
 {
 	struct net *net = sock_net(sk);
-	struct xfrm_userpolicy_info *p = (struct xfrm_userpolicy_info *)data;
+	const struct xfrm_userpolicy_info *p =
+		(struct xfrm_userpolicy_info *)data;
 	struct xfrm_user_tmpl *ut = (struct xfrm_user_tmpl *) (p + 1);
 	struct xfrm_policy *xp;
 	int nr;
@@ -2827,7 +2871,7 @@ static struct xfrm_policy *xfrm_compile_policy(struct sock *sk, int opt,
 	return xp;
 }
 
-static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp)
+static inline size_t xfrm_polexpire_msgsize(const struct xfrm_policy *xp)
 {
 	return NLMSG_ALIGN(sizeof(struct xfrm_user_polexpire))
 	       + nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr)
@@ -2836,8 +2880,10 @@ static inline size_t xfrm_polexpire_msgsize(struct xfrm_policy *xp)
 	       + userpolicy_type_attrsize();
 }
 
-static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
-			   int dir, const struct km_event *c)
+static int build_polexpire(struct sk_buff *skb,
+			   const struct xfrm_policy *xp,
+			   int dir,
+			   const struct km_event *c)
 {
 	struct xfrm_user_polexpire *upe;
 	int hard = c->data.hard;
@@ -2867,7 +2913,9 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
 	return 0;
 }
 
-static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
+static int xfrm_exp_policy_notify(const struct xfrm_policy *xp,
+				  int dir,
+				  const struct km_event *c)
 {
 	struct net *net = xp_net(xp);
 	struct sk_buff *skb;
@@ -2882,7 +2930,9 @@ static int xfrm_exp_policy_notify(struct xfrm_policy *xp, int dir, const struct
 	return xfrm_nlmsg_multicast(net, skb, 0, XFRMNLGRP_EXPIRE);
 }
 
-static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_event *c)
+static int xfrm_notify_policy(const struct xfrm_policy *xp,
+			      int dir,
+			      const struct km_event *c)
 {
 	int len = nla_total_size(sizeof(struct xfrm_user_tmpl) * xp->xfrm_nr);
 	struct net *net = xp_net(xp);
@@ -2976,7 +3026,9 @@ static int xfrm_notify_policy_flush(const struct km_event *c)
 	return err;
 }
 
-static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
+static int xfrm_send_policy_notify(const struct xfrm_policy *xp,
+				   int dir,
+				   const struct km_event *c)
 {
 
 	switch (c->event) {
@@ -3003,7 +3055,8 @@ static inline size_t xfrm_report_msgsize(void)
 }
 
 static int build_report(struct sk_buff *skb, u8 proto,
-			struct xfrm_selector *sel, xfrm_address_t *addr)
+			const struct xfrm_selector *sel,
+			const xfrm_address_t *addr)
 {
 	struct xfrm_user_report *ur;
 	struct nlmsghdr *nlh;
@@ -3027,8 +3080,10 @@ static int build_report(struct sk_buff *skb, u8 proto,
 	return 0;
 }
 
-static int xfrm_send_report(struct net *net, u8 proto,
-			    struct xfrm_selector *sel, xfrm_address_t *addr)
+static int xfrm_send_report(struct net *net,
+			    u8 proto,
+			    const struct xfrm_selector *sel,
+			    const xfrm_address_t *addr)
 {
 	struct sk_buff *skb;
 
@@ -3047,8 +3102,10 @@ static inline size_t xfrm_mapping_msgsize(void)
 	return NLMSG_ALIGN(sizeof(struct xfrm_user_mapping));
 }
 
-static int build_mapping(struct sk_buff *skb, struct xfrm_state *x,
-			 xfrm_address_t *new_saddr, __be16 new_sport)
+static int build_mapping(struct sk_buff *skb,
+			 const struct xfrm_state *x,
+			 const xfrm_address_t *new_saddr,
+			 __be16 new_sport)
 {
 	struct xfrm_user_mapping *um;
 	struct nlmsghdr *nlh;
@@ -3073,7 +3130,8 @@ static int build_mapping(struct sk_buff *skb, struct xfrm_state *x,
 	return 0;
 }
 
-static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
+static int xfrm_send_mapping(struct xfrm_state *x,
+			     const xfrm_address_t *ipaddr,
 			     __be16 sport)
 {
 	struct net *net = xs_net(x);
-- 
2.11.0.483.g087da7b7c-goog

^ permalink raw reply related

* Re: [PATCH net-next 1/8] net: dsa: mv88e6xxx: Implement external MDIO bus on mv88e6390
From: Vivien Didelot @ 2017-01-21  0:04 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Andrew Lunn
In-Reply-To: <1484955062-26718-2-git-send-email-andrew@lunn.ch>

Hi Andrew,

Andrew Lunn <andrew@lunn.ch> writes:

> The mv88e6390 has two MDIO busses. The internal MDIO bus is used for
> the internal PHYs. The external MDIO can be used for external PHYs.
> The external MDIO bus will be instantiated if there is an
> "mdio-external" node in the device tree.

Thanks for pushing the 88E6390 support. Some comments below.

> +static int mv88e6xxx_read_phy(struct mv88e6xxx_chip *chip, int addr, int reg,
> +static int mv88e6xxx_write_phy(struct mv88e6xxx_chip *chip, int addr, int reg,
>  static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
>  static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,

Adding mv88e6xxx_read/write_phy() in addition to existing
mv88e6xxx_phy_read/write() feels really confusing and hard to
maintain. Can that be done the other way around maybe?

> +static int mv88e6xxx_external_mdio_register(struct mv88e6xxx_chip *chip,
> +					    struct device_node *np)
> +static void mv88e6xxx_external_mdio_unregister(struct mv88e6xxx_chip *chip)
>  static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
>  				   struct device_node *np)

We already have mv88e6xxx_mdio_register/unregister(). Isn't it possible
to tweak them to take a struct mv88e6xxx_mdio_bus instance and use them
twice for both internal and external MDIO busses?

> +	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_EXTERNAL_MDIO)) {
> +		err = mv88e6xxx_external_mdio_register(chip, np);
> +		if (err)
> +			goto out_mdio;
> +	}

We are trying to get rid of the flags and family checks... Please don't
add new ones. If the external MDIO bus is a new feature of switches like
88E6390, isn't it better to add new external_phy_read/write ops and
register the bus if they are provided?

    if (chip->info->ops->external_phy_read) {
        struct mv88e6xxx_mdio_bus *external_mdio_bus;
        ...
        err = mv88e6xxx_mdio_register(external_mdio_bus);
        if (err)
            ...
    }

Thanks,

        Vivien

^ permalink raw reply

* [PATCH 0/4] Make xfrm usable by 32-bit programs
From: Kevin Cernekee @ 2017-01-21  0:05 UTC (permalink / raw)
  To: steffen.klassert, herbert, davem, paul, sds, eparis
  Cc: linux-kernel, netdev, selinux, fw, fan.du, dianders, dtor

Several of the xfrm netlink and setsockopt() interfaces are not usable
from a 32-bit binary running on a 64-bit kernel due to struct padding
differences.  This has been the case for many, many years[0].  This
patch series deprecates the broken netlink messages and replaces them
with packed structs that are compatible between 64-bit and 32-bit
programs.  It retains support for legacy user programs (i.e. anything
that is currently working today), and allows legacy support to be
compiled out via CONFIG_XFRM_USER_LEGACY if it becomes unnecessary in
the future.

Earlier attempts at fixing the problem had implemented a compat layer.
A compat layer is helpful because it avoids the need to recompile old
user binaries, but there are many challenges involved in implementing
it.  I believe a compat layer is of limited value in this instance
because anybody who really needed to solve the problem without
recompiling their binaries has almost certainly found another solution
in the ~7 years since the compat patches were first proposed.

A benefit of this approach is that long-term, the broken netlink messages
will no longer be used.  A drawback is that in the short term, user
programs that want to adopt the new message formats will require a
modern kernel.  Projects like strongSwan and iproute2 bundle the xfrm.h
header inside their own source trees, so they will need to make a
judgment call on when to remove support for kernels that do not support
the new messages.  And programs built against the new kernel headers
will not work on old kernels.  (Perhaps this is an argument for naming
the new messages _NEW, rather than renaming the old messages to
_LEGACY.)

The following netlink messages are affected:

    XFRM_MSG_NEWSA
    XFRM_MSG_UPDSA
    XFRM_MSG_DELSA
    XFRM_MSG_GETSA
    XFRM_MSG_NEWPOLICY
    XFRM_MSG_UPDPOLICY
    XFRM_MSG_DELPOLICY
    XFRM_MSG_GETPOLICY
    XFRM_MSG_ALLOCSPI
    XFRM_MSG_ACQUIRE
    XFRM_MSG_EXPIRE
    XFRM_MSG_POLEXPIRE

The following setsockopt() settings are affected:

    IP_XFRM_POLICY
    IPV6_XFRM_POLICY

The root cause of the problem involves padding and alignment
incompatibilities in the following structs:

    xfrm_usersa_info 220 bytes on i386 -> 224 bytes on amd64
    xfrm_userpolicy_info 164 -> 168
    xfrm_userspi_info 228 -> 232, offset mismatch on min
    xfrm_user_acquire 276 -> 280, offset mismatch on aalgos
    xfrm_user_expire 224 -> 232, offset mismatch on hard
    xfrm_user_polexpire 168 -> 176, offset mismatch on hard

Most xfrm netlink messages consist of an xfrm_* struct followed by
additional attributes (struct nlattr TLV), so even cases where the
struct layout (sans padding) is identical will result in incompatible
messages.

Some possible tweaks to this approach:

a) Name the new messages _NEW instead of renaming the old messages
_LEGACY.  This fixes the "new binary on old kernel" problem, but it
means that callers need to change every call site in their programs
to explicitly request the new interface.

b) Tweak xfrm.h so that user programs build against the legacy
interfaces by default, but can alter that behavior using a #define
flag.  Maybe in a few years, assume that everyone is running a modern
kernel and make the new interface the default.


[0] https://www.spinics.net/lists/netdev/msg126176.html


Kevin Cernekee (4):
  xfrm: Constify xfrm_user arguments and xfrm_mgr callback APIs
  xfrm_user: Allow common functions to be called from another file
  xfrm_user: Initial commit of xfrm_user_legacy.c
  xfrm_user: Add new 32/64-agnostic netlink messages

 include/net/xfrm.h          |   36 +-
 include/uapi/linux/xfrm.h   |  152 ++++--
 net/key/af_key.c            |   34 +-
 net/xfrm/Kconfig            |   14 +
 net/xfrm/Makefile           |    8 +-
 net/xfrm/xfrm_policy.c      |    8 +-
 net/xfrm/xfrm_state.c       |    2 +-
 net/xfrm/xfrm_user.c        |  587 +++++++++++++---------
 net/xfrm/xfrm_user.h        |  165 +++++++
 net/xfrm/xfrm_user_legacy.c | 1140 +++++++++++++++++++++++++++++++++++++++++++
 security/selinux/nlmsgtab.c |   61 ++-
 11 files changed, 1890 insertions(+), 317 deletions(-)
 create mode 100644 net/xfrm/xfrm_user.h
 create mode 100644 net/xfrm/xfrm_user_legacy.c

-- 
2.11.0.483.g087da7b7c-goog

^ permalink raw reply

* Re: [PATCH net-next 6/8] net: phy: Marvell: Add mv88e6390 internal PHY
From: Florian Fainelli @ 2017-01-21  0:04 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Vivien Didelot
In-Reply-To: <1484955062-26718-7-git-send-email-andrew@lunn.ch>

On 01/20/2017 03:31 PM, Andrew Lunn wrote:
> The mv88e6390 Ethernet switch has internal PHYs. These PHYs don't have
> an model ID in the ID2 register. So the MDIO driver in the switch
> intercepts reads to this register, and returns the switch family ID.
> Extend the Marvell PHY driver by including this ID, and tread the PHY
> as a 88E1540.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply

* Re: [PATCH net-next 5/8] net: dsa: mv88e6xxx: Workaround missing PHY ID on mv88e6390
From: Florian Fainelli @ 2017-01-21  0:03 UTC (permalink / raw)
  To: Andrew Lunn, David Miller; +Cc: netdev, Vivien Didelot
In-Reply-To: <1484955062-26718-6-git-send-email-andrew@lunn.ch>

On 01/20/2017 03:30 PM, Andrew Lunn wrote:
> The internal PHYs of the mv88e6390 do not have a model ID. Trap any
> calls to the ID register, and if it is zero, return the ID for the
> mv88e6390. The Marvell PHY driver can then bind to this ID.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

Nice and clean!
-- 
Florian

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox