diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index ca1e0fb576acbaa564f78b18742fa4e72aaf0915..850fc05b52d8014fb46592eec161b06d2841fcf3 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 @@ static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
 	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 :