Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v3 7/9] ipv6: add source address argument for ipv6_push_nfrag_opts
From: David Lebrun @ 2016-10-27 13:35 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch prepares for insertion of SRH through setsockopt().
The new source address argument is used when an HMAC field is
present in the SRH, which must be filled. The HMAC signature
process requires the source address as input text.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/net/ipv6.h    | 3 ++-
 net/ipv6/exthdrs.c    | 6 +++---
 net/ipv6/ip6_output.c | 5 +++--
 net/ipv6/ip6_tunnel.c | 2 +-
 4 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 8fed1cd..0a3622b 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -932,7 +932,8 @@ int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
  */
 
 void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
-			  u8 *proto, struct in6_addr **daddr_p);
+			  u8 *proto, struct in6_addr **daddr_p,
+			  struct in6_addr *saddr);
 void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
 			 u8 *proto);
 
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 9b63932..28d2629 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -874,7 +874,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
 
 static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
 			    struct ipv6_rt_hdr *opt,
-			    struct in6_addr **addr_p)
+			    struct in6_addr **addr_p, struct in6_addr *saddr)
 {
 	struct rt0_hdr *phdr, *ihdr;
 	int hops;
@@ -908,10 +908,10 @@ static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv
 
 void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
 			  u8 *proto,
-			  struct in6_addr **daddr)
+			  struct in6_addr **daddr, struct in6_addr *saddr)
 {
 	if (opt->srcrt) {
-		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
+		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr, saddr);
 		/*
 		 * IPV6_RTHDRDSTOPTS is ignored
 		 * unless IPV6_RTHDR is set (RFC3542).
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 6001e78..ddc878d 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -203,7 +203,8 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
 		if (opt->opt_flen)
 			ipv6_push_frag_opts(skb, opt, &proto);
 		if (opt->opt_nflen)
-			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop,
+					     &fl6->saddr);
 	}
 
 	skb_push(skb, sizeof(struct ipv6hdr));
@@ -1672,7 +1673,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk,
 	if (opt && opt->opt_flen)
 		ipv6_push_frag_opts(skb, opt, &proto);
 	if (opt && opt->opt_nflen)
-		ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst);
+		ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst, &fl6->saddr);
 
 	skb_push(skb, sizeof(struct ipv6hdr));
 	skb_reset_network_header(skb);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 3a70567..9c894c9 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1155,7 +1155,7 @@ int ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, __u8 dsfield,
 
 	if (encap_limit >= 0) {
 		init_tel_txopt(&opt, encap_limit);
-		ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
+		ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL, NULL);
 	}
 
 	/* Calculate max headroom for all the headers and adjust
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 8/9] ipv6: sr: add support for SRH injection through setsockopt
From: David Lebrun @ 2016-10-27 13:35 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch adds support for per-socket SRH injection with the setsockopt
system call through the IPPROTO_IPV6, IPV6_RTHDR options.
The SRH is pushed through the ipv6_push_nfrag_opts function.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 net/ipv6/exthdrs.c       | 79 +++++++++++++++++++++++++++++++++++++++++++++---
 net/ipv6/ipv6_sockglue.c |  2 ++
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 28d2629..489f9ea 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -872,9 +872,9 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
  *	for headers.
  */
 
-static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
-			    struct ipv6_rt_hdr *opt,
-			    struct in6_addr **addr_p, struct in6_addr *saddr)
+static void ipv6_push_rthdr0(struct sk_buff *skb, u8 *proto,
+			     struct ipv6_rt_hdr *opt,
+			     struct in6_addr **addr_p, struct in6_addr *saddr)
 {
 	struct rt0_hdr *phdr, *ihdr;
 	int hops;
@@ -897,6 +897,62 @@ static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
 	*proto = NEXTHDR_ROUTING;
 }
 
+static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto,
+			     struct ipv6_rt_hdr *opt,
+			     struct in6_addr **addr_p, struct in6_addr *saddr)
+{
+	struct ipv6_sr_hdr *sr_phdr, *sr_ihdr;
+	int plen, hops;
+
+	sr_ihdr = (struct ipv6_sr_hdr *)opt;
+	plen = (sr_ihdr->hdrlen + 1) << 3;
+
+	sr_phdr = (struct ipv6_sr_hdr *)skb_push(skb, plen);
+	memcpy(sr_phdr, sr_ihdr, sizeof(struct ipv6_sr_hdr));
+
+	hops = sr_ihdr->first_segment + 1;
+	memcpy(sr_phdr->segments + 1, sr_ihdr->segments + 1,
+	       (hops - 1) * sizeof(struct in6_addr));
+
+	sr_phdr->segments[0] = **addr_p;
+	*addr_p = &sr_ihdr->segments[hops - 1];
+
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	if (sr_has_hmac(sr_phdr)) {
+		struct net *net = NULL;
+
+		if (skb->dev)
+			net = dev_net(skb->dev);
+		else if (skb->sk)
+			net = sock_net(skb->sk);
+
+		WARN_ON(!net);
+
+		if (net)
+			seg6_push_hmac(net, saddr, sr_phdr);
+	}
+#endif
+
+	sr_phdr->nexthdr = *proto;
+	*proto = NEXTHDR_ROUTING;
+}
+
+static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
+			    struct ipv6_rt_hdr *opt,
+			    struct in6_addr **addr_p, struct in6_addr *saddr)
+{
+	switch (opt->type) {
+	case IPV6_SRCRT_TYPE_0:
+		ipv6_push_rthdr0(skb, proto, opt, addr_p, saddr);
+		break;
+	case IPV6_SRCRT_TYPE_4:
+		ipv6_push_rthdr4(skb, proto, opt, addr_p, saddr);
+		break;
+	default:
+		break;
+	}
+}
+
 static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
 {
 	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
@@ -1138,7 +1194,22 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
 		return NULL;
 
 	*orig = fl6->daddr;
-	fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
+
+	switch (opt->srcrt->type) {
+	case IPV6_SRCRT_TYPE_0:
+		fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
+		break;
+	case IPV6_SRCRT_TYPE_4:
+	{
+		struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt;
+
+		fl6->daddr = srh->segments[srh->first_segment];
+		break;
+	}
+	default:
+		return NULL;
+	}
+
 	return orig;
 }
 EXPORT_SYMBOL_GPL(fl6_update_dst);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 5330262..3fd6371 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -429,6 +429,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
 
 				break;
 #endif
+			case IPV6_SRCRT_TYPE_4:
+				break;
 			default:
 				goto sticky_done;
 			}
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 9/9] ipv6: sr: add documentation file for per-interface sysctls
From: David Lebrun @ 2016-10-27 13:35 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch adds documentation for some SR-related per-interface
sysctls.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 Documentation/networking/seg6-sysctl.txt | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 Documentation/networking/seg6-sysctl.txt

diff --git a/Documentation/networking/seg6-sysctl.txt b/Documentation/networking/seg6-sysctl.txt
new file mode 100644
index 0000000..bdbde23
--- /dev/null
+++ b/Documentation/networking/seg6-sysctl.txt
@@ -0,0 +1,18 @@
+/proc/sys/net/conf/<iface>/seg6_* variables:
+
+seg6_enabled - BOOL
+	Accept or drop SR-enabled IPv6 packets on this interface.
+
+	Relevant packets are those with SRH present and DA = local.
+
+	0 - disabled (default)
+	not 0 - enabled
+
+seg6_require_hmac - INTEGER
+	Define HMAC policy for ingress SR-enabled packets on this interface.
+
+	-1 - Ignore HMAC field
+	0 - Accept SR packets without HMAC, validate SR packets with HMAC
+	1 - Drop SR packets without HMAC, validate SR packets with HMAC
+
+	Default is 0.
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 6/9] ipv6: sr: add calls to verify and insert HMAC signatures
From: David Lebrun @ 2016-10-27 13:35 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch enables the verification of the HMAC signature for transiting
SR-enabled packets, and its insertion on encapsulated/injected SRH.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 net/ipv6/exthdrs.c       | 10 ++++++++++
 net/ipv6/seg6_iptunnel.c | 18 ++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index cf9f338..9b63932 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -48,6 +48,9 @@
 #include <net/xfrm.h>
 #endif
 #include <linux/seg6.h>
+#ifdef CONFIG_IPV6_SEG6_HMAC
+#include <net/seg6_hmac.h>
+#endif
 
 #include <linux/uaccess.h>
 
@@ -358,6 +361,13 @@ static int ipv6_srh_rcv(struct sk_buff *skb)
 		return -1;
 	}
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	if (!seg6_hmac_validate_skb(skb)) {
+		kfree_skb(skb);
+		return -1;
+	}
+#endif
+
 looped_back:
 	if (hdr->segments_left > 0) {
 		if (hdr->nexthdr != NEXTHDR_IPV6 && hdr->segments_left == 1 &&
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index e0ae54b..eb57d14 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -126,6 +126,14 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
 	hdr->daddr = isrh->segments[isrh->first_segment];
 	set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr);
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	if (sr_has_hmac(isrh)) {
+		err = seg6_push_hmac(net, &hdr->saddr, isrh);
+		if (unlikely(err))
+			return err;
+	}
+#endif
+
 	skb_postpush_rcsum(skb, hdr, tot_len);
 
 	return 0;
@@ -167,6 +175,16 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
 	isrh->segments[0] = hdr->daddr;
 	hdr->daddr = isrh->segments[isrh->first_segment];
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	if (sr_has_hmac(isrh)) {
+		struct net *net = dev_net(skb_dst(skb)->dev);
+
+		err = seg6_push_hmac(net, &hdr->saddr, isrh);
+		if (unlikely(err))
+			return err;
+	}
+#endif
+
 	skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen);
 
 	return 0;
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 5/9] ipv6: sr: implement API to control SR HMAC structure
From: David Lebrun @ 2016-10-27 13:35 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch provides an implementation of the genetlink commands
to associate a given HMAC key identifier with an hashing algorithm
and a secret. It also provides a per-interface sysctl called
seg6_require_hmac, allowing a user-defined policy for processing
HMAC-signed SR-enabled packets. A value of -1 means that the HMAC
field will always be ignored. A value of 0 means that if an HMAC
field is present, its validity will be enforced (the packet is
dropped is the signature is incorrect). Finally, a value of 1 means
that any SR-enabled packet that does not contain an HMAC signature
or whose signature is incorrect will be dropped.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/linux/ipv6.h      |   3 +
 include/net/seg6.h        |   6 ++
 include/net/seg6_hmac.h   |   1 +
 include/uapi/linux/ipv6.h |   1 +
 net/ipv6/Makefile         |   1 +
 net/ipv6/addrconf.c       |  18 +++++
 net/ipv6/seg6.c           | 170 ++++++++++++++++++++++++++++++++++++++++++++++
 net/ipv6/seg6_hmac.c      |  19 ++++++
 8 files changed, 219 insertions(+)

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 76830e6..bad8363 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -65,6 +65,9 @@ struct ipv6_devconf {
 	__s32		use_oif_addrs_only;
 	__s32		keep_addr_on_down;
 	__s32		seg6_enabled;
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	__s32		seg6_require_hmac;
+#endif
 
 	struct ctl_table_header *sysctl_header;
 };
diff --git a/include/net/seg6.h b/include/net/seg6.h
index 7ff7403..43b7424 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -17,10 +17,16 @@
 #include <linux/net.h>
 #include <linux/ipv6.h>
 #include <net/lwtunnel.h>
+#ifdef CONFIG_IPV6_SEG6_HMAC
+#include <net/seg6_hmac.h>
+#endif
 
 struct seg6_pernet_data {
 	struct mutex lock;
 	struct in6_addr __rcu *tun_src;
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	struct list_head hmac_infos;
+#endif
 };
 
 static inline struct seg6_pernet_data *seg6_pernet(struct net *net)
diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h
index 6e5ee6a..9646b7e 100644
--- a/include/net/seg6_hmac.h
+++ b/include/net/seg6_hmac.h
@@ -21,6 +21,7 @@
 #include <linux/ipv6.h>
 #include <linux/route.h>
 #include <net/seg6.h>
+#include <linux/seg6.h>
 #include <linux/seg6_hmac.h>
 
 #define SEG6_HMAC_MAX_DIGESTSIZE	160
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 7ff1d65..53561be 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -180,6 +180,7 @@ enum {
 	DEVCONF_KEEP_ADDR_ON_DOWN,
 	DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
 	DEVCONF_SEG6_ENABLED,
+	DEVCONF_SEG6_REQUIRE_HMAC,
 	DEVCONF_MAX
 };
 
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 59ee92f..129cad2 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o
 obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
 obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
 obj-$(CONFIG_IPV6_FOU) += fou6.o
+obj-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o
 
 obj-y += addrconf_core.o exthdrs_core.o ip6_checksum.o ip6_icmp.o
 obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 8d068ee..6e5f72b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -240,6 +240,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.ignore_routes_with_linkdown = 0,
 	.keep_addr_on_down	= 0,
 	.seg6_enabled		= 0,
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	.seg6_require_hmac	= 0,
+#endif
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -287,6 +290,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.ignore_routes_with_linkdown = 0,
 	.keep_addr_on_down	= 0,
 	.seg6_enabled		= 0,
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	.seg6_require_hmac	= 0,
+#endif
 };
 
 /* Check if a valid qdisc is available */
@@ -4968,6 +4974,9 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 	array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
 	array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
 	array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled;
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac;
+#endif
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6066,6 +6075,15 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	{
+		.procname	= "seg6_require_hmac",
+		.data		= &ipv6_devconf.seg6_require_hmac,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+#endif
 	{
 		/* sentinel */
 	}
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index fb9ebea..45d9f7e 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -25,6 +25,9 @@
 #include <net/genetlink.h>
 #include <linux/seg6.h>
 #include <linux/seg6_genl.h>
+#ifdef CONFIG_IPV6_SEG6_HMAC
+#include <net/seg6_hmac.h>
+#endif
 
 static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
 	[SEG6_ATTR_DST]				= { .type = NLA_BINARY,
@@ -46,11 +49,90 @@ static struct genl_family seg6_genl_family = {
 	.netnsok = true,
 };
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+
+static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct seg6_pernet_data *sdata;
+	struct seg6_hmac_info *hinfo;
+	u32 hmackeyid;
+	char *secret;
+	int err = 0;
+	u8 algid;
+	u8 slen;
+
+	sdata = seg6_pernet(net);
+
+	if (!info->attrs[SEG6_ATTR_HMACKEYID] ||
+	    !info->attrs[SEG6_ATTR_SECRETLEN] ||
+	    !info->attrs[SEG6_ATTR_ALGID])
+		return -EINVAL;
+
+	hmackeyid = nla_get_u32(info->attrs[SEG6_ATTR_HMACKEYID]);
+	slen = nla_get_u8(info->attrs[SEG6_ATTR_SECRETLEN]);
+	algid = nla_get_u8(info->attrs[SEG6_ATTR_ALGID]);
+
+	if (hmackeyid == 0)
+		return -EINVAL;
+
+	if (slen > SEG6_HMAC_SECRET_LEN)
+		return -EINVAL;
+
+	mutex_lock(&sdata->lock);
+	hinfo = seg6_hmac_info_lookup(net, hmackeyid);
+
+	if (!slen) {
+		if (!hinfo || seg6_hmac_info_del(net, hmackeyid, hinfo))
+			err = -ENOENT;
+		else
+			kfree(hinfo);
+
+		goto out_unlock;
+	}
+
+	if (!info->attrs[SEG6_ATTR_SECRET]) {
+		err = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (hinfo) {
+		if (seg6_hmac_info_del(net, hmackeyid, hinfo)) {
+			err = -ENOENT;
+			goto out_unlock;
+		}
+		kfree(hinfo);
+	}
+
+	secret = (char *)nla_data(info->attrs[SEG6_ATTR_SECRET]);
+
+	hinfo = kzalloc(sizeof(*hinfo), GFP_KERNEL);
+	if (!hinfo) {
+		err = -ENOMEM;
+		goto out_unlock;
+	}
+
+	memcpy(hinfo->secret, secret, slen);
+	hinfo->slen = slen;
+	hinfo->alg_id = algid;
+	hinfo->hmackeyid = hmackeyid;
+
+	seg6_hmac_info_add(net, hmackeyid, hinfo);
+
+out_unlock:
+	mutex_unlock(&sdata->lock);
+	return err;
+}
+
+#else
+
 static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
 {
 	return -ENOTSUPP;
 }
 
+#endif
+
 static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
 {
 	struct net *net = genl_info_net(info);
@@ -115,11 +197,78 @@ static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
 	return -ENOMEM;
 }
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+
+static int __seg6_hmac_fill_info(struct seg6_hmac_info *hinfo,
+				 struct sk_buff *msg)
+{
+	if (nla_put_u32(msg, SEG6_ATTR_HMACKEYID, hinfo->hmackeyid) ||
+	    nla_put_u8(msg, SEG6_ATTR_SECRETLEN, hinfo->slen) ||
+	    nla_put(msg, SEG6_ATTR_SECRET, hinfo->slen, hinfo->secret) ||
+	    nla_put_u8(msg, SEG6_ATTR_ALGID, hinfo->alg_id))
+		return -1;
+
+	return 0;
+}
+
+static int __seg6_genl_dumphmac_element(struct seg6_hmac_info *hinfo,
+					u32 portid, u32 seq, u32 flags,
+					struct sk_buff *skb, u8 cmd)
+{
+	void *hdr;
+
+	hdr = genlmsg_put(skb, portid, seq, &seg6_genl_family, flags, cmd);
+	if (!hdr)
+		return -ENOMEM;
+
+	if (__seg6_hmac_fill_info(hinfo, skb) < 0)
+		goto nla_put_failure;
+
+	genlmsg_end(skb, hdr);
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(skb, hdr);
+	return -EMSGSIZE;
+}
+
+static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct net *net = sock_net(skb->sk);
+	struct seg6_pernet_data *sdata;
+	struct seg6_hmac_info *hinfo;
+	int idx = 0, ret;
+
+	sdata = seg6_pernet(net);
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(hinfo, &sdata->hmac_infos, list) {
+		if (idx++ < cb->args[0])
+			continue;
+
+		ret = __seg6_genl_dumphmac_element(hinfo,
+						   NETLINK_CB(cb->skb).portid,
+						   cb->nlh->nlmsg_seq,
+						   NLM_F_MULTI,
+						   skb, SEG6_CMD_DUMPHMAC);
+		if (ret)
+			break;
+	}
+	rcu_read_unlock();
+
+	cb->args[0] = idx;
+	return skb->len;
+}
+
+#else
+
 static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	return -ENOTSUPP;
 }
 
+#endif
+
 static const struct genl_ops seg6_genl_ops[] = {
 	{
 		.cmd	= SEG6_CMD_SETHMAC,
@@ -165,6 +314,10 @@ static int __net_init seg6_net_init(struct net *net)
 
 	net->ipv6.seg6_data = sdata;
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	seg6_hmac_net_init(net);
+#endif
+
 	return 0;
 }
 
@@ -172,6 +325,10 @@ static void __net_exit seg6_net_exit(struct net *net)
 {
 	struct seg6_pernet_data *sdata = seg6_pernet(net);
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	seg6_hmac_net_exit(net);
+#endif
+
 	kfree(sdata->tun_src);
 	kfree(sdata);
 }
@@ -197,10 +354,20 @@ int __init seg6_init(void)
 	if (err)
 		goto out_unregister_pernet;
 
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	err = seg6_hmac_init();
+	if (err)
+		goto out_unregister_iptun;
+#endif
+
 	pr_info("Segment Routing with IPv6\n");
 
 out:
 	return err;
+#ifdef CONFIG_IPV6_SEG6_HMAC
+out_unregister_iptun:
+	seg6_iptunnel_exit();
+#endif
 out_unregister_pernet:
 	unregister_pernet_subsys(&ip6_segments_ops);
 out_unregister_genl:
@@ -210,6 +377,9 @@ int __init seg6_init(void)
 
 void seg6_exit(void)
 {
+#ifdef CONFIG_IPV6_SEG6_HMAC
+	seg6_hmac_exit();
+#endif
 	seg6_iptunnel_exit();
 	unregister_pernet_subsys(&ip6_segments_ops);
 	genl_unregister_family(&seg6_genl_family);
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index 96fe950..c09ce36 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -58,6 +58,25 @@ static struct seg6_hmac_algo hmac_algos[] = {
 	},
 };
 
+static struct sr6_tlv_hmac *seg6_get_tlv_hmac(struct ipv6_sr_hdr *srh)
+{
+	struct sr6_tlv_hmac *tlv;
+
+	if (srh->hdrlen < (srh->first_segment + 1) * 2 + 5)
+		return NULL;
+
+	if (!sr_has_hmac(srh))
+		return NULL;
+
+	tlv = (struct sr6_tlv_hmac *)
+	      ((char *)srh + ((srh->hdrlen + 1) << 3) - 40);
+
+	if (tlv->type != SR6_TLV_HMAC || tlv->len != 38)
+		return NULL;
+
+	return tlv;
+}
+
 static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id)
 {
 	struct seg6_hmac_algo *algo;
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 3/9] ipv6: sr: add support for SRH encapsulation and injection with lwtunnels
From: David Lebrun @ 2016-10-27 13:33 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch creates a new type of interfaceless lightweight tunnel (SEG6),
enabling the encapsulation and injection of SRH within locally emitted
packets and forwarded packets.

>From a configuration viewpoint, a seg6 tunnel would be configured as follows:

  ip -6 ro ad fc00::1/128 encap seg6 mode encap segs fc42::1,fc42::2,fc42::3 dev eth0

Any packet whose destination address is fc00::1 would thus be encapsulated
within an outer IPv6 header containing the SRH with three segments, and would
actually be routed to the first segment of the list. If `mode inline' was
specified instead of `mode encap', then the SRH would be directly inserted
after the IPv6 header without outer encapsulation.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/linux/seg6_iptunnel.h      |   6 +
 include/net/seg6.h                 |   3 +
 include/uapi/linux/lwtunnel.h      |   1 +
 include/uapi/linux/seg6_iptunnel.h |  41 +++++
 net/core/lwtunnel.c                |   2 +
 net/ipv6/Makefile                  |   2 +-
 net/ipv6/seg6.c                    |   7 +
 net/ipv6/seg6_iptunnel.c           | 365 +++++++++++++++++++++++++++++++++++++
 8 files changed, 426 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/seg6_iptunnel.h
 create mode 100644 include/uapi/linux/seg6_iptunnel.h
 create mode 100644 net/ipv6/seg6_iptunnel.c

diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h
new file mode 100644
index 0000000..5377cf6
--- /dev/null
+++ b/include/linux/seg6_iptunnel.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_SEG6_IPTUNNEL_H
+#define _LINUX_SEG6_IPTUNNEL_H
+
+#include <uapi/linux/seg6_iptunnel.h>
+
+#endif
diff --git a/include/net/seg6.h b/include/net/seg6.h
index 0cdd669..7ff7403 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -16,6 +16,7 @@
 
 #include <linux/net.h>
 #include <linux/ipv6.h>
+#include <net/lwtunnel.h>
 
 struct seg6_pernet_data {
 	struct mutex lock;
@@ -29,5 +30,7 @@ static inline struct seg6_pernet_data *seg6_pernet(struct net *net)
 
 extern int seg6_init(void);
 extern void seg6_exit(void);
+extern int seg6_iptunnel_init(void);
+extern void seg6_iptunnel_exit(void);
 
 #endif
diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h
index a478fe8..453cc62 100644
--- a/include/uapi/linux/lwtunnel.h
+++ b/include/uapi/linux/lwtunnel.h
@@ -9,6 +9,7 @@ enum lwtunnel_encap_types {
 	LWTUNNEL_ENCAP_IP,
 	LWTUNNEL_ENCAP_ILA,
 	LWTUNNEL_ENCAP_IP6,
+	LWTUNNEL_ENCAP_SEG6,
 	__LWTUNNEL_ENCAP_MAX,
 };
 
diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h
new file mode 100644
index 0000000..da5524a
--- /dev/null
+++ b/include/uapi/linux/seg6_iptunnel.h
@@ -0,0 +1,41 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _UAPI_LINUX_SEG6_IPTUNNEL_H
+#define _UAPI_LINUX_SEG6_IPTUNNEL_H
+
+enum {
+	SEG6_IPTUNNEL_UNSPEC,
+	SEG6_IPTUNNEL_SRH,
+	__SEG6_IPTUNNEL_MAX,
+};
+#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)
+
+struct seg6_iptunnel_encap {
+	int flags;
+	struct ipv6_sr_hdr srh[0];
+};
+
+#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3))
+
+#define SEG6_IPTUN_FLAG_ENCAP   0x1
+
+static inline size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
+{
+	int encap = !!(tuninfo->flags & SEG6_IPTUN_FLAG_ENCAP);
+
+	return ((tuninfo->srh->hdrlen + 1) << 3) +
+	       (encap * sizeof(struct ipv6hdr));
+}
+
+#endif
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index 88fd642..03976e9 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -39,6 +39,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
 		return "MPLS";
 	case LWTUNNEL_ENCAP_ILA:
 		return "ILA";
+	case LWTUNNEL_ENCAP_SEG6:
+		return "SEG6";
 	case LWTUNNEL_ENCAP_IP6:
 	case LWTUNNEL_ENCAP_IP:
 	case LWTUNNEL_ENCAP_NONE:
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index c92010d..59ee92f 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -9,7 +9,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
 		raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
-		udp_offload.o seg6.o
+		udp_offload.o seg6.o seg6_iptunnel.o
 
 ipv6-offload :=	ip6_offload.o tcpv6_offload.o exthdrs_offload.o
 
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 9cca051..fb9ebea 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -193,10 +193,16 @@ int __init seg6_init(void)
 	if (err)
 		goto out_unregister_genl;
 
+	err = seg6_iptunnel_init();
+	if (err)
+		goto out_unregister_pernet;
+
 	pr_info("Segment Routing with IPv6\n");
 
 out:
 	return err;
+out_unregister_pernet:
+	unregister_pernet_subsys(&ip6_segments_ops);
 out_unregister_genl:
 	genl_unregister_family(&seg6_genl_family);
 	goto out;
@@ -204,6 +210,7 @@ int __init seg6_init(void)
 
 void seg6_exit(void)
 {
+	seg6_iptunnel_exit();
 	unregister_pernet_subsys(&ip6_segments_ops);
 	genl_unregister_family(&seg6_genl_family);
 }
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
new file mode 100644
index 0000000..e0ae54b
--- /dev/null
+++ b/net/ipv6/seg6_iptunnel.c
@@ -0,0 +1,365 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *        modify it under the terms of the GNU General Public License
+ *        as published by the Free Software Foundation; either version
+ *        2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/lwtunnel.h>
+#include <net/netevent.h>
+#include <net/netns/generic.h>
+#include <net/ip6_fib.h>
+#include <net/route.h>
+#include <net/seg6.h>
+#include <linux/seg6.h>
+#include <linux/seg6_iptunnel.h>
+#include <net/addrconf.h>
+#include <net/ip6_route.h>
+#include <net/dst_cache.h>
+
+struct seg6_lwt {
+	struct dst_cache cache;
+	struct seg6_iptunnel_encap tuninfo[0];
+};
+
+static inline struct seg6_lwt *seg6_lwt_lwtunnel(struct lwtunnel_state *lwt)
+{
+	return (struct seg6_lwt *)lwt->data;
+}
+
+static inline struct seg6_iptunnel_encap *
+seg6_encap_lwtunnel(struct lwtunnel_state *lwt)
+{
+	return seg6_lwt_lwtunnel(lwt)->tuninfo;
+}
+
+static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {
+	[SEG6_IPTUNNEL_SRH]	= { .type = NLA_BINARY },
+};
+
+int nla_put_srh(struct sk_buff *skb, int attrtype,
+		struct seg6_iptunnel_encap *tuninfo)
+{
+	struct seg6_iptunnel_encap *data;
+	struct nlattr *nla;
+	int len;
+
+	len = SEG6_IPTUN_ENCAP_SIZE(tuninfo);
+
+	nla = nla_reserve(skb, attrtype, len);
+	if (!nla)
+		return -EMSGSIZE;
+
+	data = nla_data(nla);
+	memcpy(data, tuninfo, len);
+
+	return 0;
+}
+
+static void set_tun_src(struct net *net, struct net_device *dev,
+			struct in6_addr *daddr, struct in6_addr *saddr)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+	struct in6_addr *tun_src;
+
+	rcu_read_lock();
+
+	tun_src = rcu_dereference(sdata->tun_src);
+
+	if (!ipv6_addr_any(tun_src)) {
+		memcpy(saddr, tun_src, sizeof(struct in6_addr));
+	} else {
+		ipv6_dev_get_saddr(net, dev, daddr, IPV6_PREFER_SRC_PUBLIC,
+				   saddr);
+	}
+
+	rcu_read_unlock();
+}
+
+/* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
+static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
+{
+	struct net *net = dev_net(skb_dst(skb)->dev);
+	struct ipv6hdr *hdr, *inner_hdr;
+	struct ipv6_sr_hdr *isrh;
+	int hdrlen, tot_len, err;
+
+	hdrlen = (osrh->hdrlen + 1) << 3;
+	tot_len = hdrlen + sizeof(*hdr);
+
+	err = pskb_expand_head(skb, tot_len, 0, GFP_ATOMIC);
+	if (unlikely(err))
+		return err;
+
+	inner_hdr = ipv6_hdr(skb);
+
+	skb_push(skb, tot_len);
+	skb_reset_network_header(skb);
+	skb_mac_header_rebuild(skb);
+	hdr = ipv6_hdr(skb);
+
+	/* inherit tc, flowlabel and hlim
+	 * hlim will be decremented in ip6_forward() afterwards and
+	 * decapsulation will overwrite inner hlim with outer hlim
+	 */
+	ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
+		     ip6_flowlabel(inner_hdr));
+	hdr->hop_limit = inner_hdr->hop_limit;
+	hdr->nexthdr = NEXTHDR_ROUTING;
+
+	isrh = (void *)hdr + sizeof(*hdr);
+	memcpy(isrh, osrh, hdrlen);
+
+	isrh->nexthdr = NEXTHDR_IPV6;
+
+	hdr->daddr = isrh->segments[isrh->first_segment];
+	set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr);
+
+	skb_postpush_rcsum(skb, hdr, tot_len);
+
+	return 0;
+}
+
+/* insert an SRH within an IPv6 packet, just after the IPv6 header */
+static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh)
+{
+	struct ipv6hdr *hdr, *oldhdr;
+	struct ipv6_sr_hdr *isrh;
+	int hdrlen, err;
+
+	hdrlen = (osrh->hdrlen + 1) << 3;
+
+	err = pskb_expand_head(skb, hdrlen, 0, GFP_ATOMIC);
+	if (unlikely(err))
+		return err;
+
+	oldhdr = ipv6_hdr(skb);
+
+	skb_pull(skb, sizeof(struct ipv6hdr));
+	skb_postpull_rcsum(skb, skb_network_header(skb),
+			   sizeof(struct ipv6hdr));
+
+	skb_push(skb, sizeof(struct ipv6hdr) + hdrlen);
+	skb_reset_network_header(skb);
+	skb_mac_header_rebuild(skb);
+
+	hdr = ipv6_hdr(skb);
+
+	memmove(hdr, oldhdr, sizeof(*hdr));
+
+	isrh = (void *)hdr + sizeof(*hdr);
+	memcpy(isrh, osrh, hdrlen);
+
+	isrh->nexthdr = hdr->nexthdr;
+	hdr->nexthdr = NEXTHDR_ROUTING;
+
+	isrh->segments[0] = hdr->daddr;
+	hdr->daddr = isrh->segments[isrh->first_segment];
+
+	skb_postpush_rcsum(skb, hdr, sizeof(struct ipv6hdr) + hdrlen);
+
+	return 0;
+}
+
+static int seg6_do_srh(struct sk_buff *skb)
+{
+	struct dst_entry *dst = skb_dst(skb);
+	struct seg6_iptunnel_encap *tinfo;
+	int err = 0;
+
+	tinfo = seg6_encap_lwtunnel(dst->lwtstate);
+
+	if (likely(!skb->encapsulation)) {
+		skb_reset_inner_headers(skb);
+		skb->encapsulation = 1;
+	}
+
+	if (tinfo->flags & SEG6_IPTUN_FLAG_ENCAP) {
+		err = seg6_do_srh_encap(skb, tinfo->srh);
+	} else {
+		err = seg6_do_srh_inline(skb, tinfo->srh);
+		skb_reset_inner_headers(skb);
+	}
+
+	if (err)
+		return err;
+
+	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+	skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+	skb_set_inner_protocol(skb, skb->protocol);
+
+	return 0;
+}
+
+int seg6_input(struct sk_buff *skb)
+{
+	int err;
+
+	err = seg6_do_srh(skb);
+	if (unlikely(err)) {
+		kfree_skb(skb);
+		return err;
+	}
+
+	skb_dst_drop(skb);
+	ip6_route_input(skb);
+
+	return dst_input(skb);
+}
+
+int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+	struct dst_entry *orig_dst = skb_dst(skb);
+	struct seg6_lwt *slwt;
+	struct dst_entry *dst;
+	int err = -EINVAL;
+
+	err = seg6_do_srh(skb);
+	if (unlikely(err))
+		goto drop;
+
+	slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate);
+
+	dst = dst_cache_get(&slwt->cache);
+	if (unlikely(!dst)) {
+		struct ipv6hdr *hdr = ipv6_hdr(skb);
+		struct flowi6 fl6;
+
+		fl6.daddr = hdr->daddr;
+		fl6.saddr = hdr->saddr;
+		fl6.flowlabel = ip6_flowinfo(hdr);
+		fl6.flowi6_mark = skb->mark;
+		fl6.flowi6_proto = hdr->nexthdr;
+
+		dst = ip6_route_output(net, NULL, &fl6);
+		if (dst->error) {
+			err = dst->error;
+			dst_release(dst);
+			goto drop;
+		}
+
+		dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr);
+	}
+
+	skb_dst_drop(skb);
+	skb_dst_set(skb, dst);
+
+	return dst_output(net, sk, skb);
+drop:
+	kfree_skb(skb);
+	return err;
+}
+
+static int seg6_build_state(struct net_device *dev, struct nlattr *nla,
+			    unsigned int family, const void *cfg,
+			    struct lwtunnel_state **ts)
+{
+	struct nlattr *tb[SEG6_IPTUNNEL_MAX + 1];
+	struct seg6_iptunnel_encap *tuninfo;
+	struct lwtunnel_state *newts;
+	struct seg6_lwt *slwt;
+	int tuninfo_len;
+	int err;
+
+	err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla,
+			       seg6_iptunnel_policy);
+
+	if (err < 0)
+		return err;
+
+	if (!tb[SEG6_IPTUNNEL_SRH])
+		return -EINVAL;
+
+	tuninfo = nla_data(tb[SEG6_IPTUNNEL_SRH]);
+	tuninfo_len = SEG6_IPTUN_ENCAP_SIZE(tuninfo);
+
+	newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt));
+	if (!newts)
+		return -ENOMEM;
+
+	slwt = seg6_lwt_lwtunnel(newts);
+
+	err = dst_cache_init(&slwt->cache, GFP_KERNEL);
+	if (err) {
+		kfree(newts);
+		return err;
+	}
+
+	memcpy(&slwt->tuninfo, tuninfo, tuninfo_len);
+
+	newts->type = LWTUNNEL_ENCAP_SEG6;
+	newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
+			LWTUNNEL_STATE_INPUT_REDIRECT;
+	newts->headroom = seg6_lwt_headroom(tuninfo);
+
+	*ts = newts;
+
+	return 0;
+}
+
+static void seg6_destroy_state(struct lwtunnel_state *lwt)
+{
+	dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache);
+}
+
+static int seg6_fill_encap_info(struct sk_buff *skb,
+				struct lwtunnel_state *lwtstate)
+{
+	struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);
+
+	if (nla_put_srh(skb, SEG6_IPTUNNEL_SRH, tuninfo))
+		return -EMSGSIZE;
+
+	return 0;
+}
+
+static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)
+{
+	struct seg6_iptunnel_encap *tuninfo = seg6_encap_lwtunnel(lwtstate);
+
+	return nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo));
+}
+
+static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
+{
+	struct seg6_iptunnel_encap *a_hdr = seg6_encap_lwtunnel(a);
+	struct seg6_iptunnel_encap *b_hdr = seg6_encap_lwtunnel(b);
+	int len = SEG6_IPTUN_ENCAP_SIZE(a_hdr);
+
+	if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr))
+		return 1;
+
+	return memcmp(a_hdr, b_hdr, len);
+}
+
+static const struct lwtunnel_encap_ops seg6_iptun_ops = {
+	.build_state = seg6_build_state,
+	.destroy_state = seg6_destroy_state,
+	.output = seg6_output,
+	.input = seg6_input,
+	.fill_encap = seg6_fill_encap_info,
+	.get_encap_size = seg6_encap_nlsize,
+	.cmp_encap = seg6_encap_cmp,
+};
+
+int __init seg6_iptunnel_init(void)
+{
+	return lwtunnel_encap_add_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6);
+}
+
+void seg6_iptunnel_exit(void)
+{
+	lwtunnel_encap_del_ops(&seg6_iptun_ops, LWTUNNEL_ENCAP_SEG6);
+}
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 2/9] ipv6: sr: add code base for control plane support of SR-IPv6
From: David Lebrun @ 2016-10-27 13:33 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch adds the necessary hooks and structures to provide support
for SR-IPv6 control plane, essentially the Generic Netlink commands
that will be used for userspace control over the Segment Routing
kernel structures.

The genetlink commands provide control over two different structures:
tunnel source and HMAC data. The tunnel source is the source address
that will be used by default when encapsulating packets into an
outer IPv6 header + SRH. If the tunnel source is set to :: then an
address of the outgoing interface will be selected as the source.

The HMAC commands currently just return ENOTSUPP and will be implemented
in a future patch.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/linux/seg6_genl.h      |   6 ++
 include/net/netns/ipv6.h       |   1 +
 include/net/seg6.h             |  33 +++++++
 include/uapi/linux/seg6_genl.h |  32 +++++++
 net/ipv6/Makefile              |   2 +-
 net/ipv6/af_inet6.c            |   9 +-
 net/ipv6/seg6.c                | 209 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 290 insertions(+), 2 deletions(-)
 create mode 100644 include/linux/seg6_genl.h
 create mode 100644 include/net/seg6.h
 create mode 100644 include/uapi/linux/seg6_genl.h
 create mode 100644 net/ipv6/seg6.c

diff --git a/include/linux/seg6_genl.h b/include/linux/seg6_genl.h
new file mode 100644
index 0000000..d6c3fb4f
--- /dev/null
+++ b/include/linux/seg6_genl.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_SEG6_GENL_H
+#define _LINUX_SEG6_GENL_H
+
+#include <uapi/linux/seg6_genl.h>
+
+#endif
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 10d0848..de7745e 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -85,6 +85,7 @@ struct netns_ipv6 {
 #endif
 	atomic_t		dev_addr_genid;
 	atomic_t		fib6_sernum;
+	struct seg6_pernet_data *seg6_data;
 };
 
 #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
diff --git a/include/net/seg6.h b/include/net/seg6.h
new file mode 100644
index 0000000..0cdd669
--- /dev/null
+++ b/include/net/seg6.h
@@ -0,0 +1,33 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_SEG6_H
+#define _NET_SEG6_H
+
+#include <linux/net.h>
+#include <linux/ipv6.h>
+
+struct seg6_pernet_data {
+	struct mutex lock;
+	struct in6_addr __rcu *tun_src;
+};
+
+static inline struct seg6_pernet_data *seg6_pernet(struct net *net)
+{
+	return net->ipv6.seg6_data;
+}
+
+extern int seg6_init(void);
+extern void seg6_exit(void);
+
+#endif
diff --git a/include/uapi/linux/seg6_genl.h b/include/uapi/linux/seg6_genl.h
new file mode 100644
index 0000000..fcf1c60
--- /dev/null
+++ b/include/uapi/linux/seg6_genl.h
@@ -0,0 +1,32 @@
+#ifndef _UAPI_LINUX_SEG6_GENL_H
+#define _UAPI_LINUX_SEG6_GENL_H
+
+#define SEG6_GENL_NAME		"SEG6"
+#define SEG6_GENL_VERSION	0x1
+
+enum {
+	SEG6_ATTR_UNSPEC,
+	SEG6_ATTR_DST,
+	SEG6_ATTR_DSTLEN,
+	SEG6_ATTR_HMACKEYID,
+	SEG6_ATTR_SECRET,
+	SEG6_ATTR_SECRETLEN,
+	SEG6_ATTR_ALGID,
+	SEG6_ATTR_HMACINFO,
+	__SEG6_ATTR_MAX,
+};
+
+#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1)
+
+enum {
+	SEG6_CMD_UNSPEC,
+	SEG6_CMD_SETHMAC,
+	SEG6_CMD_DUMPHMAC,
+	SEG6_CMD_SET_TUNSRC,
+	SEG6_CMD_GET_TUNSRC,
+	__SEG6_CMD_MAX,
+};
+
+#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1)
+
+#endif
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index c174ccb..c92010d 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -9,7 +9,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
 		raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \
-		udp_offload.o
+		udp_offload.o seg6.o
 
 ipv6-offload :=	ip6_offload.o tcpv6_offload.o exthdrs_offload.o
 
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 46ad699..9d233fb 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -61,6 +61,7 @@
 #include <net/ip6_tunnel.h>
 #endif
 #include <net/calipso.h>
+#include <net/seg6.h>
 
 #include <asm/uaccess.h>
 #include <linux/mroute6.h>
@@ -990,6 +991,10 @@ static int __init inet6_init(void)
 	if (err)
 		goto calipso_fail;
 
+	err = seg6_init();
+	if (err)
+		goto seg6_fail;
+
 #ifdef CONFIG_SYSCTL
 	err = ipv6_sysctl_register();
 	if (err)
@@ -1000,8 +1005,10 @@ static int __init inet6_init(void)
 
 #ifdef CONFIG_SYSCTL
 sysctl_fail:
-	calipso_exit();
+	seg6_exit();
 #endif
+seg6_fail:
+	calipso_exit();
 calipso_fail:
 	pingv6_exit();
 pingv6_fail:
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
new file mode 100644
index 0000000..9cca051
--- /dev/null
+++ b/net/ipv6/seg6.c
@@ -0,0 +1,209 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *	  modify it under the terms of the GNU General Public License
+ *	  as published by the Free Software Foundation; either version
+ *	  2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <linux/slab.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+
+#include <net/seg6.h>
+#include <net/genetlink.h>
+#include <linux/seg6.h>
+#include <linux/seg6_genl.h>
+
+static const struct nla_policy seg6_genl_policy[SEG6_ATTR_MAX + 1] = {
+	[SEG6_ATTR_DST]				= { .type = NLA_BINARY,
+		.len = sizeof(struct in6_addr) },
+	[SEG6_ATTR_DSTLEN]			= { .type = NLA_S32, },
+	[SEG6_ATTR_HMACKEYID]		= { .type = NLA_U32, },
+	[SEG6_ATTR_SECRET]			= { .type = NLA_BINARY, },
+	[SEG6_ATTR_SECRETLEN]		= { .type = NLA_U8, },
+	[SEG6_ATTR_ALGID]			= { .type = NLA_U8, },
+	[SEG6_ATTR_HMACINFO]		= { .type = NLA_NESTED, },
+};
+
+static struct genl_family seg6_genl_family = {
+	.id = GENL_ID_GENERATE,
+	.hdrsize = 0,
+	.name = SEG6_GENL_NAME,
+	.version = SEG6_GENL_VERSION,
+	.maxattr = SEG6_ATTR_MAX,
+	.netnsok = true,
+};
+
+static int seg6_genl_sethmac(struct sk_buff *skb, struct genl_info *info)
+{
+	return -ENOTSUPP;
+}
+
+static int seg6_genl_set_tunsrc(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct in6_addr *val, *t_old, *t_new;
+	struct seg6_pernet_data *sdata;
+
+	sdata = seg6_pernet(net);
+
+	if (!info->attrs[SEG6_ATTR_DST])
+		return -EINVAL;
+
+	val = nla_data(info->attrs[SEG6_ATTR_DST]);
+	t_new = kmemdup(val, sizeof(*val), GFP_KERNEL);
+
+	mutex_lock(&sdata->lock);
+
+	t_old = sdata->tun_src;
+	rcu_assign_pointer(sdata->tun_src, t_new);
+
+	mutex_unlock(&sdata->lock);
+
+	synchronize_net();
+	kfree(t_old);
+
+	return 0;
+}
+
+static int seg6_genl_get_tunsrc(struct sk_buff *skb, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	struct in6_addr *tun_src;
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+			  &seg6_genl_family, 0, SEG6_CMD_GET_TUNSRC);
+	if (!hdr)
+		goto free_msg;
+
+	rcu_read_lock();
+	tun_src = rcu_dereference(seg6_pernet(net)->tun_src);
+
+	if (nla_put(msg, SEG6_ATTR_DST, sizeof(struct in6_addr), tun_src))
+		goto nla_put_failure;
+
+	rcu_read_unlock();
+
+	genlmsg_end(msg, hdr);
+	genlmsg_reply(msg, info);
+
+	return 0;
+
+nla_put_failure:
+	rcu_read_unlock();
+	genlmsg_cancel(msg, hdr);
+free_msg:
+	nlmsg_free(msg);
+	return -ENOMEM;
+}
+
+static int seg6_genl_dumphmac(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	return -ENOTSUPP;
+}
+
+static const struct genl_ops seg6_genl_ops[] = {
+	{
+		.cmd	= SEG6_CMD_SETHMAC,
+		.doit	= seg6_genl_sethmac,
+		.policy	= seg6_genl_policy,
+		.flags	= GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= SEG6_CMD_DUMPHMAC,
+		.dumpit	= seg6_genl_dumphmac,
+		.policy	= seg6_genl_policy,
+		.flags	= GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= SEG6_CMD_SET_TUNSRC,
+		.doit	= seg6_genl_set_tunsrc,
+		.policy	= seg6_genl_policy,
+		.flags	= GENL_ADMIN_PERM,
+	},
+	{
+		.cmd	= SEG6_CMD_GET_TUNSRC,
+		.doit	= seg6_genl_get_tunsrc,
+		.policy = seg6_genl_policy,
+		.flags	= GENL_ADMIN_PERM,
+	},
+};
+
+static int __net_init seg6_net_init(struct net *net)
+{
+	struct seg6_pernet_data *sdata;
+
+	sdata = kzalloc(sizeof(*sdata), GFP_KERNEL);
+	if (!sdata)
+		return -ENOMEM;
+
+	mutex_init(&sdata->lock);
+
+	sdata->tun_src = kzalloc(sizeof(*sdata->tun_src), GFP_KERNEL);
+	if (!sdata->tun_src) {
+		kfree(sdata);
+		return -ENOMEM;
+	}
+
+	net->ipv6.seg6_data = sdata;
+
+	return 0;
+}
+
+static void __net_exit seg6_net_exit(struct net *net)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+
+	kfree(sdata->tun_src);
+	kfree(sdata);
+}
+
+static struct pernet_operations ip6_segments_ops = {
+	.init = seg6_net_init,
+	.exit = seg6_net_exit,
+};
+
+int __init seg6_init(void)
+{
+	int err = -ENOMEM;
+
+	err = genl_register_family_with_ops(&seg6_genl_family, seg6_genl_ops);
+	if (err)
+		goto out;
+
+	err = register_pernet_subsys(&ip6_segments_ops);
+	if (err)
+		goto out_unregister_genl;
+
+	pr_info("Segment Routing with IPv6\n");
+
+out:
+	return err;
+out_unregister_genl:
+	genl_unregister_family(&seg6_genl_family);
+	goto out;
+}
+
+void seg6_exit(void)
+{
+	unregister_pernet_subsys(&ip6_segments_ops);
+	genl_unregister_family(&seg6_genl_family);
+}
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 4/9] ipv6: sr: add core files for SR HMAC support
From: David Lebrun @ 2016-10-27 13:33 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

This patch adds the necessary functions to compute and check the HMAC signature
of an SR-enabled packet. Two HMAC algorithms are supported: hmac(sha1) and
hmac(sha256).

In order to avoid dynamic memory allocation for each HMAC computation,
a per-cpu ring buffer is allocated for this purpose.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/linux/seg6_hmac.h      |   6 +
 include/net/seg6_hmac.h        |  61 ++++++
 include/uapi/linux/seg6_hmac.h |  20 ++
 net/ipv6/Kconfig               |  12 ++
 net/ipv6/seg6_hmac.c           | 437 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 536 insertions(+)
 create mode 100644 include/linux/seg6_hmac.h
 create mode 100644 include/net/seg6_hmac.h
 create mode 100644 include/uapi/linux/seg6_hmac.h
 create mode 100644 net/ipv6/seg6_hmac.c

diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h
new file mode 100644
index 0000000..da437eb
--- /dev/null
+++ b/include/linux/seg6_hmac.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_SEG6_HMAC_H
+#define _LINUX_SEG6_HMAC_H
+
+#include <uapi/linux/seg6_hmac.h>
+
+#endif
diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h
new file mode 100644
index 0000000..6e5ee6a
--- /dev/null
+++ b/include/net/seg6_hmac.h
@@ -0,0 +1,61 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_SEG6_HMAC_H
+#define _NET_SEG6_HMAC_H
+
+#include <net/flow.h>
+#include <net/ip6_fib.h>
+#include <net/sock.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/route.h>
+#include <net/seg6.h>
+#include <linux/seg6_hmac.h>
+
+#define SEG6_HMAC_MAX_DIGESTSIZE	160
+#define SEG6_HMAC_RING_SIZE		256
+
+struct seg6_hmac_info {
+	struct list_head list;
+
+	u32 hmackeyid;
+	char secret[SEG6_HMAC_SECRET_LEN];
+	u8 slen;
+	u8 alg_id;
+};
+
+struct seg6_hmac_algo {
+	u8 alg_id;
+	char name[64];
+	struct crypto_shash * __percpu *tfms;
+	struct shash_desc * __percpu *shashs;
+};
+
+extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo,
+			     struct ipv6_sr_hdr *hdr, struct in6_addr *saddr,
+			     u8 *output);
+extern struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key);
+extern int seg6_hmac_info_add(struct net *net, u32 key,
+			      struct seg6_hmac_info *hinfo);
+extern int seg6_hmac_info_del(struct net *net, u32 key,
+			      struct seg6_hmac_info *hinfo);
+extern int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
+			  struct ipv6_sr_hdr *srh);
+extern bool seg6_hmac_validate_skb(struct sk_buff *skb);
+extern int seg6_hmac_init(void);
+extern void seg6_hmac_exit(void);
+extern int seg6_hmac_net_init(struct net *net);
+extern void seg6_hmac_net_exit(struct net *net);
+
+#endif
diff --git a/include/uapi/linux/seg6_hmac.h b/include/uapi/linux/seg6_hmac.h
new file mode 100644
index 0000000..0b5eda7
--- /dev/null
+++ b/include/uapi/linux/seg6_hmac.h
@@ -0,0 +1,20 @@
+#ifndef _UAPI_LINUX_SEG6_HMAC_H
+#define _UAPI_LINUX_SEG6_HMAC_H
+
+#define SEG6_HMAC_SECRET_LEN	64
+#define SEG6_HMAC_FIELD_LEN	32
+
+struct sr6_tlv_hmac {
+	__u8 type;
+	__u8 len;
+	__u16 reserved;
+	__be32 hmackeyid;
+	__u8 hmac[SEG6_HMAC_FIELD_LEN];
+} __attribute__((packed));
+
+enum {
+	SEG6_HMAC_ALGO_SHA1 = 1,
+	SEG6_HMAC_ALGO_SHA256 = 2,
+};
+
+#endif
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 2343e4f..c647712 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -289,4 +289,16 @@ config IPV6_PIMSM_V2
 	  Support for IPv6 PIM multicast routing protocol PIM-SMv2.
 	  If unsure, say N.
 
+config IPV6_SEG6_HMAC
+	bool "IPv6: Segment Routing HMAC support"
+	depends on IPV6
+	select CRYPTO_HMAC
+	select CRYPTO_SHA1
+	select CRYPTO_SHA256
+	---help---
+	  Support for HMAC signature generation and verification
+	  of SR-enabled packets.
+
+	  If unsure, say N.
+
 endif # IPV6
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
new file mode 100644
index 0000000..96fe950
--- /dev/null
+++ b/net/ipv6/seg6_hmac.c
@@ -0,0 +1,437 @@
+/*
+ *  SR-IPv6 implementation -- HMAC functions
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <linux/mroute6.h>
+#include <linux/slab.h>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/xfrm.h>
+
+#include <linux/cryptohash.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <net/seg6.h>
+#include <net/genetlink.h>
+#include <net/seg6_hmac.h>
+#include <linux/random.h>
+
+static char * __percpu *hmac_ring;
+
+static struct seg6_hmac_algo hmac_algos[] = {
+	{
+		.alg_id = SEG6_HMAC_ALGO_SHA1,
+		.name = "hmac(sha1)",
+	},
+	{
+		.alg_id = SEG6_HMAC_ALGO_SHA256,
+		.name = "hmac(sha256)",
+	},
+};
+
+static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id)
+{
+	struct seg6_hmac_algo *algo;
+	int i, alg_count;
+
+	alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
+	for (i = 0; i < alg_count; i++) {
+		algo = &hmac_algos[i];
+		if (algo->alg_id == alg_id)
+			return algo;
+	}
+
+	return NULL;
+}
+
+static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize,
+		     u8 *output, int outlen)
+{
+	struct seg6_hmac_algo *algo;
+	struct crypto_shash *tfm;
+	struct shash_desc *shash;
+	int ret, dgsize;
+
+	algo = __hmac_get_algo(hinfo->alg_id);
+	if (!algo)
+		return -ENOENT;
+
+	tfm = *this_cpu_ptr(algo->tfms);
+
+	dgsize = crypto_shash_digestsize(tfm);
+	if (dgsize > outlen) {
+		pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n",
+			 dgsize, outlen);
+		return -ENOMEM;
+	}
+
+	ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen);
+	if (ret < 0) {
+		pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret);
+		goto failed;
+	}
+
+	shash = *this_cpu_ptr(algo->shashs);
+	shash->tfm = tfm;
+
+	ret = crypto_shash_digest(shash, text, psize, output);
+	if (ret < 0) {
+		pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret);
+		goto failed;
+	}
+
+	return dgsize;
+
+failed:
+	return ret;
+}
+
+int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
+		      struct in6_addr *saddr, u8 *output)
+{
+	__be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid);
+	u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE];
+	int plen, i, dgsize, wrsize;
+	char *ring, *off;
+
+	/* a 160-byte buffer for digest output allows to store highest known
+	 * hash function (RadioGatun) with up to 1216 bits
+	 */
+
+	/* saddr(16) + first_seg(1) + cleanup(1) + keyid(4) + seglist(16n) */
+	plen = 16 + 1 + 1 + 4 + (hdr->first_segment + 1) * 16;
+
+	/* this limit allows for 14 segments */
+	if (plen >= SEG6_HMAC_RING_SIZE)
+		return -EMSGSIZE;
+
+	local_bh_disable();
+	ring = *this_cpu_ptr(hmac_ring);
+	off = ring;
+	memcpy(off, saddr, 16);
+	off += 16;
+	*off++ = hdr->first_segment;
+	*off++ = !!(sr_has_cleanup(hdr)) << 7;
+	memcpy(off, &hmackeyid, 4);
+	off += 4;
+
+	for (i = 0; i < hdr->first_segment + 1; i++) {
+		memcpy(off, hdr->segments + i, 16);
+		off += 16;
+	}
+
+	dgsize = __do_hmac(hinfo, ring, plen, tmp_out,
+			   SEG6_HMAC_MAX_DIGESTSIZE);
+	local_bh_enable();
+
+	if (dgsize < 0)
+		return dgsize;
+
+	wrsize = SEG6_HMAC_FIELD_LEN;
+	if (wrsize > dgsize)
+		wrsize = dgsize;
+
+	memset(output, 0, SEG6_HMAC_FIELD_LEN);
+	memcpy(output, tmp_out, wrsize);
+
+	return 0;
+}
+EXPORT_SYMBOL(seg6_hmac_compute);
+
+/* checks if an incoming SR-enabled packet's HMAC status matches
+ * the incoming policy.
+ *
+ * called with rcu_read_lock()
+ */
+bool seg6_hmac_validate_skb(struct sk_buff *skb)
+{
+	u8 hmac_output[SEG6_HMAC_FIELD_LEN];
+	struct net *net = dev_net(skb->dev);
+	struct seg6_hmac_info *hinfo;
+	struct sr6_tlv_hmac *tlv;
+	struct ipv6_sr_hdr *srh;
+	struct inet6_dev *idev;
+
+	idev = __in6_dev_get(skb->dev);
+
+	srh = (struct ipv6_sr_hdr *)skb_transport_header(skb);
+
+	tlv = seg6_get_tlv_hmac(srh);
+
+	/* mandatory check but no tlv */
+	if (idev->cnf.seg6_require_hmac > 0 && !tlv)
+		return false;
+
+	/* no check */
+	if (idev->cnf.seg6_require_hmac < 0)
+		return true;
+
+	/* check only if present */
+	if (idev->cnf.seg6_require_hmac == 0 && !tlv)
+		return true;
+
+	/* now, seg6_require_hmac >= 0 && tlv */
+
+	hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
+	if (!hinfo)
+		return false;
+
+	if (seg6_hmac_compute(hinfo, srh, &ipv6_hdr(skb)->saddr, hmac_output))
+		return false;
+
+	if (memcmp(hmac_output, tlv->hmac, SEG6_HMAC_FIELD_LEN) != 0)
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL(seg6_hmac_validate_skb);
+
+/* called with rcu_read_lock() */
+struct seg6_hmac_info *seg6_hmac_info_lookup(struct net *net, u32 key)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+	struct seg6_hmac_info *hinfo;
+
+	list_for_each_entry_rcu(hinfo, &sdata->hmac_infos, list) {
+		if (hinfo->hmackeyid == key)
+			return hinfo;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(seg6_hmac_info_lookup);
+
+int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+	struct seg6_hmac_info *old_hinfo;
+
+	old_hinfo = seg6_hmac_info_lookup(net, key);
+	if (old_hinfo)
+		return -EEXIST;
+
+	list_add_rcu(&hinfo->list, &sdata->hmac_infos);
+
+	return 0;
+}
+EXPORT_SYMBOL(seg6_hmac_info_add);
+
+int seg6_hmac_info_del(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
+{
+	struct seg6_hmac_info *tmp;
+
+	tmp = seg6_hmac_info_lookup(net, key);
+	if (!tmp)
+		return -ENOENT;
+
+	/* entry was replaced, ignore deletion */
+	if (tmp != hinfo)
+		return -ENOENT;
+
+	list_del_rcu(&hinfo->list);
+	synchronize_net();
+
+	return 0;
+}
+EXPORT_SYMBOL(seg6_hmac_info_del);
+
+static void seg6_hmac_info_flush(struct net *net)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+	struct seg6_hmac_info *hinfo;
+
+	mutex_lock(&sdata->lock);
+	while ((hinfo = list_first_or_null_rcu(&sdata->hmac_infos,
+					       struct seg6_hmac_info,
+					       list)) != NULL) {
+		list_del_rcu(&hinfo->list);
+		mutex_unlock(&sdata->lock);
+		synchronize_net();
+		kfree(hinfo);
+		mutex_lock(&sdata->lock);
+	}
+
+	mutex_unlock(&sdata->lock);
+}
+
+int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
+		   struct ipv6_sr_hdr *srh)
+{
+	struct seg6_hmac_info *hinfo;
+	struct sr6_tlv_hmac *tlv;
+	int err = -ENOENT;
+
+	tlv = seg6_get_tlv_hmac(srh);
+	if (!tlv)
+		return -EINVAL;
+
+	rcu_read_lock();
+
+	hinfo = seg6_hmac_info_lookup(net, be32_to_cpu(tlv->hmackeyid));
+	if (!hinfo)
+		goto out;
+
+	memset(tlv->hmac, 0, SEG6_HMAC_FIELD_LEN);
+	err = seg6_hmac_compute(hinfo, srh, saddr, tlv->hmac);
+
+out:
+	rcu_read_unlock();
+	return err;
+}
+EXPORT_SYMBOL(seg6_push_hmac);
+
+static int seg6_hmac_init_ring(void)
+{
+	int i;
+
+	hmac_ring = alloc_percpu(char *);
+
+	if (!hmac_ring)
+		return -ENOMEM;
+
+	for_each_possible_cpu(i) {
+		char *ring = kzalloc(SEG6_HMAC_RING_SIZE, GFP_KERNEL);
+
+		if (!ring)
+			return -ENOMEM;
+
+		*per_cpu_ptr(hmac_ring, i) = ring;
+	}
+
+	return 0;
+}
+
+static int seg6_hmac_init_algo(void)
+{
+	struct seg6_hmac_algo *algo;
+	struct crypto_shash *tfm;
+	struct shash_desc *shash;
+	int i, alg_count, cpu;
+
+	alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
+
+	for (i = 0; i < alg_count; i++) {
+		struct crypto_shash **p_tfm;
+		int shsize;
+
+		algo = &hmac_algos[i];
+		algo->tfms = alloc_percpu(struct crypto_shash *);
+		if (!algo->tfms)
+			return -ENOMEM;
+
+		for_each_possible_cpu(cpu) {
+			tfm = crypto_alloc_shash(algo->name, 0, GFP_KERNEL);
+			if (IS_ERR(tfm))
+				return PTR_ERR(tfm);
+			p_tfm = per_cpu_ptr(algo->tfms, cpu);
+			*p_tfm = tfm;
+		}
+
+		p_tfm = this_cpu_ptr(algo->tfms);
+		tfm = *p_tfm;
+
+		shsize = sizeof(*shash) + crypto_shash_descsize(tfm);
+
+		algo->shashs = alloc_percpu(struct shash_desc *);
+		if (!algo->shashs)
+			return -ENOMEM;
+
+		for_each_possible_cpu(cpu) {
+			shash = kzalloc(shsize, GFP_KERNEL);
+			if (!shash)
+				return -ENOMEM;
+			*per_cpu_ptr(algo->shashs, cpu) = shash;
+		}
+	}
+
+	return 0;
+}
+
+int __init seg6_hmac_init(void)
+{
+	int ret;
+
+	ret = seg6_hmac_init_ring();
+	if (ret < 0)
+		goto out;
+
+	ret = seg6_hmac_init_algo();
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(seg6_hmac_init);
+
+int __net_init seg6_hmac_net_init(struct net *net)
+{
+	struct seg6_pernet_data *sdata = seg6_pernet(net);
+
+	INIT_LIST_HEAD(&sdata->hmac_infos);
+	return 0;
+}
+EXPORT_SYMBOL(seg6_hmac_net_init);
+
+void seg6_hmac_exit(void)
+{
+	struct seg6_hmac_algo *algo = NULL;
+	int i, alg_count, cpu;
+
+	for_each_possible_cpu(i) {
+		char *ring = *per_cpu_ptr(hmac_ring, i);
+
+		kfree(ring);
+	}
+	free_percpu(hmac_ring);
+
+	alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo);
+	for (i = 0; i < alg_count; i++) {
+		algo = &hmac_algos[i];
+		for_each_possible_cpu(cpu) {
+			struct crypto_shash *tfm;
+			struct shash_desc *shash;
+
+			shash = *per_cpu_ptr(algo->shashs, cpu);
+			kfree(shash);
+			tfm = *per_cpu_ptr(algo->tfms, cpu);
+			crypto_free_shash(tfm);
+		}
+		free_percpu(algo->tfms);
+		free_percpu(algo->shashs);
+	}
+}
+EXPORT_SYMBOL(seg6_hmac_exit);
+
+void __net_exit seg6_hmac_net_exit(struct net *net)
+{
+	seg6_hmac_info_flush(net);
+}
+EXPORT_SYMBOL(seg6_hmac_net_exit);
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 1/9] ipv6: implement dataplane support for rthdr type 4 (Segment Routing Header)
From: David Lebrun @ 2016-10-27 13:33 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun
In-Reply-To: <1477575239-11228-1-git-send-email-david.lebrun@uclouvain.be>

Implement minimal support for processing of SR-enabled packets
as described in
https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-02.

This patch implements the following operations:
- Intermediate segment endpoint: incrementation of active segment and rerouting.
- Egress for SR-encapsulated packets: decapsulation of outer IPv6 header + SRH
  and routing of inner packet.
- Cleanup flag support for SR-inlined packets: removal of SRH if we are the
  penultimate segment endpoint.

A per-interface sysctl seg6_enabled is provided, to accept/deny SR-enabled
packets. Default is deny.

This patch does not provide support for HMAC-signed packets.

Signed-off-by: David Lebrun <david.lebrun@uclouvain.be>
---
 include/linux/ipv6.h      |   1 +
 include/linux/seg6.h      |   6 ++
 include/uapi/linux/ipv6.h |   2 +
 include/uapi/linux/seg6.h |  48 ++++++++++++
 net/ipv6/addrconf.c       |  10 +++
 net/ipv6/exthdrs.c        | 183 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 250 insertions(+)
 create mode 100644 include/linux/seg6.h
 create mode 100644 include/uapi/linux/seg6.h

diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 7e9a789..76830e6 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -64,6 +64,7 @@ struct ipv6_devconf {
 	} stable_secret;
 	__s32		use_oif_addrs_only;
 	__s32		keep_addr_on_down;
+	__s32		seg6_enabled;
 
 	struct ctl_table_header *sysctl_header;
 };
diff --git a/include/linux/seg6.h b/include/linux/seg6.h
new file mode 100644
index 0000000..7a66d2b
--- /dev/null
+++ b/include/linux/seg6.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_SEG6_H
+#define _LINUX_SEG6_H
+
+#include <uapi/linux/seg6.h>
+
+#endif
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 8c27723..7ff1d65 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -39,6 +39,7 @@ struct in6_ifreq {
 #define IPV6_SRCRT_STRICT	0x01	/* Deprecated; will be removed */
 #define IPV6_SRCRT_TYPE_0	0	/* Deprecated; will be removed */
 #define IPV6_SRCRT_TYPE_2	2	/* IPv6 type 2 Routing Header	*/
+#define IPV6_SRCRT_TYPE_4	4	/* Segment Routing with IPv6 */
 
 /*
  *	routing header
@@ -178,6 +179,7 @@ enum {
 	DEVCONF_DROP_UNSOLICITED_NA,
 	DEVCONF_KEEP_ADDR_ON_DOWN,
 	DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+	DEVCONF_SEG6_ENABLED,
 	DEVCONF_MAX
 };
 
diff --git a/include/uapi/linux/seg6.h b/include/uapi/linux/seg6.h
new file mode 100644
index 0000000..7630e7d
--- /dev/null
+++ b/include/uapi/linux/seg6.h
@@ -0,0 +1,48 @@
+/*
+ *  SR-IPv6 implementation
+ *
+ *  Author:
+ *  David Lebrun <david.lebrun@uclouvain.be>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _UAPI_LINUX_SEG6_H
+#define _UAPI_LINUX_SEG6_H
+
+/*
+ * SRH
+ */
+struct ipv6_sr_hdr {
+	__u8	nexthdr;
+	__u8	hdrlen;
+	__u8	type;
+	__u8	segments_left;
+	__u8	first_segment;
+	__u8	flag_1;
+	__u8	flag_2;
+	__u8	reserved;
+
+	struct in6_addr segments[0];
+};
+
+#define SR6_FLAG1_CLEANUP	(1 << 7)
+#define SR6_FLAG1_PROTECTED	(1 << 6)
+#define SR6_FLAG1_OAM		(1 << 5)
+#define SR6_FLAG1_ALERT		(1 << 4)
+#define SR6_FLAG1_HMAC		(1 << 3)
+
+#define SR6_TLV_INGRESS		1
+#define SR6_TLV_EGRESS		2
+#define SR6_TLV_OPAQUE		3
+#define SR6_TLV_PADDING		4
+#define SR6_TLV_HMAC		5
+
+#define sr_has_cleanup(srh) ((srh)->flag_1 & SR6_FLAG1_CLEANUP)
+#define sr_has_hmac(srh) ((srh)->flag_1 & SR6_FLAG1_HMAC)
+
+#endif
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d8983e1..8d068ee 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -239,6 +239,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.use_oif_addrs_only	= 0,
 	.ignore_routes_with_linkdown = 0,
 	.keep_addr_on_down	= 0,
+	.seg6_enabled		= 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -285,6 +286,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.use_oif_addrs_only	= 0,
 	.ignore_routes_with_linkdown = 0,
 	.keep_addr_on_down	= 0,
+	.seg6_enabled		= 0,
 };
 
 /* Check if a valid qdisc is available */
@@ -4965,6 +4967,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 	array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast;
 	array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
 	array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
+	array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6057,6 +6060,13 @@ static const struct ctl_table addrconf_sysctl[] = {
 
 	},
 	{
+		.procname	= "seg6_enabled",
+		.data		= &ipv6_devconf.seg6_enabled,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+	{
 		/* sentinel */
 	}
 };
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 139ceb6..cf9f338 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -47,6 +47,7 @@
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/xfrm.h>
 #endif
+#include <linux/seg6.h>
 
 #include <linux/uaccess.h>
 
@@ -286,6 +287,184 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
 	return -1;
 }
 
+static inline void update_csum_diff4(struct sk_buff *skb, __be32 from,
+				     __be32 to)
+{
+	__be32 diff[] = { ~from, to };
+
+	skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum);
+}
+
+static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from,
+				      __be32 *to)
+{
+	__be32 diff[] = {
+		~from[0], ~from[1], ~from[2], ~from[3],
+		to[0], to[1], to[2], to[3],
+	};
+
+	skb->csum = ~csum_partial((char *)diff, sizeof(diff), ~skb->csum);
+}
+
+static void seg6_update_csum(struct sk_buff *skb)
+{
+	struct ipv6_sr_hdr *hdr;
+	struct in6_addr *addr;
+	__be32 from, to;
+
+	/* srh is at transport offset and seg_left is already decremented
+	 * but daddr is not yet updated with next segment
+	 */
+
+	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
+	addr = hdr->segments + hdr->segments_left;
+
+	hdr->segments_left++;
+	from = *(__be32 *)hdr;
+
+	hdr->segments_left--;
+	to = *(__be32 *)hdr;
+
+	/* update skb csum with diff resulting from seg_left decrement */
+
+	update_csum_diff4(skb, from, to);
+
+	/* compute csum diff between current and next segment and update */
+
+	update_csum_diff16(skb, (__be32 *)(&ipv6_hdr(skb)->daddr),
+			   (__be32 *)addr);
+}
+
+static int ipv6_srh_rcv(struct sk_buff *skb)
+{
+	struct inet6_skb_parm *opt = IP6CB(skb);
+	struct net *net = dev_net(skb->dev);
+	struct ipv6_sr_hdr *hdr;
+	struct inet6_dev *idev;
+	struct in6_addr *addr;
+	int cleanup = 0;
+	int accept_seg6;
+
+	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
+
+	idev = __in6_dev_get(skb->dev);
+
+	accept_seg6 = net->ipv6.devconf_all->seg6_enabled;
+	if (accept_seg6 > idev->cnf.seg6_enabled)
+		accept_seg6 = idev->cnf.seg6_enabled;
+
+	if (!accept_seg6) {
+		kfree_skb(skb);
+		return -1;
+	}
+
+looped_back:
+	if (hdr->segments_left > 0) {
+		if (hdr->nexthdr != NEXTHDR_IPV6 && hdr->segments_left == 1 &&
+		    sr_has_cleanup(hdr))
+			cleanup = 1;
+	} else {
+		if (hdr->nexthdr == NEXTHDR_IPV6) {
+			int offset = (hdr->hdrlen + 1) << 3;
+
+			skb_postpull_rcsum(skb, skb_network_header(skb),
+					   skb_network_header_len(skb));
+
+			if (!pskb_pull(skb, offset)) {
+				kfree_skb(skb);
+				return -1;
+			}
+			skb_postpull_rcsum(skb, skb_transport_header(skb),
+					   offset);
+
+			skb_reset_network_header(skb);
+			skb_reset_transport_header(skb);
+			skb->encapsulation = 0;
+
+			__skb_tunnel_rx(skb, skb->dev, net);
+
+			netif_rx(skb);
+			return -1;
+		}
+
+		opt->srcrt = skb_network_header_len(skb);
+		opt->lastopt = opt->srcrt;
+		skb->transport_header += (hdr->hdrlen + 1) << 3;
+		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
+
+		return 1;
+	}
+
+	if (skb_cloned(skb)) {
+		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+					IPSTATS_MIB_OUTDISCARDS);
+			kfree_skb(skb);
+			return -1;
+		}
+	}
+
+	hdr = (struct ipv6_sr_hdr *)skb_transport_header(skb);
+
+	hdr->segments_left--;
+	addr = hdr->segments + hdr->segments_left;
+
+	skb_push(skb, sizeof(struct ipv6hdr));
+
+	if (skb->ip_summed == CHECKSUM_COMPLETE)
+		seg6_update_csum(skb);
+
+	ipv6_hdr(skb)->daddr = *addr;
+
+	if (cleanup) {
+		int srhlen = (hdr->hdrlen + 1) << 3;
+		int nh = hdr->nexthdr;
+
+		skb_pull_rcsum(skb, sizeof(struct ipv6hdr) + srhlen);
+		memmove(skb_network_header(skb) + srhlen,
+			skb_network_header(skb),
+			(unsigned char *)hdr - skb_network_header(skb));
+		skb->network_header += srhlen;
+		ipv6_hdr(skb)->nexthdr = nh;
+		ipv6_hdr(skb)->payload_len = htons(skb->len -
+						   sizeof(struct ipv6hdr));
+		skb_push_rcsum(skb, sizeof(struct ipv6hdr));
+	}
+
+	skb_dst_drop(skb);
+
+	ip6_route_input(skb);
+
+	if (skb_dst(skb)->error) {
+		dst_input(skb);
+		return -1;
+	}
+
+	if (skb_dst(skb)->dev->flags & IFF_LOOPBACK) {
+		if (ipv6_hdr(skb)->hop_limit <= 1) {
+			__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
+					IPSTATS_MIB_INHDRERRORS);
+			icmpv6_send(skb, ICMPV6_TIME_EXCEED,
+				    ICMPV6_EXC_HOPLIMIT, 0);
+			kfree_skb(skb);
+			return -1;
+		}
+		ipv6_hdr(skb)->hop_limit--;
+
+		/* be sure that srh is still present before reinjecting */
+		if (!cleanup) {
+			skb_pull(skb, sizeof(struct ipv6hdr));
+			goto looped_back;
+		}
+		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+		IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+	}
+
+	dst_input(skb);
+
+	return -1;
+}
+
 /********************************
   Routing header.
  ********************************/
@@ -326,6 +505,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
 		return -1;
 	}
 
+	/* segment routing */
+	if (hdr->type == IPV6_SRCRT_TYPE_4)
+		return ipv6_srh_rcv(skb);
+
 looped_back:
 	if (hdr->segments_left == 0) {
 		switch (hdr->type) {
-- 
2.7.3

^ permalink raw reply related

* [PATCH net-next v3 0/9] net: add support for IPv6 Segment Routing
From: David Lebrun @ 2016-10-27 13:33 UTC (permalink / raw)
  To: netdev; +Cc: David Lebrun

v3:
 - Fix compilation for CONFIG_IPV6={n,m}

v2:
 - Remove packed attribute from sr6 struct and replaced unaligned
   16-bit flags with two 8-bit flags.
 - SR code now included by default. Option CONFIG_IPV6_SEG6_HMAC
   exists for HMAC support (which requires crypto dependencies).
 - Replace "hidden" calls to mutex_{un,}lock to direct calls.
 - Fix reverse xmas tree coding style.
 - Fix cast-from-void*'s.
 - Update skb->csum to account for SR modifications.
 - Add dst_cache in seg6_output.

Segment Routing (SR) is a source routing paradigm, architecturally
defined in draft-ietf-spring-segment-routing-09 [1]. The IPv6 flavor of
SR is defined in draft-ietf-6man-segment-routing-header-02 [2].

The main idea is that an SR-enabled packet contains a list of segments,
which represent mandatory waypoints. Each waypoint is called a segment
endpoint. The SR-enabled packet is routed normally (e.g. shortest path)
between the segment endpoints. A node that inserts an SRH into a packet
is called an ingress node, and a node that is the last segment endpoint
is called an egress node.

>From an IPv6 viewpoint, an SR-enabled packet contains an IPv6 extension
header, which is a Routing Header type 4, defined as follows:

struct ipv6_sr_hdr {
        __u8    nexthdr;
        __u8    hdrlen;
        __u8    type;
        __u8    segments_left; 
        __u8    first_segment;
        __u8    flag_1;
        __u8    flag_2; 
        __u8    reserved; 
    
        struct in6_addr segments[0];
};

The first 4 bytes of the SRH is consistent with the Routing Header
definition in RFC 2460. The type is set to `4' (SRH).

Each segment is encoded as an IPv6 address. The segments are encoded in
reverse order: segments[0] is the last segment of the path, and
segments[first_segment] is the first segment of the path.

segments[segments_left] points to the currently active segment and
segments_left is decremented at each segment endpoint.

There exist two ways for a packet to receive an SRH, we call them
encap mode and inline mode. In the encap mode, the packet is encapsulated
in an outer IPv6 header that contains the SRH. The inner (original) packet
is not modified. A virtual tunnel is thus created between the ingress node
(the node that encapsulates) and the egress node (the last segment of the path).
Once an encapsulated SR packet reaches the egress node, the node decapsulates
the packet and performs a routing decision on the inner packet. This kind of
SRH insertion is intended to use for routers that encapsulates in-transit
packet.

The second SRH insertion method, the inline mode, acts by directly inserting
the SRH right after the IPv6 header of the original packet. For this method,
if a particular flag (SR6_FLAG_CLEANUP) is set, then the penultimate segment
endpoint must strip the SRH from the packet before forwarding it to the last
segment endpoint. This insertion method is intended to use for endhosts,
however it is also used for in-transit packets by some industry actors.

Finally, the SRH may contain TLVs after the segments list. Several types of
TLVs are defined, but we currently consider only the HMAC TLV. This TLV is
an answer to the deprecation of the RH0 and enables to ensure the authenticity
and integrity of the SRH. The HMAC text contains the flags, the first_segment
index, the full list of segments, and the source address of the packet. While
SR is intended to use mostly within a single administrative domain, the HMAC
TLV allows to verify SR packets coming from an untrusted source.

This patches series implements support for the IPv6 flavor of SR and is
logically divided into the following components:

        (1) Data plane support (patch 01). This patch adds a function
            in net/ipv6/exthdrs.c to handle the Routing Header type 4.
            It enables the kernel to act as a segment endpoint, by supporting
            the following operations: decrementation of the segments_left field,
            cleanup flag support (removal of the SRH if we are the penultimate
            segment endpoint) and decapsulation of the inner packet as an egress
            node.

        (2) Control plane support (patches 02..03 and 07..09). These patches enables
            to insert SRH on locally emitted and/or forwarded packets, both with
            encap mode and with inline mode. The SRH insertion is controlled through
            the lightweight tunnels mechanism. Furthermore, patch 08 enables the
            applications to insert an SRH on a per-socket basis, through the
            setsockopt() system call. The mechanism to specify a per-socket
            Routing Header was already defined for RH0 and no special modification
            was performed on this side. However, the code to actually push the RH
            onto the packets had to be adapted for the SRH specifications.
        (3) HMAC support (patches 04..06). These patches adds the support of the
            HMAC TLV verification for the dataplane part, and generation for
            the control plane part. Two hashing algorithms are supported
            (SHA-1 as legacy and SHA-256 as required by the IETF draft), but
            additional algorithms can be easily supported by simply adding an
            entry into an array.

[1] https://tools.ietf.org/html/draft-ietf-spring-segment-routing-09
[2] https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-02

David Lebrun (9):
  ipv6: implement dataplane support for rthdr type 4 (Segment Routing
    Header)
  ipv6: sr: add code base for control plane support of SR-IPv6
  ipv6: sr: add support for SRH encapsulation and injection with
    lwtunnels
  ipv6: sr: add core files for SR HMAC support
  ipv6: sr: implement API to control SR HMAC structure
  ipv6: sr: add calls to verify and insert HMAC signatures
  ipv6: add source address argument for ipv6_push_nfrag_opts
  ipv6: sr: add support for SRH injection through setsockopt
  ipv6: sr: add documentation file for per-interface sysctls

 Documentation/networking/seg6-sysctl.txt |  18 ++
 include/linux/ipv6.h                     |   4 +
 include/linux/seg6.h                     |   6 +
 include/linux/seg6_genl.h                |   6 +
 include/linux/seg6_hmac.h                |   6 +
 include/linux/seg6_iptunnel.h            |   6 +
 include/net/ipv6.h                       |   3 +-
 include/net/netns/ipv6.h                 |   1 +
 include/net/seg6.h                       |  42 +++
 include/net/seg6_hmac.h                  |  62 +++++
 include/uapi/linux/ipv6.h                |   3 +
 include/uapi/linux/lwtunnel.h            |   1 +
 include/uapi/linux/seg6.h                |  48 ++++
 include/uapi/linux/seg6_genl.h           |  32 +++
 include/uapi/linux/seg6_hmac.h           |  20 ++
 include/uapi/linux/seg6_iptunnel.h       |  41 +++
 net/core/lwtunnel.c                      |   2 +
 net/ipv6/Kconfig                         |  12 +
 net/ipv6/Makefile                        |   3 +-
 net/ipv6/addrconf.c                      |  28 ++
 net/ipv6/af_inet6.c                      |   9 +-
 net/ipv6/exthdrs.c                       | 276 ++++++++++++++++++-
 net/ipv6/ip6_output.c                    |   5 +-
 net/ipv6/ip6_tunnel.c                    |   2 +-
 net/ipv6/ipv6_sockglue.c                 |   2 +
 net/ipv6/seg6.c                          | 386 ++++++++++++++++++++++++++
 net/ipv6/seg6_hmac.c                     | 456 +++++++++++++++++++++++++++++++
 net/ipv6/seg6_iptunnel.c                 | 383 ++++++++++++++++++++++++++
 28 files changed, 1851 insertions(+), 12 deletions(-)
 create mode 100644 Documentation/networking/seg6-sysctl.txt
 create mode 100644 include/linux/seg6.h
 create mode 100644 include/linux/seg6_genl.h
 create mode 100644 include/linux/seg6_hmac.h
 create mode 100644 include/linux/seg6_iptunnel.h
 create mode 100644 include/net/seg6.h
 create mode 100644 include/net/seg6_hmac.h
 create mode 100644 include/uapi/linux/seg6.h
 create mode 100644 include/uapi/linux/seg6_genl.h
 create mode 100644 include/uapi/linux/seg6_hmac.h
 create mode 100644 include/uapi/linux/seg6_iptunnel.h
 create mode 100644 net/ipv6/seg6.c
 create mode 100644 net/ipv6/seg6_hmac.c
 create mode 100644 net/ipv6/seg6_iptunnel.c

-- 
2.7.3

^ permalink raw reply

* Re: [PATCH v6 4/4] mac80211: multicast to unicast conversion
From: Johannes Berg @ 2016-10-27 12:29 UTC (permalink / raw)
  To: Michael Braun
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	projekt-wlan-3kN+8DYepx7zMJDuovMtMLNAH6kLmebB,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1476119543-24509-4-git-send-email-michael-dev-1SGGS//iJ+Y38rf8aCqVIw@public.gmane.org>


> @@ -2242,6 +2242,20 @@ static int ieee80211_set_wds_peer(struct wiphy
> *wiphy, struct net_device *dev,
>  	return 0;
>  }
>  
> +static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
> +					      struct net_device
> *dev,
> +					      const bool enabled)

I don't understand why you put this with set_wds_peer, please add it
here also at the end like anywhere else - I've changed that for you in
the cfg80211 patch.

> +	struct ieee80211_sub_if_data *sdata =
> IEEE80211_DEV_TO_SUB_IF(dev);
> +
> +	if (sdata->vif.type != NL80211_IFTYPE_AP)
> +		return -1;

Not needed. Also, don't ever just blindly return -1 in the kernel, that
means -EPERM, i.e. permission denied.

> +/* rewrite destination mac address */

that's a pretty useless comment ...

> +/* Check if multicast to unicast conversion is needed and do it.
> + * Returns 1 if skb was freed and should not be send out.

Follow kernel convention of returning a negative error code, e.g.
-ENOTCONN in this case, and remove this comment.

> +	/* clone packets and update destination mac */
> +	list_for_each_entry_rcu(sta, &local->sta_list, list) {
> +		if (sdata != sta->sdata)
> +			continue;
> +		if (unlikely(!ether_addr_equal(eth->h_source, sta-
> >sta.addr)))
> +			/* do not send back to source */
> +			continue;
> +		if (!prev) {
> +			prev = sta;
> +			continue;
> +		}
> +		cloned_skb = skb_clone(skb, GFP_ATOMIC);
> +		if (unlikely(ieee80211_change_da(cloned_skb, prev)))
> {
> +			dev_kfree_skb(cloned_skb);
> +			continue;
> +		}
> +		__ieee80211_subif_start_xmit(cloned_skb, cloned_skb-
> >dev, 0);

I don't like the recursion here.

I think we can call this from ieee80211_subif_start_xmit(), and then do
something like

if (unlikely(multicast)) {
	struct skb_queue queue;

	__skb_queue_head_init(&queue);
	convert_frames(
	while ((skb = __skb_dequeue(&queue))
		__ieee80211_subif_start_xmit(...);
} else {
	__ieee80211_subif_start_xmit(...);
}

Yes, that's a little less efficient (RCU stuff, although that could
even move in theory), but it avoids the recursion which imho is better.

johannes

^ permalink raw reply

* Re: [PATCH] net: smsc911x: Synchronize the runtime PM status during system suspend
From: Geert Uytterhoeven @ 2016-10-27 11:54 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rafael J. Wysocki, Alan Stern, Linux PM list,
	netdev@vger.kernel.org, Len Brown, Pavel Machek, Kevin Hilman,
	Lina Iyer, Jon Hunter, Marek Szyprowski, Linus Walleij,
	Steve Glendinning
In-Reply-To: <CAPDyKFprWcita5sbdwTqbdi2CMW4gx7_UB7+kMd9R2rqXdcALQ@mail.gmail.com>

Hi Ulf,

On Thu, Oct 27, 2016 at 1:53 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On 27 October 2016 at 13:41, Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>> On Thu, Oct 27, 2016 at 1:23 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>>> The smsc911c driver puts its device into low power state when entering
>>> system suspend. Although it doesn't update the device's runtime PM status
>>> to RPM_SUSPENDED, which causes problems for a parent device.
>>>
>>> In particular, when the runtime PM status of the parent is requested to be
>>> updated to RPM_SUSPENDED, the runtime PM core prevent this, because it's
>>> forbidden to runtime suspend a device, which has an active child.
>>>
>>> Fix this by updating the runtime PM status of the smsc911x device to
>>> RPM_SUSPENDED during system suspend. In system resume, let's reverse that
>>> action by runtime resuming the device and thus also the parent.
>>
>> Thanks for your patch!
>>
>> The changelog sounds quite innocent, but this does fix a system crash
>> during resume from s2ram.
>>
>>> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
>>> Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
>>> Cc: Steve Glendinning <steve.glendinning@shawell.net>
>>> Fixes: 8b1107b85efd ("PM / Runtime: Don't allow to suspend a device with an active child")
>>
>> While the abovementioned commit made the problem visible, the root cause
>> was present before, right?
>
> Yes.
>
>>> ---
>>>
>>> Note that the commit this change fixes is currently queued for 4.10 via
>>> Rafael's linux-pm tree. So this fix should go via that tree as well.
>>
>> Alternatively, this could go in in v4.9 to avoid the problem from ever
>> appearing in upstream?
>
> Makes perfect sense! In that case we should remove the fixes tag.
>
> Rafael, can you pick this up for 4.9 rc[n]?

Actually I was thinking about DaveM and the network tree instead.

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] net: smsc911x: Synchronize the runtime PM status during system suspend
From: Ulf Hansson @ 2016-10-27 11:23 UTC (permalink / raw)
  To: Rafael J. Wysocki, Alan Stern, Ulf Hansson, linux-pm, netdev
  Cc: Len Brown, Pavel Machek, Kevin Hilman, Geert Uytterhoeven,
	Lina Iyer, Jon Hunter, Marek Szyprowski, Linus Walleij,
	Steve Glendinning

The smsc911c driver puts its device into low power state when entering
system suspend. Although it doesn't update the device's runtime PM status
to RPM_SUSPENDED, which causes problems for a parent device.

In particular, when the runtime PM status of the parent is requested to be
updated to RPM_SUSPENDED, the runtime PM core prevent this, because it's
forbidden to runtime suspend a device, which has an active child.

Fix this by updating the runtime PM status of the smsc911x device to
RPM_SUSPENDED during system suspend. In system resume, let's reverse that
action by runtime resuming the device and thus also the parent.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Steve Glendinning <steve.glendinning@shawell.net>
Fixes: 8b1107b85efd ("PM / Runtime: Don't allow to suspend a device with an active child")
---

Note that the commit this change fixes is currently queued for 4.10 via
Rafael's linux-pm tree. So this fix should go via that tree as well.

---
 drivers/net/ethernet/smsc/smsc911x.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index e9b8579..65fca9c 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -2584,6 +2584,9 @@ static int smsc911x_suspend(struct device *dev)
 		PMT_CTRL_PM_MODE_D1_ | PMT_CTRL_WOL_EN_ |
 		PMT_CTRL_ED_EN_ | PMT_CTRL_PME_EN_);
 
+	pm_runtime_disable(dev);
+	pm_runtime_set_suspended(dev);
+
 	return 0;
 }
 
@@ -2593,6 +2596,9 @@ static int smsc911x_resume(struct device *dev)
 	struct smsc911x_data *pdata = netdev_priv(ndev);
 	unsigned int to = 100;
 
+	pm_runtime_enable(dev);
+	pm_runtime_resume(dev);
+
 	/* Note 3.11 from the datasheet:
 	 * 	"When the LAN9220 is in a power saving state, a write of any
 	 * 	 data to the BYTE_TEST register will wake-up the device."
-- 
1.9.1


^ permalink raw reply related

* Re: [PATCH (net.git) 2/3] stmmac: fix PTP support for GMAC4
From: Rayagond Kokatanur @ 2016-10-27 10:51 UTC (permalink / raw)
  To: Giuseppe CAVALLARO
  Cc: netdev, alexandre.torgue, Richard Cochran, linux-kernel,
	seraphin.bonnaffe
In-Reply-To: <743ffe4a-2c8f-e5f7-f93c-6be16a8a0350@st.com>

On Thu, Oct 27, 2016 at 4:02 PM, Giuseppe CAVALLARO
<peppe.cavallaro@st.com> wrote:
> Hello Rayagond !
>
> On 10/27/2016 12:25 PM, Rayagond Kokatanur wrote:
>>>
>>> +static int dwmac4_wrback_get_rx_timestamp_status(void *desc, u32 ats)
>>> >  {
>>> >         struct dma_desc *p = (struct dma_desc *)desc;
>>> > +       int ret = -EINVAL;
>>> > +
>>> > +       /* Get the status from normal w/b descriptor */
>>> > +       if (likely(p->des3 & TDES3_RS1V)) {
>>> > +               if (likely(p->des1 & RDES1_TIMESTAMP_AVAILABLE)) {
>>> > +                       int i = 0;
>>> > +
>>> > +                       /* Check if timestamp is OK from context
>>> > descriptor */
>>> > +                       do {
>>> > +                               ret = dwmac4_rx_check_timestamp(desc);
>>
>> Here, "desc" is not pointing to next descriptor (ie context
>> descriptor). Driver should check the context descriptor.
>
>
> you are right and this is done by the caller:  stmmac_get_rx_hwtstamp

Yes.

>
> Cheers
> peppe
>



-- 
wwr
Rayagond

^ permalink raw reply

* Re: [PATCH 2/5] Documentation: devicetree: net: add NS2 bindings to amac
From: Andrew Lunn @ 2016-10-27  9:17 UTC (permalink / raw)
  To: Jon Mason
  Cc: Mark Rutland, devicetree, Florian Fainelli, netdev, linux-kernel,
	Rob Herring, bcm-kernel-feedback-list, rafal, David Miller,
	linux-arm-kernel
In-Reply-To: <1477510561-17035-3-git-send-email-jon.mason@broadcom.com>

On Wed, Oct 26, 2016 at 03:35:58PM -0400, Jon Mason wrote:
> Signed-off-by: Jon Mason <jon.mason@broadcom.com>
> ---
>  Documentation/devicetree/bindings/net/brcm,amac.txt | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/net/brcm,amac.txt b/Documentation/devicetree/bindings/net/brcm,amac.txt
> index ba5ecc1..f92caee 100644
> --- a/Documentation/devicetree/bindings/net/brcm,amac.txt
> +++ b/Documentation/devicetree/bindings/net/brcm,amac.txt
> @@ -2,15 +2,18 @@ Broadcom AMAC Ethernet Controller Device Tree Bindings
>  -------------------------------------------------------------
>  
>  Required properties:
> - - compatible:	"brcm,amac" or "brcm,nsp-amac"
> + - compatible:	"brcm,amac", "brcm,nsp-amac", or "brcm,ns2-amac"
>   - reg:		Address and length of the GMAC registers,
>  		Address and length of the GMAC IDM registers
> +		Address and length of the NIC Port Manager registers (optional)
>   - reg-names:	Names of the registers.  Must have both "amac_base" and
> -		"idm_base"
> +		"idm_base". "nicpm_base" is optional (required for NS2)
>   - interrupts:	Interrupt number
>  
>  Optional properties:
>  - mac-address:	See ethernet.txt file in the same directory
> +- brcm,enet-phy-lane-swap:
> +		boolean; Swap the PHY lanes (needed on some SKUs of NS2)

Maybe i'm missing something here, but the patch to the PHY swapped the
lanes. This seems to be a PHY property, not a MAC property. And it
swapped them unconditionally....

	Andrew

^ permalink raw reply

* Re: Bug in cadence macb driver
From: Steve Bennett @ 2016-10-27  9:16 UTC (permalink / raw)
  To: Nicolas Ferre; +Cc: netdev, Cyrille Pitchen
In-Reply-To: <1da8552b-a7e8-0850-31ce-f608504b01a1@atmel.com>


> On 27 Oct 2016, at 7:05 PM, Nicolas Ferre <nicolas.ferre@atmel.com> wrote:
> 
> Le 27/10/2016 à 09:22, Steve Bennett a écrit :
>> Hi,
>> 
>> We are seeing the following problem in the cadence macb driver.
>> 
>> BUG: scheduling while atomic: kworker/0:1/44/0x00000002
>> Modules linked in: 8021q of_serial beacon_sigproc io_processor emsattr ems_dma_fifo xaxi_dma libgpioreg leds_gpio
>> CPU: 0 PID: 44 Comm: kworker/0:1 Not tainted 4.4.0 #1
>> Hardware name: Xilinx Zynq Platform
>> Workqueue: events macb_tx_error_task
>> [<c0016a18>] (unwind_backtrace) from [<c0012ad4>] (show_stack+0x10/0x14)
>> [<c0012ad4>] (show_stack) from [<c01b089c>] (dump_stack+0x88/0xd8)
>> [<c01b089c>] (dump_stack) from [<c008e94c>] (__schedule_bug+0x48/0x60)
>> [<c008e94c>] (__schedule_bug) from [<c03592d4>] (__schedule+0x80/0x4c4)
>> [<c03592d4>] (__schedule) from [<c0359968>] (schedule+0xac/0xd4)
>> [<c0359968>] (schedule) from [<c035c284>] (schedule_hrtimeout_range_clock+0xc0/0xf4)
>> [<c035c284>] (schedule_hrtimeout_range_clock) from [<c035bebc>] (usleep_range+0x44/0x48)
>> [<c035bebc>] (usleep_range) from [<c025ec48>] (macb_tx_error_task+0xa0/0x270)
>> [<c025ec48>] (macb_tx_error_task) from [<c0035b6c>] (process_one_work+0x1b8/0x2f0)
>> [<c0035b6c>] (process_one_work) from [<c0035fb4>] (worker_thread+0x2e4/0x3f0)
>> [<c0035fb4>] (worker_thread) from [<c003ac94>] (kthread+0xdc/0xf0)
>> [<c003ac94>] (kthread) from [<c000f698>] (ret_from_fork+0x14/0x3c)
>> 
>> Here we are running at 10M half duplex, so tx is slow.
>> The problem was introduced in e4bfd971b where a spinlock is taken before
> 
> I can't find this commit ID. Can you please give the one on Linus' tree
> together with the commit subject?

Ahh yes. My apologies. Here it is in Linus' tree

commit 02c958dd344643259212e50f04f0ec90a3ace34c
Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Date:   Fri Dec 12 13:26:44 2014 +0100

    net/macb: add TX multiqueue support for gem

> 
> Thanks,
> 
>> calling macb_halt_tx() which may then sleep waiting for the current packet to finish
>> transmitting.
>> 
>> We don't need the spinlock on our platform since we don't have multiple queues, but
>> this should be fixed properly in the general case.
>> 
>> Cheers,
>> Steve
>> 
>> --
>> µWeb: Embedded Web Framework - http://uweb.workware.net.au/
>> WorkWare Systems Pty Ltd
>> W: www.workware.net.au      P: +61 434 921 300
>> E: steveb@workware.net.au
>> 
>> 
>> 
>> 
>> 
>> 
> 
> 
> -- 
> Nicolas Ferre

^ permalink raw reply

* Re: [PATCH] can: fix warning in bcm_connect/proc_register
From: Marc Kleine-Budde @ 2016-10-27  8:45 UTC (permalink / raw)
  To: Cong Wang, Oliver Hartkopp
  Cc: Andrey Konovalov, David Miller, linux-can,
	Linux Kernel Network Developers, LKML, syzkaller,
	Kostya Serebryany, Alexander Potapenko, Dmitry Vyukov,
	Eric Dumazet
In-Reply-To: <CAM_iQpVmhH_9vQcTw+wP1qmPLOijZZrMZrq=_e4i5fUXYDON0Q@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 1273 bytes --]

On 10/24/2016 10:17 PM, Cong Wang wrote:
> On Mon, Oct 24, 2016 at 1:10 PM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
>> On Mon, Oct 24, 2016 at 12:11 PM, Oliver Hartkopp
>> <socketcan@hartkopp.net> wrote:
>>>         if (proc_dir) {
>>>                 /* unique socket address as filename */
>>>                 sprintf(bo->procname, "%lu", sock_i_ino(sk));
>>>                 bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
>>>                                                      proc_dir,
>>>                                                      &bcm_proc_fops, sk);
>>> +               if (!bo->bcm_proc_read) {
>>> +                       ret = -ENOMEM;
>>> +                       goto fail;
>>> +               }
>>
>> Well, I meant we need to call proc_create_data() once per socket,
>> so we need a check before proc_create_data() too.
> 
> Hmm, bo->bound should guarantee it, so never mind, your patch
> looks fine.

Can I add your Acked-by?

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Re: [PATCH 1/5] net: phy: broadcom: Add BCM54810 phy entry
From: Andrew Lunn @ 2016-10-27  9:15 UTC (permalink / raw)
  To: Jon Mason
  Cc: Mark Rutland, devicetree, Florian Fainelli, netdev, linux-kernel,
	Vikas Soni, Rob Herring, bcm-kernel-feedback-list, rafal,
	David Miller, linux-arm-kernel
In-Reply-To: <1477510561-17035-2-git-send-email-jon.mason@broadcom.com>

On Wed, Oct 26, 2016 at 03:35:57PM -0400, Jon Mason wrote:
> From: Vikas Soni <vsoni@broadcom.com>
> 
> Add BCM54810 phy entry

Hi Jon, Vikis

The subject line is a bit misleading. It does more than add a PHY ID
entry.

> Signed-off-by: Vikas Soni <vsoni@broadcom.com>
> Signed-off-by: Jon Mason <jon.mason@broadcom.com>
> ---
>  drivers/net/phy/Kconfig    |  2 +-
>  drivers/net/phy/broadcom.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/brcmphy.h    |  7 +++++
>  3 files changed, 73 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 45f68ea..31967ca 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -217,7 +217,7 @@ config BROADCOM_PHY
>  	select BCM_NET_PHYLIB
>  	---help---
>  	  Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
> -	  BCM5481 and BCM5482 PHYs.
> +	  BCM5481, BCM54810 and BCM5482 PHYs.
>  
>  config CICADA_PHY
>  	tristate "Cicada PHYs"
> diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
> index 870327e..cdce761 100644
> --- a/drivers/net/phy/broadcom.c
> +++ b/drivers/net/phy/broadcom.c
> @@ -35,6 +35,35 @@ static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
>  	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
>  }
>  
> +static int bcm54810_config(struct phy_device *phydev)
> +{
> +	int rc;
> +
> +	/* Disable BroadR-Reach */
> +	rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, 0);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* SKEW DISABLE */
> +	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
> +				  0xF0E0);
> +	if (rc < 0)
> +		return rc;
> +
> +	/* DELAY DISABLE */
> +	rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
> +				  0x7000);

This driver mostly uses symbolic names, not #defines. Please can you
use #defines here and else were in this patch.


> +	if (rc < 0)
> +		return rc;
> +
> +	/* DELAY DISABLE */
> +	rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, 0);
> +	if (rc < 0)
> +		return rc;

Twice the same comment?

> +
> +	return 0;
> +}
> +
>  /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
>  static int bcm50610_a0_workaround(struct phy_device *phydev)
>  {
> @@ -207,6 +236,20 @@ static int bcm54xx_config_init(struct phy_device *phydev)
>  	    (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
>  		bcm54xx_adjust_rxrefclk(phydev);
>  
> +	if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
> +		err = bcm54810_config(phydev);
> +		if (err)
> +			return err;
> +
> +		reg = phy_read(phydev, MII_BMCR);
> +		if (reg < 0)
> +			return reg;
> +
> +		err = phy_write(phydev, MII_BMCR, reg & ~BMCR_PDOWN);
> +		if (err)
> +			return err;

This seems a bit odd. I would expect the PHY core correctly handles
the PHY being powered down. Can you explain this a bit more, why it is
needed.

	Thanks
		Andrew

^ permalink raw reply

* Re: [PATCH net-next RFC WIP] Patch for XDP support for virtio_net
From: Jesper Dangaard Brouer @ 2016-10-27  8:55 UTC (permalink / raw)
  To: David Miller; +Cc: mst, shrijeet, tom, netdev, shm, roopa, nikolay, brouer
In-Reply-To: <20161026.131122.318218609871320141.davem@davemloft.net>

On Wed, 26 Oct 2016 13:11:22 -0400 (EDT)
David Miller <davem@davemloft.net> wrote:

> From: "Michael S. Tsirkin" <mst@redhat.com>
> Date: Wed, 26 Oct 2016 20:07:19 +0300
> 
> > On Wed, Oct 26, 2016 at 12:52:45PM -0400, David Miller wrote:  
> >> From: "Michael S. Tsirkin" <mst@redhat.com>
> >> Date: Wed, 26 Oct 2016 19:36:45 +0300
> >>   
> >> > On Wed, Oct 26, 2016 at 03:52:02PM +0200, Jesper Dangaard Brouer wrote:  
> >> >> On Sat, 22 Oct 2016 04:07:23 +0000
> >> >> Shrijeet Mukherjee <shrijeet@gmail.com> wrote:
> >> >>   
> >> >> > This patch adds support for xdp ndo and also inserts the xdp program
> >> >> > call into the merged RX buffers and big buffers paths  
> >> >> 
> >> >> I really appreciate you are doing this for virtio_net.
> >> >> 
> >> >> My first question is: Is the (packet) page data writable?
> >> >> (MST might be able to answer?)
> >> >> 
> >> >> As this is currently an XDP requirement[1].    
> >> > 
> >> > I'm not sure I understand what does writable mean.
> >> > Could you explain a bit more pls?
> >> > We do copy data into skb ATM but I plan to change that.  
> >> 
> >> The packet data area must be writable,  
> > 
> > This is the part I don't fully understand.
> > It's in RAM so of course it's writeable.  
> 
> Pages in SKB frag lists are not usually writable, because they share
> space with data from other packets the way drivers usually carve up
> pages to receive packets into.
> 
> It is therefore illegal for the networking code to write into SKB frag
> pages.
> 
> Pages used for XDP processed packets must not have this restriction.
> 
> > We share pages between arbitrary multiple packets. I think that's
> > OK  
> 
> That's exactly what is not allowed with XDP.
> 
> Each packet must be the sole user of a page, otherwise the semantics
> required by XDP are not met.

Looking at the virtio_net.c code, the function call receive_big() might
actually be okay for XDP, unless the incoming packet is larger than
PAGE_SIZE and thus uses several pages (via a linked list in page->private).

The receive_mergeable() does not look compatible with XDP.

-- 
Best regards,
  Jesper Dangaard Brouer
  MSc.CS, Principal Kernel Engineer at Red Hat
  Author of http://www.iptv-analyzer.org
  LinkedIn: http://www.linkedin.com/in/brouer

^ permalink raw reply

* Re: Bug in cadence macb driver
From: Nicolas Ferre @ 2016-10-27  9:05 UTC (permalink / raw)
  To: Steve Bennett, netdev, Cyrille Pitchen
In-Reply-To: <3DDE7A42-84A6-44DC-9919-C0D9166E87EF@workware.net.au>

Le 27/10/2016 à 09:22, Steve Bennett a écrit :
> Hi,
> 
> We are seeing the following problem in the cadence macb driver.
> 
> BUG: scheduling while atomic: kworker/0:1/44/0x00000002
> Modules linked in: 8021q of_serial beacon_sigproc io_processor emsattr ems_dma_fifo xaxi_dma libgpioreg leds_gpio
> CPU: 0 PID: 44 Comm: kworker/0:1 Not tainted 4.4.0 #1
> Hardware name: Xilinx Zynq Platform
> Workqueue: events macb_tx_error_task
> [<c0016a18>] (unwind_backtrace) from [<c0012ad4>] (show_stack+0x10/0x14)
> [<c0012ad4>] (show_stack) from [<c01b089c>] (dump_stack+0x88/0xd8)
> [<c01b089c>] (dump_stack) from [<c008e94c>] (__schedule_bug+0x48/0x60)
> [<c008e94c>] (__schedule_bug) from [<c03592d4>] (__schedule+0x80/0x4c4)
> [<c03592d4>] (__schedule) from [<c0359968>] (schedule+0xac/0xd4)
> [<c0359968>] (schedule) from [<c035c284>] (schedule_hrtimeout_range_clock+0xc0/0xf4)
> [<c035c284>] (schedule_hrtimeout_range_clock) from [<c035bebc>] (usleep_range+0x44/0x48)
> [<c035bebc>] (usleep_range) from [<c025ec48>] (macb_tx_error_task+0xa0/0x270)
> [<c025ec48>] (macb_tx_error_task) from [<c0035b6c>] (process_one_work+0x1b8/0x2f0)
> [<c0035b6c>] (process_one_work) from [<c0035fb4>] (worker_thread+0x2e4/0x3f0)
> [<c0035fb4>] (worker_thread) from [<c003ac94>] (kthread+0xdc/0xf0)
> [<c003ac94>] (kthread) from [<c000f698>] (ret_from_fork+0x14/0x3c)
> 
> Here we are running at 10M half duplex, so tx is slow.
> The problem was introduced in e4bfd971b where a spinlock is taken before

I can't find this commit ID. Can you please give the one on Linus' tree
together with the commit subject?

Thanks,

> calling macb_halt_tx() which may then sleep waiting for the current packet to finish
> transmitting.
> 
> We don't need the spinlock on our platform since we don't have multiple queues, but
> this should be fixed properly in the general case.
> 
> Cheers,
> Steve
> 
> --
> µWeb: Embedded Web Framework - http://uweb.workware.net.au/
> WorkWare Systems Pty Ltd
> W: www.workware.net.au      P: +61 434 921 300
> E: steveb@workware.net.au
> 
> 
> 
> 
> 
> 


-- 
Nicolas Ferre

^ permalink raw reply

* Re: [PATCH] can: fix warning in bcm_connect/proc_register
From: Marc Kleine-Budde @ 2016-10-27  8:47 UTC (permalink / raw)
  To: Oliver Hartkopp, andreyknvl, xiyou.wangcong, davem
  Cc: linux-can, netdev, linux-kernel, syzkaller, kcc, glider, dvyukov,
	edumazet
In-Reply-To: <20161024191126.30256-1-socketcan@hartkopp.net>


[-- Attachment #1.1: Type: text/plain, Size: 856 bytes --]

On 10/24/2016 09:11 PM, Oliver Hartkopp wrote:
> Andrey Konovalov reported an issue with proc_register in bcm.c.
> As suggested by Cong Wang this patch adds a lock_sock() protection and
> a check for unsuccessful proc_create_data() in bcm_connect().
> 
> Reference: http://marc.info/?l=linux-netdev&m=147732648731237
> 
> Reported-by: Andrey Konovalov <andreyknvl@google.com>
> Suggested-by: Cong Wang <xiyou.wangcong@gmail.com>
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>

Added with Andrey Konovalov Tested-by to can/master with stable on Cc.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Re: [PATCH v7 0/6] Add eBPF hooks for cgroups
From: Daniel Mack @ 2016-10-27  8:40 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: htejun-b10kYP2dOMg, daniel-FeC+5ew28dpmcu3hnIyYJQ,
	ast-b10kYP2dOMg, davem-fT/PcQaiUtIeIZ0/mPfg9Q, kafai-b10kYP2dOMg,
	fw-HFFVJYpyMKqzQB+pC5nmwQ, harald-H+wXaHxf7aLQT0dZR+AlfA,
	netdev-u79uwXL29TY76Z2rM5mHXA, sargun-GaZTRHToo+CzQB+pC5nmwQ,
	cgroups-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20161026195933.GA2031@salvia>

On 10/26/2016 09:59 PM, Pablo Neira Ayuso wrote:
> On Tue, Oct 25, 2016 at 12:14:08PM +0200, Daniel Mack wrote:
> [...]
>>   Dumping programs once they are installed is problematic because of
>>   the internal optimizations done to the eBPF program during its
>>   lifetime. Also, the references to maps etc. would need to be
>>   restored during the dump.
>>
>>   Just exposing whether or not a program is attached would be
>>   trivial to do, however, most easily through another bpf(2)
>>   command. That can be added later on though.
> 
> I don't know if anyone told you, but during last netconf, this topic
> took a bit of time of discussion and it was controversial, I would say
> 1/3 of netdev hackers there showed their concerns, and that's
> something that should not be skipped IMO.
> 
> While xdp is pushing bpf programs at the very early packet path, not
> interfering with the stack, before even entering the generic ingress
> path. But this is adding hooks to push bpf programs in the middle of
> our generic stack, this is way different domain.

It's not anything new. These hooks live on the very same level as
SO_ATTACH_FILTER. The only differences are that the BPF programs are
stored in the cgroup, and not in the socket, and that they exist for
egress as well.

> I would really like to explore way earlier filtering, by extending
> socket lookup facilities. So far the problem seems to be that we need
> to lookup for broadcast/multicast UDP sockets and those cannot be
> attach via the usual skb->sk.

We've been there. We've discussed all that. And we concluded that doing
early demux in the input filter path is not the right approach. That was
my very first take on that issue back in June 2015 (!), and it was
rightfully turned down for good reasons.

Adding it there would mean we need to early-demux *every* packet as soon
as there is *any* such rule installed, and that renders many
optimizations in the kernel to drop traffic that has no local receiver
useless.

> I think it would be possible to wrap
> around this socket code in functions so we can invoke it. I guess
> filtering of UDP and TCP should be good for you at this stage. This
> would require more work though, but this would come with no hooks in
> the stack and packets will not have to consume *lots of cycles* just
> to be dropped before entering the socket queue.
>
> How useful can be to drop lots of unwanted traffic at such a late
> stage? How would the performance numbers to drop packets would look
> like? Extremely bad, I predict.

I fear I'm repeating myself here, but this is unfounded. I'm not sure
why you keep bringing it up. As I said weeks ago - just loading the
netfilter modules without any rules deployed has more impact than
running the example program in 6/6 on every packet in the test traffic.
Please give it a shot yourself.

Also, the eBPF programs can well be used in combination with existing
netfilter setups. There is no reason to not combine the two levels of
filtering. Both have their right to exist, and nobody is taking anything
away.


Thanks,
Daniel

^ permalink raw reply

* pull-request: mac80211 2016-10-27
From: Johannes Berg @ 2016-10-27  8:05 UTC (permalink / raw)
  To: David Miller
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA

Hi Dave,

Before I go off for LPC and the workshop and everything, I have
two fixes. Neither is very important, but I figured I'd get them
out since I won't have any others soon.

Let me know if there's any problem.

Thanks,
johannes



The following changes since commit b4f0fd4baa90ecce798e0d26d1cce8f4457f2028:

  qed: Use list_move_tail instead of list_del/list_add_tail (2016-10-18 16:40:41 -0400)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git tags/mac80211-for-davem-2016-10-27

for you to fetch changes up to b4f7f4ad425a84fd5d40da21aff8681997b25ff9:

  mac80211: fix some sphinx warnings (2016-10-26 08:01:07 +0200)

----------------------------------------------------------------
Just two fixes:
 * a fix to process all events while suspending, so any
   potential calls into the driver are done before it is
   suspended
 * small markup fixes for the sphinx documentation conversion
   that's coming into the tree via the doc tree

----------------------------------------------------------------
Jani Nikula (1):
      mac80211: fix some sphinx warnings

Johannes Berg (1):
      cfg80211: process events caused by suspend before suspending

 include/net/mac80211.h | 21 +++++++++++++--------
 net/wireless/sysfs.c   |  5 ++++-
 2 files changed, 17 insertions(+), 9 deletions(-)

^ permalink raw reply

* [PATCHv2 iproute2 net-next] tc: m_mirred: Fix parsing of 'index' optional argument
From: Shmulik Ladkani @ 2016-10-27  7:36 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Jamal Hadi Salim, netdev, Shmulik Ladkani

Code in parse_mirred() suggests "index" argument can be placed either
after the egress/ingress clause, or as the first argument (after
"action mirred").

However, parse_direction() fails to correctly parse "index" if it's the
first argument.

For example:

 # tc filter add ... action mirred index 5
 RTNETLINK answers: Invalid argument
 (unnecessary RTNETLINK issued, should have been parse error)

 # tc filter add ... action mirred index 5 egress redirect dev eth0
 bad action type egress
 (should have been parsed successfully)

Fix parse_direction as follows:
 - continue parsing after valid "index" is seen
 - don't issue the RTNETLINK unless valid "egress"/"ingress" is seen

Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
---
 v2: rebased to recent tip of net-next, amended log message

 An alternative solution: banning "index" as 1st argument in parse_mirred

 tc/m_mirred.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/tc/m_mirred.c b/tc/m_mirred.c
index 01f916d..64d870a 100644
--- a/tc/m_mirred.c
+++ b/tc/m_mirred.c
@@ -105,9 +105,8 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
 				}
 				iok++;
 				if (!ok) {
-					argc--;
-					argv++;
-					break;
+					NEXT_ARG();
+					continue;
 				}
 			} else if (!ok) {
 				fprintf(stderr, "was expecting egress or ingress (%s)\n", *argv);
@@ -150,7 +149,7 @@ parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
 		NEXT_ARG();
 	}
 
-	if (!ok && !iok) {
+	if (!ok) {
 		return -1;
 	}
 
-- 
2.10.1

^ permalink raw reply related

* Bug in cadence macb driver
From: Steve Bennett @ 2016-10-27  7:22 UTC (permalink / raw)
  To: Nicolas Ferre, netdev, Cyrille Pitchen

Hi,

We are seeing the following problem in the cadence macb driver.

BUG: scheduling while atomic: kworker/0:1/44/0x00000002
Modules linked in: 8021q of_serial beacon_sigproc io_processor emsattr ems_dma_fifo xaxi_dma libgpioreg leds_gpio
CPU: 0 PID: 44 Comm: kworker/0:1 Not tainted 4.4.0 #1
Hardware name: Xilinx Zynq Platform
Workqueue: events macb_tx_error_task
[<c0016a18>] (unwind_backtrace) from [<c0012ad4>] (show_stack+0x10/0x14)
[<c0012ad4>] (show_stack) from [<c01b089c>] (dump_stack+0x88/0xd8)
[<c01b089c>] (dump_stack) from [<c008e94c>] (__schedule_bug+0x48/0x60)
[<c008e94c>] (__schedule_bug) from [<c03592d4>] (__schedule+0x80/0x4c4)
[<c03592d4>] (__schedule) from [<c0359968>] (schedule+0xac/0xd4)
[<c0359968>] (schedule) from [<c035c284>] (schedule_hrtimeout_range_clock+0xc0/0xf4)
[<c035c284>] (schedule_hrtimeout_range_clock) from [<c035bebc>] (usleep_range+0x44/0x48)
[<c035bebc>] (usleep_range) from [<c025ec48>] (macb_tx_error_task+0xa0/0x270)
[<c025ec48>] (macb_tx_error_task) from [<c0035b6c>] (process_one_work+0x1b8/0x2f0)
[<c0035b6c>] (process_one_work) from [<c0035fb4>] (worker_thread+0x2e4/0x3f0)
[<c0035fb4>] (worker_thread) from [<c003ac94>] (kthread+0xdc/0xf0)
[<c003ac94>] (kthread) from [<c000f698>] (ret_from_fork+0x14/0x3c)

Here we are running at 10M half duplex, so tx is slow.
The problem was introduced in e4bfd971b where a spinlock is taken before
calling macb_halt_tx() which may then sleep waiting for the current packet to finish
transmitting.

We don't need the spinlock on our platform since we don't have multiple queues, but
this should be fixed properly in the general case.

Cheers,
Steve

^ 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