|
@@ -74,6 +74,68 @@ bad:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+/**
|
|
|
+ * ipv6_rearrange_destopt - rearrange IPv6 destination options header
|
|
|
+ * @iph: IPv6 header
|
|
|
+ * @destopt: destionation options header
|
|
|
+ */
|
|
|
+static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *destopt)
|
|
|
+{
|
|
|
+ u8 *opt = (u8 *)destopt;
|
|
|
+ int len = ipv6_optlen(destopt);
|
|
|
+ int off = 0;
|
|
|
+ int optlen = 0;
|
|
|
+
|
|
|
+ off += 2;
|
|
|
+ len -= 2;
|
|
|
+
|
|
|
+ while (len > 0) {
|
|
|
+
|
|
|
+ switch (opt[off]) {
|
|
|
+
|
|
|
+ case IPV6_TLV_PAD0:
|
|
|
+ optlen = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (len < 2)
|
|
|
+ goto bad;
|
|
|
+ optlen = opt[off+1]+2;
|
|
|
+ if (len < optlen)
|
|
|
+ goto bad;
|
|
|
+
|
|
|
+ /* Rearrange the source address in @iph and the
|
|
|
+ * addresses in home address option for final source.
|
|
|
+ * See 11.3.2 of RFC 3775 for details.
|
|
|
+ */
|
|
|
+ if (opt[off] == IPV6_TLV_HAO) {
|
|
|
+ struct in6_addr final_addr;
|
|
|
+ struct ipv6_destopt_hao *hao;
|
|
|
+
|
|
|
+ hao = (struct ipv6_destopt_hao *)&opt[off];
|
|
|
+ if (hao->length != sizeof(hao->addr)) {
|
|
|
+ if (net_ratelimit())
|
|
|
+ printk(KERN_WARNING "destopt hao: invalid header length: %u\n", hao->length);
|
|
|
+ goto bad;
|
|
|
+ }
|
|
|
+ ipv6_addr_copy(&final_addr, &hao->addr);
|
|
|
+ ipv6_addr_copy(&hao->addr, &iph->saddr);
|
|
|
+ ipv6_addr_copy(&iph->saddr, &final_addr);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ off += optlen;
|
|
|
+ len -= optlen;
|
|
|
+ }
|
|
|
+ if (len == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+bad:
|
|
|
+ return;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/**
|
|
|
* ipv6_rearrange_rthdr - rearrange IPv6 routing header
|
|
|
* @iph: IPv6 header
|
|
@@ -113,7 +175,11 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
|
|
|
ipv6_addr_copy(&iph->daddr, &final_addr);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
|
|
|
+#else
|
|
|
static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
|
|
|
+#endif
|
|
|
{
|
|
|
union {
|
|
|
struct ipv6hdr *iph;
|
|
@@ -128,6 +194,28 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
|
|
|
|
|
|
while (exthdr.raw < end) {
|
|
|
switch (nexthdr) {
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+ case NEXTHDR_HOP:
|
|
|
+ if (!zero_out_mutable_opts(exthdr.opth)) {
|
|
|
+ LIMIT_NETDEBUG(
|
|
|
+ KERN_WARNING "overrun %sopts\n",
|
|
|
+ nexthdr == NEXTHDR_HOP ?
|
|
|
+ "hop" : "dest");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case NEXTHDR_DEST:
|
|
|
+ if (dir == XFRM_POLICY_OUT)
|
|
|
+ ipv6_rearrange_destopt(iph, exthdr.opth);
|
|
|
+ if (!zero_out_mutable_opts(exthdr.opth)) {
|
|
|
+ LIMIT_NETDEBUG(
|
|
|
+ KERN_WARNING "overrun %sopts\n",
|
|
|
+ nexthdr == NEXTHDR_HOP ?
|
|
|
+ "hop" : "dest");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+#else
|
|
|
case NEXTHDR_HOP:
|
|
|
case NEXTHDR_DEST:
|
|
|
if (!zero_out_mutable_opts(exthdr.opth)) {
|
|
@@ -138,6 +226,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
break;
|
|
|
+#endif
|
|
|
|
|
|
case NEXTHDR_ROUTING:
|
|
|
ipv6_rearrange_rthdr(iph, exthdr.rth);
|
|
@@ -164,6 +253,9 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
u8 nexthdr;
|
|
|
char tmp_base[8];
|
|
|
struct {
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+ struct in6_addr saddr;
|
|
|
+#endif
|
|
|
struct in6_addr daddr;
|
|
|
char hdrs[0];
|
|
|
} *tmp_ext;
|
|
@@ -188,10 +280,18 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
err = -ENOMEM;
|
|
|
goto error;
|
|
|
}
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+ memcpy(tmp_ext, &top_iph->saddr, extlen);
|
|
|
+ err = ipv6_clear_mutable_options(top_iph,
|
|
|
+ extlen - sizeof(*tmp_ext) +
|
|
|
+ sizeof(*top_iph),
|
|
|
+ XFRM_POLICY_OUT);
|
|
|
+#else
|
|
|
memcpy(tmp_ext, &top_iph->daddr, extlen);
|
|
|
err = ipv6_clear_mutable_options(top_iph,
|
|
|
extlen - sizeof(*tmp_ext) +
|
|
|
sizeof(*top_iph));
|
|
|
+#endif
|
|
|
if (err)
|
|
|
goto error_free_iph;
|
|
|
}
|
|
@@ -222,7 +322,11 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
|
|
|
memcpy(top_iph, tmp_base, sizeof(tmp_base));
|
|
|
if (tmp_ext) {
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+ memcpy(&top_iph->saddr, tmp_ext, extlen);
|
|
|
+#else
|
|
|
memcpy(&top_iph->daddr, tmp_ext, extlen);
|
|
|
+#endif
|
|
|
error_free_iph:
|
|
|
kfree(tmp_ext);
|
|
|
}
|
|
@@ -282,8 +386,13 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
|
|
|
if (!tmp_hdr)
|
|
|
goto out;
|
|
|
memcpy(tmp_hdr, skb->nh.raw, hdr_len);
|
|
|
+#ifdef CONFIG_IPV6_MIP6
|
|
|
+ if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len, XFRM_POLICY_IN))
|
|
|
+ goto free_out;
|
|
|
+#else
|
|
|
if (ipv6_clear_mutable_options(skb->nh.ipv6h, hdr_len))
|
|
|
goto free_out;
|
|
|
+#endif
|
|
|
skb->nh.ipv6h->priority = 0;
|
|
|
skb->nh.ipv6h->flow_lbl[0] = 0;
|
|
|
skb->nh.ipv6h->flow_lbl[1] = 0;
|