Netdev List
 help / color / mirror / Atom feed
* [PATCH v2 01/18] gtp: add genl family modules alias
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Auto-load the module when userspace asks for the gtp netlink
family.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 8b6810b..7580ccc 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1376,3 +1376,4 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Harald Welte <hwelte@sysmocom.de>");
 MODULE_DESCRIPTION("Interface driver for GTP encapsulated traffic");
 MODULE_ALIAS_RTNL_LINK("gtp");
+MODULE_ALIAS_GENL_FAMILY("gtp");
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 07/18] gtp: remove unnecessary rcu_read_lock
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

The rcu read lock is hold by default in the ip input path. There
is no need to hold it twice in the socket recv decapsulate code path.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 24 ++++--------------------
 1 file changed, 4 insertions(+), 20 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index bc8734b7..f434f84 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -183,7 +183,6 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 			      sizeof(struct gtp0_header);
 	struct gtp0_header *gtp0;
 	struct pdp_ctx *pctx;
-	int ret = 0;
 
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
@@ -196,26 +195,19 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 	if (gtp0->type != GTP_TPDU)
 		return 1;
 
-	rcu_read_lock();
 	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
 	if (IS_ERR(pctx)) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-		ret = -1;
-		goto out_rcu;
+		return -1;
 	}
 
 	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
 		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		ret = -1;
-		goto out_rcu;
+		return -1;
 	}
-	rcu_read_unlock();
 
 	/* Get rid of the GTP + UDP headers. */
 	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
-out_rcu:
-	rcu_read_unlock();
-	return ret;
 }
 
 static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
@@ -225,7 +217,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 			      sizeof(struct gtp1_header);
 	struct gtp1_header *gtp1;
 	struct pdp_ctx *pctx;
-	int ret = 0;
 
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
@@ -253,26 +244,19 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 
 	gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
 
-	rcu_read_lock();
 	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
 	if (IS_ERR(pctx)) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-		ret = -1;
-		goto out_rcu;
+		return -1;
 	}
 
 	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
 		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		ret = -1;
-		goto out_rcu;
+		return -1;
 	}
-	rcu_read_unlock();
 
 	/* Get rid of the GTP + UDP headers. */
 	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
-out_rcu:
-	rcu_read_unlock();
-	return ret;
 }
 
 /* UDP encapsulation receive handler. See net/ipv4/udp.c.
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 06/18] gtp: fix cross netns recv on gtp socket
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

The use of the paassed through netlink src_net to check for a
cross netns operation was wrong. Using the GTP socket and the
GTP netdevice is always correct (even if the netdev has been
moved to new netns after link creation).

Remove the now obsolete net field from gtp_dev.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 7a3c5f6..bc8734b7 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -69,7 +69,6 @@ struct gtp_dev {
 	struct socket		*sock0;
 	struct socket		*sock1u;
 
-	struct net		*net;
 	struct net_device	*dev;
 
 	unsigned int		hash_size;
@@ -292,7 +291,7 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 	netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
 
-	xnet = !net_eq(gtp->net, dev_net(gtp->dev));
+	xnet = !net_eq(sock_net(sk), dev_net(gtp->dev));
 
 	switch (udp_sk(sk)->encap_type) {
 	case UDP_ENCAP_GTP0:
@@ -642,7 +641,7 @@ static void gtp_link_setup(struct net_device *dev)
 static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
 static void gtp_hashtable_free(struct gtp_dev *gtp);
 static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
-			    struct net *src_net, struct nlattr *data[]);
+			    struct nlattr *data[]);
 static void gtp_encap_disable(struct gtp_dev *gtp);
 
 static int gtp_newlink(struct net *src_net, struct net_device *dev,
@@ -660,7 +659,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 		hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]);
 
 	if (data[IFLA_GTP_FD0] || data[IFLA_GTP_FD1]) {
-		err = gtp_encap_enable(dev, gtp, src_net, data);
+		err = gtp_encap_enable(dev, gtp, data);
 		if (err < 0)
 			goto out_err;
 	}
@@ -839,7 +838,7 @@ static struct socket *gtp_encap_enable_socket(int fd, int type,
 }
 
 static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
-			    struct net *src_net, struct nlattr *data[])
+			    struct nlattr *data[])
 {
 	struct socket *sock0 = NULL;
 	struct socket *sock1u = NULL;
@@ -867,7 +866,6 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
 
 	gtp->sock0 = sock0;
 	gtp->sock1u = sock1u;
-	gtp->net = src_net;
 
 	return 0;
 }
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 05/18] gtp: unify genl_find_pdp and prepare for per socket lookup
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

TEID are unique per socket and not per network device. Therefore
the API needs to be changed here.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 99 +++++++++++++++++--------------------------------------
 1 file changed, 31 insertions(+), 68 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index e95c856..7a3c5f6 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1044,56 +1044,61 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 	return ipv4_pdp_add(dev, info);
 }
 
-static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info)
+static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb,
+						 struct genl_info *info)
 {
 	struct net_device *dev;
-	struct pdp_ctx *pctx;
 	struct gtp_dev *gtp;
 	struct net *net;
+	__be32 ms_addr;
 
-	if (!info->attrs[GTPA_VERSION] ||
-	    !info->attrs[GTPA_LINK])
-		return -EINVAL;
+	if (!info->attrs[GTPA_MS_ADDRESS])
+		return ERR_PTR(-EINVAL);
+	ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 
 	net = gtp_genl_get_net(sock_net(skb->sk), info->attrs);
 	if (IS_ERR(net))
-		return PTR_ERR(net);
+		return ERR_PTR(PTR_ERR(net));
 
 	/* Check if there's an existing gtpX device to configure */
 	dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK]));
 	if (dev == NULL) {
 		put_net(net);
-		return -ENODEV;
+		return ERR_PTR(-ENODEV);
 	}
 	put_net(net);
 
 	gtp = netdev_priv(dev);
 
-	switch (nla_get_u32(info->attrs[GTPA_VERSION])) {
-	case GTP_V0:
-		if (!info->attrs[GTPA_TID])
-			return -EINVAL;
-		pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID]));
-		break;
-	case GTP_V1:
-		if (!info->attrs[GTPA_I_TEI])
-			return -EINVAL;
-		pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI]));
-		break;
+	return ipv4_pdp_find(gtp, ms_addr);
+}
 
-	default:
+static struct pdp_ctx *gtp_genl_find_pdp(struct sk_buff *skb,
+					 struct genl_info *info)
+{
+	if (info->attrs[GTPA_LINK])
+		return gtp_genl_find_pdp_by_link(skb, info);
+	else
+		return ERR_PTR(-EINVAL);
+}
+
+static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info)
+{
+	struct pdp_ctx *pctx;
+
+	if (!info->attrs[GTPA_VERSION])
 		return -EINVAL;
-	}
 
+	pctx = gtp_genl_find_pdp(skb, info);
 	if (IS_ERR(pctx))
 		return PTR_ERR(pctx);
 
 	if (pctx->gtp_version == GTP_V0)
-		netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n",
-			   pctx->u.v0.tid, pctx);
+		pr_debug("GTPv0-U: deleting tunnel id = %llx (pdp %p)\n",
+			 pctx->u.v0.tid, pctx);
 	else if (pctx->gtp_version == GTP_V1)
-		netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n",
-			   pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx);
+		pr_debug("GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n",
+			 pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx);
 
 	hlist_del_rcu(&pctx->hlist_tid);
 	hlist_del_rcu(&pctx->hlist_addr);
@@ -1143,57 +1148,15 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info)
 {
 	struct pdp_ctx *pctx = NULL;
-	struct net_device *dev;
 	struct sk_buff *skb2;
-	struct gtp_dev *gtp;
-	u32 gtp_version;
-	struct net *net;
 	int err;
 
-	if (!info->attrs[GTPA_VERSION] ||
-	    !info->attrs[GTPA_LINK])
-		return -EINVAL;
-
-	gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);
-	switch (gtp_version) {
-	case GTP_V0:
-	case GTP_V1:
-		break;
-	default:
+	if (!info->attrs[GTPA_VERSION])
 		return -EINVAL;
-	}
-
-	net = gtp_genl_get_net(sock_net(skb->sk), info->attrs);
-	if (IS_ERR(net))
-		return PTR_ERR(net);
-
-	/* Check if there's an existing gtpX device to configure */
-	dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK]));
-	if (dev == NULL) {
-		put_net(net);
-		return -ENODEV;
-	}
-	put_net(net);
-
-	gtp = netdev_priv(dev);
 
 	rcu_read_lock();
-	if (gtp_version == GTP_V0 &&
-	    info->attrs[GTPA_TID]) {
-		u64 tid = nla_get_u64(info->attrs[GTPA_TID]);
-
-		pctx = gtp0_pdp_find(gtp, tid);
-	} else if (gtp_version == GTP_V1 &&
-		 info->attrs[GTPA_I_TEI]) {
-		u32 tid = nla_get_u32(info->attrs[GTPA_I_TEI]);
-
-		pctx = gtp1_pdp_find(gtp, tid);
-	} else if (info->attrs[GTPA_MS_ADDRESS]) {
-		__be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
-
-		pctx = ipv4_pdp_find(gtp, ip);
-	}
 
+	pctx = gtp_genl_find_pdp(skb, info);
 	if (IS_ERR(pctx)) {
 		err = PTR_ERR(pctx);
 		goto err_unlock;
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 08/18] gtp: consolidate pdp context destruction into helper
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Remove duplicate code and use a rcu helper to free the
structure to prepare for addition of PDP context members
that need to be handled in the rcu callcack.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 32 +++++++++++++++++++++-----------
 1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index f434f84..c117f63 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -84,6 +84,8 @@ struct gtp_net {
 
 static u32 gtp_h_initval;
 
+static void pdp_context_delete(struct pdp_ctx *pctx);
+
 static inline u32 gtp0_hashfn(u64 tid)
 {
 	u32 *tid32 = (u32 *) &tid;
@@ -775,13 +777,10 @@ static void gtp_hashtable_free(struct gtp_dev *gtp)
 	struct pdp_ctx *pctx;
 	int i;
 
-	for (i = 0; i < gtp->hash_size; i++) {
-		hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i], hlist_tid) {
-			hlist_del_rcu(&pctx->hlist_tid);
-			hlist_del_rcu(&pctx->hlist_addr);
-			kfree_rcu(pctx, rcu_head);
-		}
-	}
+	for (i = 0; i < gtp->hash_size; i++)
+		hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i], hlist_tid)
+			pdp_context_delete(pctx);
+
 	synchronize_rcu();
 	kfree(gtp->addr_hash);
 	kfree(gtp->tid_hash);
@@ -984,6 +983,20 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
 	return 0;
 }
 
+static void pdp_context_free(struct rcu_head *head)
+{
+	struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head);
+
+	kfree(pctx);
+}
+
+static void pdp_context_delete(struct pdp_ctx *pctx)
+{
+	hlist_del_rcu(&pctx->hlist_tid);
+	hlist_del_rcu(&pctx->hlist_addr);
+	call_rcu(&pctx->rcu_head, pdp_context_free);
+}
+
 static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 {
 	struct net_device *dev;
@@ -1082,10 +1095,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info)
 		pr_debug("GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n",
 			 pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx);
 
-	hlist_del_rcu(&pctx->hlist_tid);
-	hlist_del_rcu(&pctx->hlist_addr);
-	kfree_rcu(pctx, rcu_head);
-
+	pdp_context_delete(pctx);
 	return 0;
 }
 
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 09/18] gtp: use addr_hash when traversing pdp contexts
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

This prepares for the removal of the tid_hash from the device.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index c117f63..c7e32a6 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -778,7 +778,7 @@ static void gtp_hashtable_free(struct gtp_dev *gtp)
 	int i;
 
 	for (i = 0; i < gtp->hash_size; i++)
-		hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i], hlist_tid)
+		hlist_for_each_entry_rcu(pctx, &gtp->addr_hash[i], hlist_addr)
 			pdp_context_delete(pctx);
 
 	synchronize_rcu();
@@ -1195,7 +1195,7 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
 			last_gtp = NULL;
 
 		for (i = k; i < gtp->hash_size; i++) {
-			hlist_for_each_entry_rcu(pctx, &gtp->tid_hash[i], hlist_tid) {
+			hlist_for_each_entry_rcu(pctx, &gtp->addr_hash[i], hlist_addr) {
 				if (tid && tid != pctx->u.tid)
 					continue;
 				else
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 16/18] gtp: add genl cmd to enable GTP encapsulation on UDP socket
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c        | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/gtp.h |  4 ++++
 2 files changed, 52 insertions(+)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 9e5f858..c6c1f0d 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1247,6 +1247,46 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
 	return skb->len;
 }
 
+static int gtp_genl_enable_socket(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 version, fd, hashsize;
+	struct socket *sock;
+
+	if (!info->attrs[GTPA_VERSION] ||
+	    !info->attrs[GTPA_FD])
+		return -EINVAL;
+
+	if (!info->attrs[GTPA_PDP_HASHSIZE])
+		hashsize = 1024;
+	else
+		hashsize = nla_get_u32(info->attrs[IFLA_GTP_PDP_HASHSIZE]);
+
+	version = nla_get_u32(info->attrs[GTPA_VERSION]);
+	fd = nla_get_u32(info->attrs[GTPA_FD]);
+
+	switch (version) {
+	case GTP_V0:
+		sock = gtp_encap_enable_socket(fd, UDP_ENCAP_GTP0, hashsize);
+		break;
+
+	case GTP_V1:
+		sock = gtp_encap_enable_socket(fd, UDP_ENCAP_GTP1U, hashsize);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (!sock)
+		return -EINVAL;
+
+	if (IS_ERR(sock))
+		return PTR_ERR(sock);
+
+	sockfd_put(sock);
+	return 0;
+}
+
 static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
 	[GTPA_LINK]		= { .type = NLA_U32, },
 	[GTPA_VERSION]		= { .type = NLA_U32, },
@@ -1257,6 +1297,8 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
 	[GTPA_NET_NS_FD]	= { .type = NLA_U32, },
 	[GTPA_I_TEI]		= { .type = NLA_U32, },
 	[GTPA_O_TEI]		= { .type = NLA_U32, },
+	[GTPA_PDP_HASHSIZE]	= { .type = NLA_U32, },
+	[GTPA_FD]		= { .type = NLA_U32, },
 };
 
 static const struct genl_ops gtp_genl_ops[] = {
@@ -1279,6 +1321,12 @@ static const struct genl_ops gtp_genl_ops[] = {
 		.policy = gtp_genl_policy,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = GTP_CMD_ENABLE_SOCKET,
+		.doit = gtp_genl_enable_socket,
+		.policy = gtp_genl_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family gtp_genl_family __ro_after_init = {
diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h
index 72a04a0..a9e9fe0 100644
--- a/include/uapi/linux/gtp.h
+++ b/include/uapi/linux/gtp.h
@@ -6,6 +6,8 @@ enum gtp_genl_cmds {
 	GTP_CMD_DELPDP,
 	GTP_CMD_GETPDP,
 
+	GTP_CMD_ENABLE_SOCKET,
+
 	GTP_CMD_MAX,
 };
 
@@ -26,6 +28,8 @@ enum gtp_attrs {
 	GTPA_I_TEI,	/* for GTPv1 only */
 	GTPA_O_TEI,	/* for GTPv1 only */
 	GTPA_PAD,
+	GTPA_PDP_HASHSIZE,
+	GTPA_FD,
 	__GTPA_MAX,
 };
 #define GTPA_MAX (__GTPA_MAX + 1)
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 15/18] gtp: rename gtp hashtable helpers
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

The gtp_hashtable helper are now olny used for the per netdevice
address hashes. Rename them to make their purpose clearer.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index d2ba943..9e5f858 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -618,8 +618,8 @@ static void gtp_link_setup(struct net_device *dev)
 				  sizeof(struct gtp0_header);
 }
 
-static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
-static void gtp_hashtable_free(struct gtp_dev *gtp);
+static int gtp_dev_hashtable_new(struct gtp_dev *gtp, int hsize);
+static void gtp_dev_hashtable_free(struct gtp_dev *gtp);
 static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
 			    int hsize, struct nlattr *data[]);
 static void gtp_encap_disable(struct gtp_dev *gtp);
@@ -644,7 +644,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 			goto out_err;
 	}
 
-	err = gtp_hashtable_new(gtp, hashsize);
+	err = gtp_dev_hashtable_new(gtp, hashsize);
 	if (err < 0)
 		goto out_socket;
 
@@ -662,7 +662,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 	return 0;
 
 out_hashtable:
-	gtp_hashtable_free(gtp);
+	gtp_dev_hashtable_free(gtp);
 out_socket:
 	gtp_encap_disable(gtp);
 out_err:
@@ -673,7 +673,7 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head)
 {
 	struct gtp_dev *gtp = netdev_priv(dev);
 
-	gtp_hashtable_free(gtp);
+	gtp_dev_hashtable_free(gtp);
 	if (gtp->sock0)
 		sockfd_put(gtp->sock0);
 	if (gtp->sock1u)
@@ -742,7 +742,7 @@ static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[])
 	return net;
 }
 
-static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
+static int gtp_dev_hashtable_new(struct gtp_dev *gtp, int hsize)
 {
 	int i;
 
@@ -758,7 +758,7 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
 	return 0;
 }
 
-static void gtp_hashtable_free(struct gtp_dev *gtp)
+static void gtp_dev_hashtable_free(struct gtp_dev *gtp)
 {
 	struct pdp_ctx *pctx;
 	int i;
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 14/18] gtp: move TEID hash to per socket structure
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Untangele the TEID information from the network device and move
it into a per socket structure.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 100 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 60 insertions(+), 40 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 7d82252..d2ba943 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -75,10 +75,15 @@ struct gtp_dev {
 	struct net_device	*dev;
 
 	unsigned int		hash_size;
-	struct hlist_head	*tid_hash;
 	struct hlist_head	*addr_hash;
 };
 
+/* One instance of the GTP socket. */
+struct gtp_sock {
+	unsigned int		hash_size;
+	struct hlist_head	tid_hash[];
+};
+
 static unsigned int gtp_net_id __read_mostly;
 
 struct gtp_net {
@@ -106,12 +111,12 @@ static inline u32 ipv4_hashfn(__be32 ip)
 }
 
 /* Resolve a PDP context structure based on the 64bit TID. */
-static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
+static struct pdp_ctx *gtp0_pdp_find(struct gtp_sock *gsk, u64 tid)
 {
 	struct hlist_head *head;
 	struct pdp_ctx *pdp;
 
-	head = &gtp->tid_hash[gtp0_hashfn(tid) % gtp->hash_size];
+	head = &gsk->tid_hash[gtp0_hashfn(tid) % gsk->hash_size];
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
 		if (pdp->gtp_version == GTP_V0 &&
@@ -122,12 +127,12 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
 }
 
 /* Resolve a PDP context structure based on the 32bit TEI. */
-static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid)
+static struct pdp_ctx *gtp1_pdp_find(struct gtp_sock *gsk, u32 tid)
 {
 	struct hlist_head *head;
 	struct pdp_ctx *pdp;
 
-	head = &gtp->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size];
+	head = &gsk->tid_hash[gtp1u_hashfn(tid) % gsk->hash_size];
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
 		if (pdp->gtp_version == GTP_V1 &&
@@ -215,7 +220,7 @@ static int gtp_rx(struct sk_buff *skb, struct pdp_ctx *pctx, unsigned int hdrlen
 }
 
 /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
-static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
+static int gtp0_udp_encap_recv(struct gtp_sock *gsk, struct sk_buff *skb)
 {
 	unsigned int hdrlen = sizeof(struct udphdr) +
 			      sizeof(struct gtp0_header);
@@ -233,7 +238,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	if (gtp0->type != GTP_TPDU)
 		return 1;
 
-	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
+	pctx = gtp0_pdp_find(gsk, be64_to_cpu(gtp0->tid));
 	if (IS_ERR(pctx)) {
 		pr_debug("No PDP ctx to decap skb=%p\n", skb);
 		return 1;
@@ -242,7 +247,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	return gtp_rx(skb, pctx, hdrlen);
 }
 
-static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
+static int gtp1u_udp_encap_recv(struct gtp_sock *gsk, struct sk_buff *skb)
 {
 	unsigned int hdrlen = sizeof(struct udphdr) +
 			      sizeof(struct gtp1_header);
@@ -275,7 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 
 	gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
 
-	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
+	pctx = gtp1_pdp_find(gsk, ntohl(gtp1->tid));
 	if (IS_ERR(pctx)) {
 		pr_debug("No PDP ctx to decap skb=%p\n", skb);
 		return 1;
@@ -289,11 +294,11 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
  */
 static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
-	struct gtp_dev *gtp;
+	struct gtp_sock *gsk;
 	int ret = 0;
 
-	gtp = rcu_dereference_sk_user_data(sk);
-	if (!gtp)
+	gsk = rcu_dereference_sk_user_data(sk);
+	if (!gsk)
 		return 1;
 
 	pr_debug("encap_recv sk=%p\n", sk);
@@ -301,11 +306,11 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	switch (udp_sk(sk)->encap_type) {
 	case UDP_ENCAP_GTP0:
 		pr_debug("received GTP0 packet\n");
-		ret = gtp0_udp_encap_recv(gtp, skb);
+		ret = gtp0_udp_encap_recv(gsk, skb);
 		break;
 	case UDP_ENCAP_GTP1U:
 		pr_debug("received GTP1U packet\n");
-		ret = gtp1u_udp_encap_recv(gtp, skb);
+		ret = gtp1u_udp_encap_recv(gsk, skb);
 		break;
 	default:
 		ret = -1; /* Shouldn't happen. */
@@ -329,12 +334,21 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 static void gtp_encap_destroy(struct sock *sk)
 {
-	struct gtp_dev *gtp;
+	struct gtp_sock *gsk;
+	struct pdp_ctx *pctx;
+	int i;
 
-	gtp = rcu_dereference_sk_user_data(sk);
-	if (gtp) {
+	gsk = rcu_dereference_sk_user_data(sk);
+	if (gsk) {
 		udp_sk(sk)->encap_type = 0;
 		rcu_assign_sk_user_data(sk, NULL);
+
+		for (i = 0; i < gsk->hash_size; i++)
+			hlist_for_each_entry_rcu(pctx, &gsk->tid_hash[i], hlist_tid)
+				pdp_context_delete(pctx);
+
+		synchronize_rcu();
+		kfree(gsk);
 	}
 }
 
@@ -607,7 +621,7 @@ static void gtp_link_setup(struct net_device *dev)
 static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
 static void gtp_hashtable_free(struct gtp_dev *gtp);
 static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
-			    struct nlattr *data[]);
+			    int hsize, struct nlattr *data[]);
 static void gtp_encap_disable(struct gtp_dev *gtp);
 
 static int gtp_newlink(struct net *src_net, struct net_device *dev,
@@ -625,7 +639,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 		hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]);
 
 	if (data[IFLA_GTP_FD0] || data[IFLA_GTP_FD1]) {
-		err = gtp_encap_enable(dev, gtp, data);
+		err = gtp_encap_enable(dev, gtp, hashsize, data);
 		if (err < 0)
 			goto out_err;
 	}
@@ -736,20 +750,12 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)
 	if (gtp->addr_hash == NULL)
 		return -ENOMEM;
 
-	gtp->tid_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL);
-	if (gtp->tid_hash == NULL)
-		goto err1;
-
 	gtp->hash_size = hsize;
 
-	for (i = 0; i < hsize; i++) {
+	for (i = 0; i < hsize; i++)
 		INIT_HLIST_HEAD(&gtp->addr_hash[i]);
-		INIT_HLIST_HEAD(&gtp->tid_hash[i]);
-	}
+
 	return 0;
-err1:
-	kfree(gtp->addr_hash);
-	return -ENOMEM;
 }
 
 static void gtp_hashtable_free(struct gtp_dev *gtp)
@@ -763,15 +769,14 @@ static void gtp_hashtable_free(struct gtp_dev *gtp)
 
 	synchronize_rcu();
 	kfree(gtp->addr_hash);
-	kfree(gtp->tid_hash);
 }
 
-static struct socket *gtp_encap_enable_socket(int fd, int type,
-					      struct gtp_dev *gtp)
+static struct socket *gtp_encap_enable_socket(int fd, int type, int hsize)
 {
 	struct udp_tunnel_sock_cfg tuncfg = {NULL};
+	struct gtp_sock *gsk;
 	struct socket *sock;
-	int err;
+	int err, i;
 
 	pr_debug("enable gtp on %d, %d\n", fd, type);
 
@@ -787,7 +792,17 @@ static struct socket *gtp_encap_enable_socket(int fd, int type,
 		goto out_sock;
 	}
 
-	tuncfg.sk_user_data = gtp;
+	gsk = kzalloc(sizeof(*gsk) + sizeof(struct hlist_head) * hsize, GFP_KERNEL);
+	if (!gsk) {
+		err = -ENOMEM;
+		goto out_sock;
+	}
+
+	gsk->hash_size = hsize;
+	for (i = 0; i < hsize; i++)
+		INIT_HLIST_HEAD(&gsk->tid_hash[i]);
+
+	tuncfg.sk_user_data = gsk;
 	tuncfg.encap_type = type;
 	tuncfg.encap_rcv = gtp_encap_recv;
 	tuncfg.encap_destroy = gtp_encap_destroy;
@@ -801,7 +816,7 @@ static struct socket *gtp_encap_enable_socket(int fd, int type,
 }
 
 static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
-			    struct nlattr *data[])
+			    int hsize, struct nlattr *data[])
 {
 	struct socket *sock0 = NULL;
 	struct socket *sock1u = NULL;
@@ -809,7 +824,7 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
 	if (data[IFLA_GTP_FD0]) {
 		u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
 
-		sock0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp);
+		sock0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, hsize);
 		if (IS_ERR(sock0))
 			return PTR_ERR(sock0);
 	}
@@ -817,7 +832,7 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp,
 	if (data[IFLA_GTP_FD1]) {
 		u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]);
 
-		sock1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp);
+		sock1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, hsize);
 		if (IS_ERR(sock1u)) {
 			if (sock0)
 				sockfd_put(sock0);
@@ -890,11 +905,16 @@ static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
 			struct genl_info *info)
 {
 	struct gtp_dev *gtp = netdev_priv(dev);
+	struct gtp_sock *gsk;
 	u32 hash_ms, hash_tid = 0;
 	struct pdp_ctx *pctx;
 	bool found = false;
 	__be32 ms_addr;
 
+	gsk = rcu_dereference_sk_user_data(sk);
+	if (!gsk)
+		return -ENODEV;
+
 	ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 	hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;
 
@@ -941,15 +961,15 @@ static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
 		 * situation in which this doesn't unambiguosly identify the
 		 * PDP context.
 		 */
-		hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gtp->hash_size;
+		hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gsk->hash_size;
 		break;
 	case GTP_V1:
-		hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gtp->hash_size;
+		hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gsk->hash_size;
 		break;
 	}
 
 	hlist_add_head_rcu(&pctx->hlist_addr, &gtp->addr_hash[hash_ms]);
-	hlist_add_head_rcu(&pctx->hlist_tid, &gtp->tid_hash[hash_tid]);
+	hlist_add_head_rcu(&pctx->hlist_tid, &gsk->tid_hash[hash_tid]);
 
 	switch (pctx->gtp_version) {
 	case GTP_V0:
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 13/18] gtp: replace netdev_dbg and KERN_DEBUG printk with pr_debug
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

pr_debug is more versatile for normal debugging.
Also replace netdev_dbg in in places where the network device
will be remove soon.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 8d74b7d..7d82252 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -235,7 +235,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 
 	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
 	if (IS_ERR(pctx)) {
-		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
+		pr_debug("No PDP ctx to decap skb=%p\n", skb);
 		return 1;
 	}
 
@@ -277,7 +277,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 
 	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
 	if (IS_ERR(pctx)) {
-		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
+		pr_debug("No PDP ctx to decap skb=%p\n", skb);
 		return 1;
 	}
 
@@ -296,15 +296,15 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	if (!gtp)
 		return 1;
 
-	netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
+	pr_debug("encap_recv sk=%p\n", sk);
 
 	switch (udp_sk(sk)->encap_type) {
 	case UDP_ENCAP_GTP0:
-		netdev_dbg(gtp->dev, "received GTP0 packet\n");
+		pr_debug("received GTP0 packet\n");
 		ret = gtp0_udp_encap_recv(gtp, skb);
 		break;
 	case UDP_ENCAP_GTP1U:
-		netdev_dbg(gtp->dev, "received GTP1U packet\n");
+		pr_debug("received GTP1U packet\n");
 		ret = gtp1u_udp_encap_recv(gtp, skb);
 		break;
 	default:
@@ -313,12 +313,12 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 	switch (ret) {
 	case 1:
-		netdev_dbg(gtp->dev, "pass up to the process\n");
+		pr_debug("pass up to the process\n");
 		break;
 	case 0:
 		break;
 	case -1:
-		netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
+		pr_debug("GTP packet has been dropped\n");
 		kfree_skb(skb);
 		ret = 0;
 		break;
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 10/18] gtp: add socket to pdp context
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Having the socket present in context simplifies the sending logic.
It also fixes the invalid assumtion that we have to use the same
sending socket for all client IP's on a specific gtp interface.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 70 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 38 insertions(+), 32 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index c7e32a6..fb93468 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -58,6 +58,8 @@ struct pdp_ctx {
 	struct in_addr		ms_addr_ip4;
 	struct in_addr		sgsn_addr_ip4;
 
+	struct sock		*sk;
+
 	atomic_t		tx_seq;
 	struct rcu_head		rcu_head;
 };
@@ -353,8 +355,9 @@ static void gtp_dev_uninit(struct net_device *dev)
 	free_percpu(dev->tstats);
 }
 
-static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4,
-					   const struct sock *sk, __be32 daddr)
+static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
+					   const struct sock *sk,
+					   __be32 daddr)
 {
 	memset(fl4, 0, sizeof(*fl4));
 	fl4->flowi4_oif		= sk->sk_bound_dev_if;
@@ -363,7 +366,7 @@ static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4,
 	fl4->flowi4_tos		= RT_CONN_FLAGS(sk);
 	fl4->flowi4_proto	= sk->sk_protocol;
 
-	return ip_route_output_key(net, fl4);
+	return ip_route_output_key(sock_net(sk), fl4);
 }
 
 static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
@@ -452,7 +455,6 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	struct rtable *rt;
 	struct flowi4 fl4;
 	struct iphdr *iph;
-	struct sock *sk;
 	__be16 df;
 	int mtu;
 
@@ -468,30 +470,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	}
 	netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-	switch (pctx->gtp_version) {
-	case GTP_V0:
-		if (gtp->sock0)
-			sk = gtp->sock0->sk;
-		else
-			sk = NULL;
-		break;
-	case GTP_V1:
-		if (gtp->sock1u)
-			sk = gtp->sock1u->sk;
-		else
-			sk = NULL;
-		break;
-	default:
-		return -ENOENT;
-	}
-
-	if (!sk) {
-		netdev_dbg(dev, "no userspace socket is available, skip\n");
-		return -ENOENT;
-	}
-
-	rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk,
-				  pctx->sgsn_addr_ip4.s_addr);
+	rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr);
 	if (IS_ERR(rt)) {
 		netdev_dbg(dev, "no route to SSGN %pI4\n",
 			   &pctx->sgsn_addr_ip4.s_addr);
@@ -536,7 +515,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 		goto err_rt;
 	}
 
-	gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev);
+	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
 	gtp_push_header(skb, pktinfo);
 
 	return 0;
@@ -906,7 +885,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 	}
 }
 
-static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
+static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
+			struct genl_info *info)
 {
 	struct gtp_dev *gtp = netdev_priv(dev);
 	u32 hash_ms, hash_tid = 0;
@@ -947,6 +927,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info)
 	if (pctx == NULL)
 		return -ENOMEM;
 
+	sock_hold(sk);
+	pctx->sk = sk;
 	ipv4_pdp_fill(pctx, info);
 	atomic_set(&pctx->tx_seq, 0);
 
@@ -987,6 +969,7 @@ static void pdp_context_free(struct rcu_head *head)
 {
 	struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head);
 
+	sock_put(pctx->sk);
 	kfree(pctx);
 }
 
@@ -997,10 +980,27 @@ static void pdp_context_delete(struct pdp_ctx *pctx)
 	call_rcu(&pctx->rcu_head, pdp_context_free);
 }
 
+static struct socket *gtp_genl_new_pdp_select_socket(int version,
+						     struct net_device *dev)
+{
+	struct gtp_dev *gtp = netdev_priv(dev);
+
+	switch (version) {
+	case GTP_V0:
+		return gtp->sock0;
+	case GTP_V1:
+		return gtp->sock1u;
+	default:
+		return NULL;
+	}
+}
+
 static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 {
+	unsigned int version;
 	struct net_device *dev;
 	struct net *net;
+	struct socket *sock;
 
 	if (!info->attrs[GTPA_VERSION] ||
 	    !info->attrs[GTPA_LINK] ||
@@ -1008,7 +1008,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 	    !info->attrs[GTPA_MS_ADDRESS])
 		return -EINVAL;
 
-	switch (nla_get_u32(info->attrs[GTPA_VERSION])) {
+	version = nla_get_u32(info->attrs[GTPA_VERSION]);
+
+	switch (version) {
 	case GTP_V0:
 		if (!info->attrs[GTPA_TID] ||
 		    !info->attrs[GTPA_FLOW])
@@ -1036,7 +1038,11 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 	}
 	put_net(net);
 
-	return ipv4_pdp_add(dev, info);
+	sock = gtp_genl_new_pdp_select_socket(version, dev);
+	if (!sock)
+		return -ENODEV;
+
+	return ipv4_pdp_add(dev, sock->sk, info);
 }
 
 static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb,
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 11/18] gtp: consolidate gtp socket rx path
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Add network device to gtp context in preparation for splitting
the TEID from the network device.

Use this to rework the socker rx path. Move the common RX part
of v0 and v1 into a helper. Also move the final rx part into
that helper as well.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 92 ++++++++++++++++++++++++++++---------------------------
 1 file changed, 47 insertions(+), 45 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index fb93468..ea97b49 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -58,6 +58,7 @@ struct pdp_ctx {
 	struct in_addr		ms_addr_ip4;
 	struct in_addr		sgsn_addr_ip4;
 
+	struct net_device       *dev;
 	struct sock		*sk;
 
 	atomic_t		tx_seq;
@@ -179,9 +180,42 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
 	return false;
 }
 
+static int gtp_rx(struct sk_buff *skb, struct pdp_ctx *pctx, unsigned int hdrlen)
+{
+	struct pcpu_sw_netstats *stats;
+
+	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
+		pr_debug("No PDP ctx for this MS\n");
+		return -1;
+	}
+
+	/* Get rid of the GTP + UDP headers. */
+	if (iptunnel_pull_header(skb, hdrlen, skb->protocol,
+				 !net_eq(sock_net(pctx->sk), dev_net(pctx->dev))))
+		return -1;
+
+	pr_debug("forwarding packet from GGSN to uplink\n");
+
+	/* Now that the UDP and the GTP header have been removed, set up the
+	 * new network header. This is required by the upper layer to
+	 * calculate the transport header.
+	 */
+	skb_reset_network_header(skb);
+
+	skb->dev = pctx->dev;
+
+	stats = this_cpu_ptr(pctx->dev->tstats);
+	u64_stats_update_begin(&stats->syncp);
+	stats->rx_packets++;
+	stats->rx_bytes += skb->len;
+	u64_stats_update_end(&stats->syncp);
+
+	netif_rx(skb);
+	return 0;
+}
+
 /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
-static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
-			       bool xnet)
+static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 {
 	unsigned int hdrlen = sizeof(struct udphdr) +
 			      sizeof(struct gtp0_header);
@@ -205,17 +239,10 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 		return -1;
 	}
 
-	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
-		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		return -1;
-	}
-
-	/* Get rid of the GTP + UDP headers. */
-	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
+	return gtp_rx(skb, pctx, hdrlen);
 }
 
-static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
-				bool xnet)
+static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 {
 	unsigned int hdrlen = sizeof(struct udphdr) +
 			      sizeof(struct gtp1_header);
@@ -254,13 +281,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 		return -1;
 	}
 
-	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
-		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		return -1;
-	}
-
-	/* Get rid of the GTP + UDP headers. */
-	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
+	return gtp_rx(skb, pctx, hdrlen);
 }
 
 /* UDP encapsulation receive handler. See net/ipv4/udp.c.
@@ -268,10 +289,8 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
  */
 static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
-	struct pcpu_sw_netstats *stats;
 	struct gtp_dev *gtp;
-	bool xnet;
-	int ret;
+	int ret = 0;
 
 	gtp = rcu_dereference_sk_user_data(sk);
 	if (!gtp)
@@ -279,16 +298,14 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 
 	netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
 
-	xnet = !net_eq(sock_net(sk), dev_net(gtp->dev));
-
 	switch (udp_sk(sk)->encap_type) {
 	case UDP_ENCAP_GTP0:
 		netdev_dbg(gtp->dev, "received GTP0 packet\n");
-		ret = gtp0_udp_encap_recv(gtp, skb, xnet);
+		ret = gtp0_udp_encap_recv(gtp, skb);
 		break;
 	case UDP_ENCAP_GTP1U:
 		netdev_dbg(gtp->dev, "received GTP1U packet\n");
-		ret = gtp1u_udp_encap_recv(gtp, skb, xnet);
+		ret = gtp1u_udp_encap_recv(gtp, skb);
 		break;
 	default:
 		ret = -1; /* Shouldn't happen. */
@@ -297,33 +314,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	switch (ret) {
 	case 1:
 		netdev_dbg(gtp->dev, "pass up to the process\n");
-		return 1;
+		break;
 	case 0:
-		netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
 		break;
 	case -1:
 		netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
 		kfree_skb(skb);
-		return 0;
+		ret = 0;
+		break;
 	}
 
-	/* Now that the UDP and the GTP header have been removed, set up the
-	 * new network header. This is required by the upper layer to
-	 * calculate the transport header.
-	 */
-	skb_reset_network_header(skb);
-
-	skb->dev = gtp->dev;
-
-	stats = this_cpu_ptr(gtp->dev->tstats);
-	u64_stats_update_begin(&stats->syncp);
-	stats->rx_packets++;
-	stats->rx_bytes += skb->len;
-	u64_stats_update_end(&stats->syncp);
-
-	netif_rx(skb);
-
-	return 0;
+	return ret;
 }
 
 static void gtp_encap_destroy(struct sock *sk)
@@ -929,6 +930,7 @@ static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
 
 	sock_hold(sk);
 	pctx->sk = sk;
+	pctx->dev = dev;
 	ipv4_pdp_fill(pctx, info);
 	atomic_set(&pctx->tx_seq, 0);
 
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 18/18] gtp: add dst_cache support
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 4637ce7..c3e40cc 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -61,6 +61,10 @@ struct pdp_ctx {
 	struct net_device       *dev;
 	struct sock		*sk;
 
+#ifdef CONFIG_DST_CACHE
+	struct dst_cache        dst_cache;
+#endif
+
 	atomic_t		tx_seq;
 	struct rcu_head		rcu_head;
 };
@@ -485,12 +489,18 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	}
 	netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-	rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to SSGN %pI4\n",
-			   &pctx->sgsn_addr_ip4.s_addr);
-		dev->stats.tx_carrier_errors++;
-		goto err;
+	rt = dst_cache_get_ip4(&pctx->dst_cache, &pctx->sgsn_addr_ip4.s_addr);
+	if (!rt) {
+		rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr);
+		if (IS_ERR(rt)) {
+			netdev_dbg(dev, "no route to SSGN %pI4\n",
+				   &pctx->sgsn_addr_ip4.s_addr);
+			dev->stats.tx_carrier_errors++;
+			goto err;
+		}
+
+		dst_cache_set_ip4(&pctx->dst_cache, &rt->dst,
+				  pctx->sgsn_addr_ip4.s_addr);
 	}
 
 	if (rt->dst.dev == dev) {
@@ -883,6 +893,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 	pctx->ms_addr_ip4.s_addr =
 		nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 
+	dst_cache_reset(&pctx->dst_cache);
+
 	switch (pctx->gtp_version) {
 	case GTP_V0:
 		/* According to TS 09.60, sections 7.5.1 and 7.5.2, the flow
@@ -910,6 +922,7 @@ static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
 	struct pdp_ctx *pctx;
 	bool found = false;
 	__be32 ms_addr;
+	int err;
 
 	gsk = rcu_dereference_sk_user_data(sk);
 	if (!gsk)
@@ -948,6 +961,12 @@ static int ipv4_pdp_add(struct net_device *dev, struct sock *sk,
 	if (pctx == NULL)
 		return -ENOMEM;
 
+	err = dst_cache_init(&pctx->dst_cache, GFP_KERNEL);
+	if (err) {
+		kfree(pctx);
+		return err;
+	}
+
 	sock_hold(sk);
 	pctx->sk = sk;
 	pctx->dev = dev;
@@ -992,6 +1011,7 @@ static void pdp_context_free(struct rcu_head *head)
 	struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head);
 
 	sock_put(pctx->sk);
+	dst_cache_destroy(&pctx->dst_cache);
 	kfree(pctx);
 }
 
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 17/18] gtp: add support to select a GTP socket during PDP context creation
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index c6c1f0d..4637ce7 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1023,6 +1023,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 	struct net_device *dev;
 	struct net *net;
 	struct socket *sock;
+	int err;
 
 	if (!info->attrs[GTPA_VERSION] ||
 	    !info->attrs[GTPA_LINK] ||
@@ -1060,11 +1061,19 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)
 	}
 	put_net(net);
 
-	sock = gtp_genl_new_pdp_select_socket(version, dev);
-	if (!sock)
+	if (info->attrs[GTPA_FD])
+		sock = sockfd_lookup(nla_get_u32(info->attrs[GTPA_FD]), &err);
+	else
+		sock = gtp_genl_new_pdp_select_socket(version, dev);
+	if (!sock || !sock->sk)
 		return -ENODEV;
 
-	return ipv4_pdp_add(dev, sock->sk, info);
+	err = ipv4_pdp_add(dev, sock->sk, info);
+
+	if (info->attrs[GTPA_FD])
+		sockfd_put(sock);
+
+	return err;
 }
 
 static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb,
@@ -1096,11 +1105,64 @@ static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb,
 	return ipv4_pdp_find(gtp, ms_addr);
 }
 
+static struct pdp_ctx *gtp_genl_find_pdp_by_socket(struct sk_buff *skb,
+						   struct genl_info *info)
+{
+	struct socket *sock;
+	struct gtp_sock *gsk;
+	struct pdp_ctx *pctx;
+	int fd, err = 0;
+
+	if (!info->attrs[GTPA_FD])
+		return ERR_PTR(-EINVAL);
+
+	fd = nla_get_u32(info->attrs[GTPA_FD]);
+	sock = sockfd_lookup(fd, &err);
+	if (!sock) {
+		pr_debug("gtp socket fd=%d not found\n", fd);
+		return ERR_PTR(-EBADF);
+	}
+
+	gsk = rcu_dereference_sk_user_data(sock->sk);
+	if (!gsk) {
+		pctx = ERR_PTR(-EINVAL);
+		goto out_sock;
+	}
+
+	switch (nla_get_u32(info->attrs[GTPA_VERSION])) {
+	case GTP_V0:
+		if (!info->attrs[GTPA_TID]) {
+			pctx = ERR_PTR(-EINVAL);
+			break;
+		}
+		pctx = gtp0_pdp_find(gsk, nla_get_u64(info->attrs[GTPA_TID]));
+		break;
+
+	case GTP_V1:
+		if (!info->attrs[GTPA_I_TEI]) {
+			pctx = ERR_PTR(-EINVAL);
+			break;
+		}
+		pctx = gtp1_pdp_find(gsk, nla_get_u64(info->attrs[GTPA_I_TEI]));
+		break;
+
+	default:
+		pctx = ERR_PTR(-EINVAL);
+		break;
+	}
+
+out_sock:
+	sockfd_put(sock);
+	return pctx;
+}
+
 static struct pdp_ctx *gtp_genl_find_pdp(struct sk_buff *skb,
 					 struct genl_info *info)
 {
 	if (info->attrs[GTPA_LINK])
 		return gtp_genl_find_pdp_by_link(skb, info);
+	else if (info->attrs[GTPA_FD])
+		return gtp_genl_find_pdp_by_socket(skb, info);
 	else
 		return ERR_PTR(-EINVAL);
 }
-- 
2.10.2

^ permalink raw reply related

* [PATCH v2 12/18] gtp: let userspace handle packets for invalid tunnels
From: Andreas Schultz @ 2017-01-24 15:28 UTC (permalink / raw)
  To: Pablo Neira; +Cc: netdev, Lionel Gauthier, openbsc, Harald Welte
In-Reply-To: <20170124152848.6120-1-aschultz@tpip.net>

enable userspace to send error replies for invalid tunnels

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index ea97b49..8d74b7d 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -186,7 +186,7 @@ static int gtp_rx(struct sk_buff *skb, struct pdp_ctx *pctx, unsigned int hdrlen
 
 	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
 		pr_debug("No PDP ctx for this MS\n");
-		return -1;
+		return 1;
 	}
 
 	/* Get rid of the GTP + UDP headers. */
@@ -236,7 +236,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
 	if (IS_ERR(pctx)) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-		return -1;
+		return 1;
 	}
 
 	return gtp_rx(skb, pctx, hdrlen);
@@ -278,7 +278,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
 	if (IS_ERR(pctx)) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-		return -1;
+		return 1;
 	}
 
 	return gtp_rx(skb, pctx, hdrlen);
-- 
2.10.2

^ permalink raw reply related

* pull-request: mac80211 2017-01-24
From: Johannes Berg @ 2017-01-24 15:34 UTC (permalink / raw)
  To: David Miller
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

Hi Dave,

LTP found a problem last week, but I couldn't fix it since I
was travelling, so here's the fix now.

Please pull and let me know if there's any problem.

Thanks,
johannes



The following changes since commit 4078b76cac68e50ccf1f76a74e7d3d5788aec3fe:

  net: dsa: Check return value of phy_connect_direct() (2017-01-23 15:43:35 -0500)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git tags/mac80211-for-davem-2017-01-24

for you to fetch changes up to 115865fa0826ed18ca04717cf72d0fe874c0fe7f:

  mac80211: don't try to sleep in rate_control_rate_init() (2017-01-24 16:31:54 +0100)

----------------------------------------------------------------
A single fix, for a sleeping context problem found by LTP.

----------------------------------------------------------------
Johannes Berg (1):
      mac80211: don't try to sleep in rate_control_rate_init()

 net/mac80211/rate.c | 2 --
 1 file changed, 2 deletions(-)

^ permalink raw reply

* [PATCH 0/2] net: phy: leds: Fix truncated LED trigger names and crashes
From: Geert Uytterhoeven @ 2017-01-24 15:41 UTC (permalink / raw)
  To: David S. Miller, Zach Brown, Volodymyr Bendiuga
  Cc: Andrew Lunn, Magnus Öberg, Florian Fainelli, netdev,
	linux-renesas-soc, linux-kernel, Geert Uytterhoeven

	Hi David,

I started seeing crashes during s2ram and poweroff on all my ARM boards,
like:

    Unable to handle kernel NULL pointer dereference at virtual address 00000000
    ...
    [<c04116d4>] (__list_del_entry_valid) from [<c05e8948>] (led_trigger_unregister+0x34/0xcc)
    [<c05e8948>] (led_trigger_unregister) from [<c05336c4>] (phy_led_triggers_unregister+0x28/0x34)
    [<c05336c4>] (phy_led_triggers_unregister) from [<c0531d44>] (phy_detach+0x30/0x74)
    [<c0531d44>] (phy_detach) from [<c0538bdc>] (sh_eth_close+0x64/0x9c)
    [<c0538bdc>] (sh_eth_close) from [<c04d4ce0>] (dpm_run_callback+0x48/0xc8)

or:

    list_del corruption. prev->next should be dede6540, but was 2e323931
    ------------[ cut here ]------------
    kernel BUG at lib/list_debug.c:52!
    ...
    [<c02f6d70>] (__list_del_entry_valid) from [<c0425168>] (led_trigger_unregister+0x34/0xcc)
    [<c0425168>] (led_trigger_unregister) from [<c03a05a0>] (phy_led_triggers_unregister+0x28/0x34)
    [<c03a05a0>] (phy_led_triggers_unregister) from [<c039ec04>] (phy_detach+0x30/0x74)
    [<c039ec04>] (phy_detach) from [<c03a4fc0>] (sh_eth_close+0x6c/0xa4)
    [<c03a4fc0>] (sh_eth_close) from [<c0483234>] (__dev_close_many+0xac/0xd0)

As the only clue was a kernel message like

    sh-eth ee700000.ethernet eth0: No phy led trigger registered for speed(100)

I had to bisected this, leading to commit 4567d686f5c6d955 ("phy:
increase size of MII_BUS_ID_SIZE and bus_id").  Reverting that commit
fixed the issue.

More investigation revealed the crashes are due to the combination of
two things:
  - Truncated LED trigger names, leading to duplicate names, and
    registration failures,
  - Bad error handling in case of registration failures.

Both are fixed by this patch series.

Thanks!

Geert Uytterhoeven (2):
  net: phy: leds: Clear phy_num_led_triggers on failure to avoid crash
  net: phy: leds: Fix truncated LED trigger names

 drivers/net/phy/phy_led_triggers.c | 8 ++++++--
 include/linux/phy.h                | 3 ++-
 include/linux/phy_led_triggers.h   | 3 +--
 3 files changed, 9 insertions(+), 5 deletions(-)

-- 
1.9.1

Gr{oetje,eeting}s,

						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
							    -- Linus Torvalds

^ permalink raw reply

* [PATCH 1/2] net: phy: leds: Clear phy_num_led_triggers on failure to avoid crash
From: Geert Uytterhoeven @ 2017-01-24 15:41 UTC (permalink / raw)
  To: David S. Miller, Zach Brown, Volodymyr Bendiuga
  Cc: Andrew Lunn, Magnus Öberg, Florian Fainelli, netdev,
	linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1485272470-3497-1-git-send-email-geert+renesas@glider.be>

phy_attach_direct() ignores errors returned by
phy_led_triggers_register(). I think that's OK, as LED triggers can be
considered a non-critical feature.

However, this causes problems later:
  - phy_led_trigger_change_speed() will access the array
    phy_device.phy_led_triggers, which has been freed in the error path
    of phy_led_triggers_register(), which may lead to a crash.

  - phy_led_triggers_unregister() will access the same array, leading to
    crashes during s2ram or poweroff, like:

	Unable to handle kernel NULL pointer dereference at virtual address
	00000000
	...
	[<c04116d4>] (__list_del_entry_valid) from [<c05e8948>] (led_trigger_unregister+0x34/0xcc)
	[<c05e8948>] (led_trigger_unregister) from [<c05336c4>] (phy_led_triggers_unregister+0x28/0x34)
	[<c05336c4>] (phy_led_triggers_unregister) from [<c0531d44>] (phy_detach+0x30/0x74)
	[<c0531d44>] (phy_detach) from [<c0538bdc>] (sh_eth_close+0x64/0x9c)
	[<c0538bdc>] (sh_eth_close) from [<c04d4ce0>] (dpm_run_callback+0x48/0xc8)

    or:

	list_del corruption. prev->next should be dede6540, but was 2e323931
	------------[ cut here ]------------
	kernel BUG at lib/list_debug.c:52!
	...
	[<c02f6d70>] (__list_del_entry_valid) from [<c0425168>] (led_trigger_unregister+0x34/0xcc)
	[<c0425168>] (led_trigger_unregister) from [<c03a05a0>] (phy_led_triggers_unregister+0x28/0x34)
	[<c03a05a0>] (phy_led_triggers_unregister) from [<c039ec04>] (phy_detach+0x30/0x74)
	[<c039ec04>] (phy_detach) from [<c03a4fc0>] (sh_eth_close+0x6c/0xa4)
	[<c03a4fc0>] (sh_eth_close) from [<c0483234>] (__dev_close_many+0xac/0xd0)

To fix this, clear phy_device.phy_num_led_triggers in the error path of
phy_led_triggers_register() fails.

Note that the "No phy led trigger registered for speed" message will
still be printed on link speed changes, which is a good cue that
something went wrong with the LED triggers.

Fixes: 2e0bc452f4721520 ("net: phy: leds: add support for led triggers on phy link state change")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Alternatively, phy_attach_direct() could consider
phy_led_triggers_register() failures as fatal, so
phy_led_trigger_change_speed() and phy_led_triggers_unregister() are
never called afterwards.

Exposed by commit 4567d686f5c6d955 ("phy: increase size of
MII_BUS_ID_SIZE and bus_id"), which caused duplicate trigger names.
---
 drivers/net/phy/phy_led_triggers.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c
index fa62bdf2f52694de..3f619e7371e97d8a 100644
--- a/drivers/net/phy/phy_led_triggers.c
+++ b/drivers/net/phy/phy_led_triggers.c
@@ -102,8 +102,10 @@ int phy_led_triggers_register(struct phy_device *phy)
 					    sizeof(struct phy_led_trigger) *
 						   phy->phy_num_led_triggers,
 					    GFP_KERNEL);
-	if (!phy->phy_led_triggers)
-		return -ENOMEM;
+	if (!phy->phy_led_triggers) {
+		err = -ENOMEM;
+		goto out_clear;
+	}
 
 	for (i = 0; i < phy->phy_num_led_triggers; i++) {
 		err = phy_led_trigger_register(phy, &phy->phy_led_triggers[i],
@@ -120,6 +122,8 @@ int phy_led_triggers_register(struct phy_device *phy)
 	while (i--)
 		phy_led_trigger_unregister(&phy->phy_led_triggers[i]);
 	devm_kfree(&phy->mdio.dev, phy->phy_led_triggers);
+out_clear:
+	phy->phy_num_led_triggers = 0;
 	return err;
 }
 EXPORT_SYMBOL_GPL(phy_led_triggers_register);
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] net: phy: leds: Fix truncated LED trigger names
From: Geert Uytterhoeven @ 2017-01-24 15:41 UTC (permalink / raw)
  To: David S. Miller, Zach Brown, Volodymyr Bendiuga
  Cc: Andrew Lunn, Magnus Öberg, Florian Fainelli, netdev,
	linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <1485272470-3497-1-git-send-email-geert+renesas@glider.be>

Commit 4567d686f5c6d955 ("phy: increase size of MII_BUS_ID_SIZE and
bus_id") increased the size of MII bus IDs, but forgot to update the
private definition in <linux/phy_led_triggers.h>.
This may cause:
  1. Truncation of LED trigger names,
  2. Duplicate LED trigger names,
  3. Failures registering LED triggers,
  4. Crashes due to bad error handling in the LED trigger failure path.

To fix this, and prevent the definitions going out of sync again in the
future, let the PHY LED trigger code use the existing MII_BUS_ID_SIZE
definition. Note that this requires moving the #include down, after the
definition of MII_BUS_ID_SIZE.

Example:
  - Before I had triggers "ee700000.etherne:01:100Mbps" and
    "ee700000.etherne:01:10Mbps",
  - After the increase of MII_BUS_ID_SIZE, both became
    "ee700000.ethernet-ffffffff:01:" => FAIL,
  - Now, the triggers are "ee700000.ethernet-ffffffff:01:100Mbps" and
    "ee700000.ethernet-ffffffff:01:10Mbps", which are unique again.

Fixes: 4567d686f5c6d955 ("phy: increase size of MII_BUS_ID_SIZE and bus_id")
Fixes: 2e0bc452f4721520 ("net: phy: leds: add support for led triggers on phy link state change")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 include/linux/phy.h              | 3 ++-
 include/linux/phy_led_triggers.h | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5c9d2529685fe215..f6ab919528ab3627 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -25,7 +25,6 @@
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/mod_devicetable.h>
-#include <linux/phy_led_triggers.h>
 
 #include <linux/atomic.h>
 
@@ -339,6 +338,8 @@ struct phy_c45_device_ids {
 	u32 device_ids[8];
 };
 
+#include <linux/phy_led_triggers.h>
+
 /* phy_device: An instance of a PHY
  *
  * drv: Pointer to the driver for this PHY instance
diff --git a/include/linux/phy_led_triggers.h b/include/linux/phy_led_triggers.h
index a2daea0a37d2ae14..69dffb4fc5a294e9 100644
--- a/include/linux/phy_led_triggers.h
+++ b/include/linux/phy_led_triggers.h
@@ -20,9 +20,8 @@
 #include <linux/leds.h>
 
 #define PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE	10
-#define PHY_MII_BUS_ID_SIZE	(20 - 3)
 
-#define PHY_LINK_LED_TRIGGER_NAME_SIZE (PHY_MII_BUS_ID_SIZE + \
+#define PHY_LINK_LED_TRIGGER_NAME_SIZE (MII_BUS_ID_SIZE + \
 				       FIELD_SIZEOF(struct mdio_device, addr)+\
 				       PHY_LED_TRIGGER_SPEED_SUFFIX_SIZE)
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 1/2] mac80211: use helper function to access ieee802_1d_to_ac[]
From: Amadeusz Sławiński @ 2017-01-24 15:42 UTC (permalink / raw)
  To: linux-wireless, Johannes Berg
  Cc: David S. Miller, netdev, linux-kernel,
	Amadeusz Sławiński

cleanup patch to make use of ieee80211_ac_from_tid() to retrieve ac from
ieee802_1d_to_ac[]

Signed-off-by: Amadeusz Sławiński <amadeusz.slawinski@tieto.com>
---
 net/mac80211/rx.c     | 2 +-
 net/mac80211/status.c | 2 +-
 net/mac80211/tx.c     | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3e289a6..cf8ed1c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1391,7 +1391,7 @@ void ieee80211_sta_pspoll(struct ieee80211_sta *pubsta)
 void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid)
 {
 	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
-	u8 ac = ieee802_1d_to_ac[tid & 7];
+	int ac = ieee80211_ac_from_tid(tid);
 
 	/*
 	 * If this AC is not trigger-enabled do nothing unless the
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index ddf71c6..dd48de0 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -95,7 +95,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
 		 */
 		if (*p & IEEE80211_QOS_CTL_EOSP)
 			*p &= ~IEEE80211_QOS_CTL_EOSP;
-		ac = ieee802_1d_to_ac[tid & 7];
+		ac = ieee80211_ac_from_tid(tid);
 	} else {
 		ac = IEEE80211_AC_BE;
 	}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 0d8b716..009eb36 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1410,7 +1410,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
 		txqi->txq.sta = &sta->sta;
 		sta->sta.txq[tid] = &txqi->txq;
 		txqi->txq.tid = tid;
-		txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+		txqi->txq.ac = ieee80211_ac_from_tid(tid);
 	} else {
 		sdata->vif.txq = &txqi->txq;
 		txqi->txq.tid = 0;
@@ -4542,7 +4542,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 				 struct sk_buff *skb, int tid,
 				 enum nl80211_band band)
 {
-	int ac = ieee802_1d_to_ac[tid & 7];
+	int ac = ieee80211_ac_from_tid(tid);
 
 	skb_reset_mac_header(skb);
 	skb_set_queue_mapping(skb, ac);
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] mac80211: use accessor functions to set sta->_flags
From: Amadeusz Sławiński @ 2017-01-24 15:42 UTC (permalink / raw)
  To: linux-wireless, Johannes Berg
  Cc: David S. Miller, netdev, linux-kernel,
	Amadeusz Sławiński
In-Reply-To: <1485272531-11587-1-git-send-email-amadeusz.slawinski@tieto.com>

cleanup patch to make use of set_sta_flag()/clear_sta_flag() in places
where we access sta->_flags

Signed-off-by: Amadeusz Sławiński <amadeusz.slawinski@tieto.com>
---
 net/mac80211/sta_info.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b6cfcf0..6c9cc2f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1855,13 +1855,13 @@ int sta_info_move_state(struct sta_info *sta,
 	switch (new_state) {
 	case IEEE80211_STA_NONE:
 		if (sta->sta_state == IEEE80211_STA_AUTH)
-			clear_bit(WLAN_STA_AUTH, &sta->_flags);
+			clear_sta_flag(sta, WLAN_STA_AUTH);
 		break;
 	case IEEE80211_STA_AUTH:
 		if (sta->sta_state == IEEE80211_STA_NONE) {
-			set_bit(WLAN_STA_AUTH, &sta->_flags);
+			set_sta_flag(sta, WLAN_STA_AUTH);
 		} else if (sta->sta_state == IEEE80211_STA_ASSOC) {
-			clear_bit(WLAN_STA_ASSOC, &sta->_flags);
+			clear_sta_flag(sta, WLAN_STA_ASSOC);
 			ieee80211_recalc_min_chandef(sta->sdata);
 			if (!sta->sta.support_p2p_ps)
 				ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
@@ -1869,13 +1869,13 @@ int sta_info_move_state(struct sta_info *sta,
 		break;
 	case IEEE80211_STA_ASSOC:
 		if (sta->sta_state == IEEE80211_STA_AUTH) {
-			set_bit(WLAN_STA_ASSOC, &sta->_flags);
+			set_sta_flag(sta, WLAN_STA_ASSOC);
 			ieee80211_recalc_min_chandef(sta->sdata);
 			if (!sta->sta.support_p2p_ps)
 				ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
 		} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
 			ieee80211_vif_dec_num_mcast(sta->sdata);
-			clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+			clear_sta_flag(sta, WLAN_STA_AUTHORIZED);
 			ieee80211_clear_fast_xmit(sta);
 			ieee80211_clear_fast_rx(sta);
 		}
@@ -1883,7 +1883,7 @@ int sta_info_move_state(struct sta_info *sta,
 	case IEEE80211_STA_AUTHORIZED:
 		if (sta->sta_state == IEEE80211_STA_ASSOC) {
 			ieee80211_vif_inc_num_mcast(sta->sdata);
-			set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+			set_sta_flag(sta, WLAN_STA_AUTHORIZED);
 			ieee80211_check_fast_xmit(sta);
 			ieee80211_check_fast_rx(sta);
 		}
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH net] net: reset ct before calling ndo_start_xmit
From: Eric Dumazet @ 2017-01-24 15:42 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: netdev, David S. Miller, Hannes Frederic Sowa, Florian Westphal
In-Reply-To: <1485270249.2409.10.camel@redhat.com>

On Tue, 2017-01-24 at 16:04 +0100, Paolo Abeni wrote:

> Double checking to see if I understood the above correctly: do you
> suggest to call nf_reset() from the affected drivers's
> ndo_features_check(), eventually adding such ndo if needed ?
> 
> I think calling nf_reset() in the common code should be better: the
> conntrack entry is hot in the cache and we may want to clear it early
> for as many devices as possible.

This would add a conditional test, which will be not correctly predicted
by CPU in tunnel or bonding/team very common cases.

While doing it in an affected driver would not penalize fast path.

A little bit harder sure, but performance matters ;)

^ permalink raw reply

* Re: [PATCH 2/2] mac80211: use accessor functions to set sta->_flags
From: Johannes Berg @ 2017-01-24 15:44 UTC (permalink / raw)
  To: Amadeusz Sławiński, linux-wireless
  Cc: David S. Miller, netdev, linux-kernel
In-Reply-To: <1485272531-11587-2-git-send-email-amadeusz.slawinski@tieto.com>

On Tue, 2017-01-24 at 16:42 +0100, Amadeusz Sławiński wrote:
> cleanup patch to make use of set_sta_flag()/clear_sta_flag() in
> places
> where we access sta->_flags
> 
> Signed-off-by: Amadeusz Sławiński <amadeusz.slawinski@tieto.com>
> ---
>  net/mac80211/sta_info.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
> index b6cfcf0..6c9cc2f 100644
> --- a/net/mac80211/sta_info.c
> +++ b/net/mac80211/sta_info.c
> @@ -1855,13 +1855,13 @@ int sta_info_move_state(struct sta_info *sta,
>  	switch (new_state) {
>  	case IEEE80211_STA_NONE:
>  		if (sta->sta_state == IEEE80211_STA_AUTH)
> -			clear_bit(WLAN_STA_AUTH, &sta->_flags);
> +			clear_sta_flag(sta, WLAN_STA_AUTH);

You should try to run this patch sometime :)

johannes

^ permalink raw reply

* Re: [PATCH 1/3] sh_eth: rename EESIPR bits
From: Sergei Shtylyov @ 2017-01-24 15:47 UTC (permalink / raw)
  To: Geert Uytterhoeven; +Cc: netdev@vger.kernel.org, Linux-Renesas
In-Reply-To: <CAMuHMdVS4C37FGtB+eCytz-=aXSjGB7vYGUCnLG=rKN0L1io7g@mail.gmail.com>

Hello!

On 01/23/2017 10:39 AM, Geert Uytterhoeven wrote:

>> Since the  commit  b0ca2a21f769 ("sh_eth: Add support of SH7763 to sh_eth")
>> the *enum* declaring the EESIPR bits (interrupt mask) went out of sync with
>> the *enum* declaring the EESR bits (interrupt status) WRT  bit naming  and
>> formatting. I'd like to restore the consistency by using EESIPR as the bit
>> name prefix, renaming the *enum* to EESIPR_BIT, and (finally) renaming the
>> bits according to the available  Renesas SH77{34|63} manuals...
>
> Which versions of the SH77{34|63} manuals did you use?
> Several registers are called slightly different in mine, and also in my
> r8a7740 manual.

    SH7734 User's Manual: Hardware, Rev.1.00 Jun 2012.
    SH7763 User’s Manual: Hardware, Rev.3.00 Mar 2012

>> --- net-next.orig/drivers/net/ethernet/renesas/sh_eth.h
>> +++ net-next/drivers/net/ethernet/renesas/sh_eth.h
>> @@ -268,19 +268,29 @@ enum EESR_BIT {
>>                                  EESR_TFE | EESR_TDE)
>>
>>  /* EESIPR */
>> -enum DMAC_IM_BIT {
>> -       DMAC_M_TWB = 0x40000000, DMAC_M_TABT = 0x04000000,
>> -       DMAC_M_RABT = 0x02000000,
>> -       DMAC_M_RFRMER = 0x01000000, DMAC_M_ADF = 0x00800000,
>> -       DMAC_M_ECI = 0x00400000, DMAC_M_FTC = 0x00200000,
>> -       DMAC_M_TDE = 0x00100000, DMAC_M_TFE = 0x00080000,
>> -       DMAC_M_FRC = 0x00040000, DMAC_M_RDE = 0x00020000,
>> -       DMAC_M_RFE = 0x00010000, DMAC_M_TINT4 = 0x00000800,
>> -       DMAC_M_TINT3 = 0x00000400, DMAC_M_TINT2 = 0x00000200,
>> -       DMAC_M_TINT1 = 0x00000100, DMAC_M_RINT8 = 0x00000080,
>> -       DMAC_M_RINT5 = 0x00000010, DMAC_M_RINT4 = 0x00000008,
>> -       DMAC_M_RINT3 = 0x00000004, DMAC_M_RINT2 = 0x00000002,
>> -       DMAC_M_RINT1 = 0x00000001,
>> +enum EESIPR_BIT {
>> +       EESIPR_TWBIP    = 0x40000000,
>
> TWBIP is actually two bits in my manual: TWB1IP and TWB0IP

    Yes. I'm adding TWB1 in the next patch.

>> +       EESIPR_ADEIP    = 0x00800000,
>
> Nonexistent bit in my manual.

    In mine as well. I used EESR *enum* as a source of info in this case.
Note that this bit is documented for R-Car gen2 (but called differently).

>> +       EESIPR_CNDIP    = 0x00000800,
>
> Nonexistent bit in my manual.

    I used the EESR *enum* again.

> Gr{oetje,eeting}s,
>
>                         Geert

MBR, Sergei

^ permalink raw reply

* Re: [RFC PATCH net-next 0/5] bridge: per vlan lwt and dst_metadata support
From: Stephen Hemminger @ 2017-01-24 15:47 UTC (permalink / raw)
  To: Roopa Prabhu
  Cc: netdev, davem, nikolay, tgraf, hannes, jbenc, pshelar, dsa, hadi
In-Reply-To: <1484977616-1541-1-git-send-email-roopa@cumulusnetworks.com>

On Fri, 20 Jan 2017 21:46:51 -0800
Roopa Prabhu <roopa@cumulusnetworks.com> wrote:

> 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(-)
> 

Yes this is a complex issue, but I am concerned about the added code bloat
and complexity. The bridge which is already a mess with netfilter, multicast, vlan
filtering etc.The current code not modular and grows with each feature.
At the same time, the same bridge functionality is used in its simplest form by
all the network virtualization and container technolgies.

Maybe do it in OpenVswitch or figure out a way to do customization with BPF?

more complex than it should

^ 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