Commit e8d7832c authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[AH6]: Rearrange routing headers

This patch rearranges the IPv6 routing header so that the destination
addresses appear in the order as they would on the destination.  This
is specified in Appendix A2 of RFC 2402.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@redhat.com>
parent 39e8d3e1
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <net/ah.h> #include <net/ah.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <linux/pfkeyv2.h> #include <linux/pfkeyv2.h>
#include <linux/string.h>
#include <net/icmp.h> #include <net/icmp.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/xfrm.h> #include <net/xfrm.h>
...@@ -74,6 +75,45 @@ static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) ...@@ -74,6 +75,45 @@ static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
return 0; return 0;
} }
/**
* ipv6_rearrange_rthdr - rearrange IPv6 routing header
* @iph: IPv6 header
* @rthdr: routing header
*
* Rearrange the destination address in @iph and the addresses in @rthdr
* so that they appear in the order they will at the final destination.
* See Appendix A2 of RFC 2402 for details.
*/
static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
{
int segments, segments_left;
struct in6_addr *addrs;
struct in6_addr final_addr;
segments_left = rthdr->segments_left;
if (segments_left == 0)
return;
rthdr->segments_left = 0;
/* The value of rthdr->hdrlen has been verified either by the system
* call if it is locally generated, or by ipv6_rthdr_rcv() for incoming
* packets. So we can assume that it is even and that segments is
* greater than or equal to segments_left.
*
* For the same reason we can assume that this option is of type 0.
*/
segments = rthdr->hdrlen >> 1;
addrs = ((struct rt0_hdr *)rthdr)->addr;
ipv6_addr_copy(&final_addr, addrs + segments - 1);
addrs += segments - segments_left;
memmove(addrs + 1, addrs, (segments_left - 1) * sizeof(*addrs));
ipv6_addr_copy(addrs, &iph->daddr);
ipv6_addr_copy(&iph->daddr, &final_addr);
}
static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
{ {
union { union {
...@@ -101,7 +141,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len) ...@@ -101,7 +141,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
break; break;
case NEXTHDR_ROUTING: case NEXTHDR_ROUTING:
exthdr.rth->segments_left = 0; ipv6_rearrange_rthdr(iph, exthdr.rth);
break; break;
default : default :
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment