Skip to contentSkip to conversation

ipv6: fix the bug when propagating Redirect Message

PATCH In review Duan Jiong 2 participants 6 messages Oct 23, 2012 – Dec 12, 2012

Subsystems (3)
lore
Share & tools
lore

Duan Jiong

author
#0 lore raw

PATCH 1/1 ipv6: fix the bug when propagating Redirect Message

Before using icmpv6_notify() to propagate redirect, change skb->data to poing the IP packet that triggered the sending of the Redirect.

---
 net/ipv6/ndisc.c |   39 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 39 insertions(+), 0 deletions(-)
net/ipv6/ndisc.c +39 -0
index ff36194..0f73303 100644
@@ -1334,6 +1334,11 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	int opt_len;
+	int opt_offset;
+	int ndisc_head_len;
+	struct nd_opt_hdr *nd_opt;
+	
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
 	switch (skb->ndisc_nodetype) {
 	case NDISC_NODETYPE_HOST:
@@ -1350,6 +1355,40 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
 		return;
 	}
 
+	ndisc_head_len = sizeof(struct icmp6hdr) + 2*sizeof(struct in6_addr);
+	if (!pskb_may_pull(skb, ndisc_head_len)) {
+		return;
+	}
+
+	nd_opt = (struct nd_opt_hdr *)(skb->data + ndisc_head_len);
+
+	opt_len = skb->tail - skb->transport_header - ndisc_head_len;
+	if (opt_len < 0) {
+		return;
+	}
+	while (opt_len) {
+		int l;
+	
+		if (opt_len < sizeof(struct nd_opt_hdr)) {
+			return;
+		}
+		l = nd_opt->nd_opt_len << 3;
+		if (opt_len < l || l == 0) {
+			return;
+		}
+		if (nd_opt->nd_opt_type == ND_OPT_REDIRECT_HDR) {
+			__skb_pull(skb, ndisc_head_len + opt_offset + 8);
+			break;
+		}
+		opt_len -= l;
+		nd_opt = ((void *)nd_opt) + 1;
+		opt_offset += 1;
+	}
+	
+	if (opt_len == 0) {
+		return;
+	}	
+
 	icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
 }
 
Signature · 1 line
1.7.4.4
#1 lore raw
Quoted 48 lines · Duan Jiong

Before using icmpv6_notify() to propagate redirect, change skb->data to poing the IP packet that triggered the sending of the Redirect.

---
 net/ipv6/ndisc.c |   39 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 39 insertions(+), 0 deletions(-)
net/ipv6/ndisc.c +24 -0
index ff36194..0f73303 100644
@@ -1334,6 +1334,11 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	int opt_len;
+	int opt_offset;
+	int ndisc_head_len;
+	struct nd_opt_hdr *nd_opt;
+	
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
 	switch (skb->ndisc_nodetype) {
 	case NDISC_NODETYPE_HOST:
@@ -1350,6 +1355,40 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
 		return;
 	}
 
+	ndisc_head_len = sizeof(struct icmp6hdr) + 2*sizeof(struct in6_addr);
+	if (!pskb_may_pull(skb, ndisc_head_len)) {
+		return;
+	}
+
+	nd_opt = (struct nd_opt_hdr *)(skb->data + ndisc_head_len);
+
+	opt_len = skb->tail - skb->transport_header - ndisc_head_len;
+	if (opt_len < 0) {
+		return;
+	}
+	while (opt_len) {
+		int l;
+	
+		if (opt_len < sizeof(struct nd_opt_hdr)) {
+			return;
+		}
+		l = nd_opt->nd_opt_len << 3;
+		if (opt_len < l || l == 0) {

net/ipv6/ndisc.c static void ndisc_redirect_rcv(struct sk_buff *skb) · line 1386

Quoted 10 lines
net/ipv6/ndisc.c +10 -0
+			return;
+		}
+		if (nd_opt->nd_opt_type == ND_OPT_REDIRECT_HDR) {
+			__skb_pull(skb, ndisc_head_len + opt_offset + 8);
+			break;
+		}
+		opt_len -= l;
+		nd_opt = ((void *)nd_opt) + 1;
+		opt_offset += 1;
+	}

Instead of the above loop, you could use ndisc_parse_options(). This does the same what you are doing here and it would make it a bit clearer what's going on.

#2 lore raw
Quoted 15 lines · Steffen Klassert
Quoted 14 lines · Duan Jiong
net/ipv6/ndisc.c +14 -0
+	while (opt_len) {
+		int l;
+	
+		if (opt_len < sizeof(struct nd_opt_hdr)) {
+			return;
+		}
+		l = nd_opt->nd_opt_len << 3;
+		if (opt_len < l || l == 0) {
+			return;
+		}
+		if (nd_opt->nd_opt_type == ND_OPT_REDIRECT_HDR) {
+			__skb_pull(skb, ndisc_head_len + opt_offset + 8);
+			break;
+		}
Quoted 9 lines

net/ipv6/ndisc.c static void ndisc_redirect_rcv(struct sk_buff *skb) · line 1386

Quoted 4 lines
net/ipv6/ndisc.c +4 -0
+		opt_len -= l;
+		nd_opt = ((void *)nd_opt) + 1;
+		opt_offset += 1;
+	}

Instead of the above loop, you could use ndisc_parse_options(). This does the same what you are doing here and it would make it a bit clearer what's going on.

Duan, are you going to update your patches? We really need fixes for the problems you have discovered.

Thanks!

author
#3 lore raw
In reply to Steffen Klassert
Quoted 55 lines · Steffen Klassert
Quoted 54 lines · Duan Jiong

Before using icmpv6_notify() to propagate redirect, change skb->data to poing the IP packet that triggered the sending of the Redirect.

---
 net/ipv6/ndisc.c |   39 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 39 insertions(+), 0 deletions(-)
net/ipv6/ndisc.c +30 -0
index ff36194..0f73303 100644
@@ -1334,6 +1334,11 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	int opt_len;
+	int opt_offset;
+	int ndisc_head_len;
+	struct nd_opt_hdr *nd_opt;
+	
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
 	switch (skb->ndisc_nodetype) {
 	case NDISC_NODETYPE_HOST:
@@ -1350,6 +1355,40 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
 		return;
 	}
 
+	ndisc_head_len = sizeof(struct icmp6hdr) + 2*sizeof(struct in6_addr);
+	if (!pskb_may_pull(skb, ndisc_head_len)) {
+		return;
+	}
+
+	nd_opt = (struct nd_opt_hdr *)(skb->data + ndisc_head_len);
+
+	opt_len = skb->tail - skb->transport_header - ndisc_head_len;
+	if (opt_len < 0) {
+		return;
+	}
+	while (opt_len) {
+		int l;
+	
+		if (opt_len < sizeof(struct nd_opt_hdr)) {
+			return;
+		}
+		l = nd_opt->nd_opt_len << 3;
+		if (opt_len < l || l == 0) {
+			return;
+		}
+		if (nd_opt->nd_opt_type == ND_OPT_REDIRECT_HDR) {
+			__skb_pull(skb, ndisc_head_len + opt_offset + 8);
+			break;
+		}
Quoted 9 lines

net/ipv6/ndisc.c static void ndisc_redirect_rcv(struct sk_buff *skb) · line 1386

Quoted 4 lines
net/ipv6/ndisc.c +4 -0
+		opt_len -= l;
+		nd_opt = ((void *)nd_opt) + 1;
+		opt_offset += 1;
+	}

Instead of the above loop, you could use ndisc_parse_options(). This does the same what you are doing here and it would make it a bit clearer what's going on.

I apologize for not replying to you earlier,and i will continue to update my patches.

Just like you said, i try to use ndisc_parse_options() to instead of the loop, but i find the skb->data can't be changed in function ndisc_parse_options() due to lack of arguments. So i think it is better to continue to use the loop. How do you think this?

Thanks!

#4 lore raw
Quoted 6 lines · Duan Jiong

Just like you said, i try to use ndisc_parse_options() to instead of the loop, but i find the skb->data can't be changed in function ndisc_parse_options() due to lack of arguments. So i think it is better to continue to use the loop. How do you think this?

You can change the data pointer after ndisc_parse_options(). Something like the (untested) patch below should do it.

 include/net/ndisc.h |    7 +++++++
 net/ipv6/ndisc.c    |   20 ++++++++++++++++++++
 2 files changed, 27 insertions(+)
include/net/ndisc.h +7 -0
index 980d263..c17bccd 100644
@@ -78,6 +78,13 @@ struct ra_msg {
 	__be32			retrans_timer;
 };
 
+struct rd_msg {
+        struct icmp6hdr	icmph;
+        struct in6_addr	target;
+        struct in6_addr	dest;
+	__u8		opt[0];
+};
+
 struct nd_opt_hdr {
 	__u8		nd_opt_type;
 	__u8		nd_opt_len;
net/ipv6/ndisc.c +20 -0
index 2edce30..9afd23f 100644
@@ -1333,6 +1333,12 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	u8 *hdr;
+	struct ndisc_options ndopts;
+	struct rd_msg *msg = (struct rd_msg *) skb_transport_header(skb);
+	u32 ndoptlen = skb->tail - (skb->transport_header +
+				    offsetof(struct rd_msg, opt));
+
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
 	switch (skb->ndisc_nodetype) {
 	case NDISC_NODETYPE_HOST:
@@ -1349,6 +1355,20 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
 		return;
 	}
 
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		ND_PRINTK(2, warn, "Redirect: invalid ND options\n");
+		return;
+	}
+
+	if (!ndopts.nd_opts_rh)
+		return;
+
+	hdr = (u8 *) ndopts.nd_opts_rh;
+	hdr += 8;
+
+	if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
+		return;
+
 	icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
 }
 
Signature · 1 line
1.7.9.5
author
#5 lore raw
Quoted 62 lines · Steffen Klassert
Quoted 6 lines · Duan Jiong

Just like you said, i try to use ndisc_parse_options() to instead of the loop, but i find the skb->data can't be changed in function ndisc_parse_options() due to lack of arguments. So i think it is better to continue to use the loop. How do you think this?

You can change the data pointer after ndisc_parse_options(). Something like the (untested) patch below should do it.

 include/net/ndisc.h |    7 +++++++
 net/ipv6/ndisc.c    |   20 ++++++++++++++++++++
 2 files changed, 27 insertions(+)
include/net/ndisc.h +7 -0
index 980d263..c17bccd 100644
@@ -78,6 +78,13 @@ struct ra_msg {
 	__be32			retrans_timer;
 };
 
+struct rd_msg {
+        struct icmp6hdr	icmph;
+        struct in6_addr	target;
+        struct in6_addr	dest;
+	__u8		opt[0];
+};
+
 struct nd_opt_hdr {
 	__u8		nd_opt_type;
 	__u8		nd_opt_len;
net/ipv6/ndisc.c +13 -0
index 2edce30..9afd23f 100644
@@ -1333,6 +1333,12 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+	u8 *hdr;
+	struct ndisc_options ndopts;
+	struct rd_msg *msg = (struct rd_msg *) skb_transport_header(skb);
+	u32 ndoptlen = skb->tail - (skb->transport_header +
+				    offsetof(struct rd_msg, opt));
+
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
 	switch (skb->ndisc_nodetype) {
 	case NDISC_NODETYPE_HOST:
@@ -1349,6 +1355,20 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
 		return;
 	}
 
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		ND_PRINTK(2, warn, "Redirect: invalid ND options\n");
+		return;
+	}
+
+	if (!ndopts.nd_opts_rh)
+		return;

net/ipv6/ndisc.c static void ndisc_redirect_rcv(struct sk_buff *skb) · line 1374

Quoted 10 lines
net/ipv6/ndisc.c +7 -0
+
+	hdr = (u8 *) ndopts.nd_opts_rh;
+	hdr += 8;
+
+	if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
+		return;
+
 	icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
 }
 

Thanks for you help. I will test it.