aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2004-07-29 05:07:16 -0700
committerDavid S. Miller <davem@nuts.davemloft.net>2004-07-29 05:07:16 -0700
commite8d7832c61f4e1028cdbefd52d97f1bb182fc05b (patch)
treebae99559bf4f9009e73955d2382a61605f2b105b /net
parent39e8d3e10cb1f0fb9402f60fe41fc0e34ab11353 (diff)
downloadhistory-e8d7832c61f4e1028cdbefd52d97f1bb182fc05b.tar.gz
[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: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@redhat.com>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/ah6.c42
1 files changed, 41 insertions, 1 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index ca1e0fb576acba..850fc05b52d801 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -31,6 +31,7 @@
#include <net/ah.h>
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
+#include <linux/string.h>
#include <net/icmp.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
@@ -74,6 +75,45 @@ bad:
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)
{
union {
@@ -101,7 +141,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
break;
case NEXTHDR_ROUTING:
- exthdr.rth->segments_left = 0;
+ ipv6_rearrange_rthdr(iph, exthdr.rth);
break;
default :