[PATCHv4 net-next-2.6 5/5] XFRM,IPv6: Add IRO remapping capability via socket ancillary data path

Arnaud Ebalard
2010-10-04T06:25:31Z

This provides the ability to remap src/dst address using IRO
via ancillary data passed to sockets. This is the IRO equivalent
of what is done for RH2/HAO (i.e. IPV6_RTHDR/IPV6_DSTOPTS).
This is used by UMIP during BA emission when acting as a Home
Agent.

Signed-off-by: Arnaud Ebalard <arno@natisbad.org>
---
include/net/ipv6.h | 4 ++++
net/ipv6/datagram.c | 20 ++++++++++++++++++++
net/ipv6/exthdrs.c | 19 +++++++++++++------
net/ipv6/ip6_flowlabel.c | 7 +++++++
net/ipv6/ip6_output.c | 22 ++++++++++++++++++++++
net/ipv6/raw.c | 3 ++-
6 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 4a3cd2c..2ba96d8 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -188,6 +188,10 @@ struct ipv6_txoptions {
struct ipv6_rt_hdr *srcrt; /* Routing Header */
struct ipv6_opt_hdr *dst1opt;

+ /* XXX protect those via some ifdef e.g. CONFIG_XFRM_SUB_POLICY ? */
+ struct in6_addr *iro_src;
+ struct in6_addr *iro_dst;
+
/* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
};

diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 2952c9e..0ac7adf 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -757,6 +757,26 @@ int datagram_send_ctl(struct net *net,
}
break;

+#ifdef CONFIG_XFRM_SUB_POLICY
+ case IPV6_IROSRC:
+ case IPV6_IRODST:
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_addr))) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ if (!capable(CAP_NET_RAW)) {
+ err = -EPERM;
+ goto exit_f;
+ }
+
+ if (cmsg->cmsg_type == IPV6_IROSRC)
+ opt->iro_src = (struct in6_addr *)CMSG_DATA(cmsg);
+ else
+ opt->iro_dst = (struct in6_addr *)CMSG_DATA(cmsg);
+ break;
+#endif
+
case IPV6_2292RTHDR:
case IPV6_RTHDR:
if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 262f105..e480b06 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -750,6 +750,10 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
*((char**)&opt2->dst1opt) += dif;
if (opt2->srcrt)
*((char**)&opt2->srcrt) += dif;
+ if (opt2->iro_src)
+ *((char**)&opt2->iro_src) += dif;
+ if (opt2->iro_dst)
+ *((char**)&opt2->iro_dst) += dif;
}
return opt2;
}
@@ -874,24 +878,27 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,

/**
* fl6_update_dst - update flowi destination address with info given
- * by srcrt option, if any.
+ * by srcrt/iro_dst option, if any.
*
* @fl: flowi for which fl6_dst is to be updated
- * @opt: struct ipv6_txoptions in which to look for srcrt opt
+ * @opt: struct ipv6_txoptions in which to look for srcrt/iro_dst opt
* @orig: copy of original fl6_dst address if modified
*
- * Returns NULL if no txoptions or no srcrt, otherwise returns orig
- * and initial value of fl->fl6_dst set in orig
+ * Returns NULL if no txoptions or no options to change flowi destination
+ * (srcrt or IRO destination remapping rule), otherwise returns orig and
+ * initial value of fl->fl6_dst set in orig
*/
struct in6_addr *fl6_update_dst(struct flowi *fl,
const struct ipv6_txoptions *opt,
struct in6_addr *orig)
{
- if (!opt || !opt->srcrt)
+ if (!opt || (!opt->srcrt && !opt->iro_dst))
return NULL;

ipv6_addr_copy(orig, &fl->fl6_dst);
- ipv6_addr_copy(&fl->fl6_dst, ((struct rt0_hdr *)opt->srcrt)->addr);
+ ipv6_addr_copy(&fl->fl6_dst,
+ opt->iro_dst ?: ((struct rt0_hdr *)opt->srcrt)->addr);
+
return orig;
}

diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 1365468..dbf9c29 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -280,6 +280,9 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
opt_space->hopopt = fl_opt->hopopt;
opt_space->dst0opt = fl_opt->dst0opt;
opt_space->srcrt = fl_opt->srcrt;
+ /* XXX protect those via some ifdef - see net/ipv6.h */
+ opt_space->iro_src = fl_opt->iro_src;
+ opt_space->iro_dst = fl_opt->iro_dst;
opt_space->opt_nflen = fl_opt->opt_nflen;
} else {
if (fopt->opt_nflen == 0)
@@ -287,6 +290,9 @@ struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space,
opt_space->hopopt = NULL;
opt_space->dst0opt = NULL;
opt_space->srcrt = NULL;
+ /* XXX protect those via some ifdef - see net/ipv6.h */
+ opt_space->iro_src = NULL;
+ opt_space->iro_dst = NULL;
opt_space->opt_nflen = 0;
}
opt_space->dst1opt = fopt->dst1opt;
@@ -456,6 +462,7 @@ static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2)
return 1;
if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt))
return 1;
+
return 0;
}

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 99157b4..210f269 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -222,6 +222,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
ipv6_push_frag_opts(skb, opt, &proto);
if (opt->opt_nflen)
ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+ if (opt->iro_dst)
+ first_hop = opt->iro_dst;
}

skb_push(skb, sizeof(struct ipv6hdr));
@@ -1106,6 +1108,12 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src,
return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL;
}

+static inline struct in6_addr *ip6_iro_addr_dup(struct in6_addr *addr,
+ gfp_t gfp)
+{
+ return addr ? kmemdup(addr, sizeof(struct in6_addr), gfp) : NULL;
+}
+
int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
int offset, int len, int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen,
@@ -1162,6 +1170,16 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
if (opt->srcrt && !np->cork.opt->srcrt)
return -ENOBUFS;

+ np->cork.opt->iro_src = ip6_iro_addr_dup(opt->iro_src,
+ sk->sk_allocation);
+ if (opt->iro_src && !np->cork.opt->iro_src)
+ return -ENOBUFS;
+
+ np->cork.opt->iro_dst = ip6_iro_addr_dup(opt->iro_dst,
+ sk->sk_allocation);
+ if (opt->iro_dst && !np->cork.opt->iro_dst)
+ return -ENOBUFS;
+
/* need source address above miyazawa*/
}
dst_hold(&rt->dst);
@@ -1440,6 +1458,8 @@ static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np)
kfree(np->cork.opt->dst1opt);
kfree(np->cork.opt->hopopt);
kfree(np->cork.opt->srcrt);
+ kfree(np->cork.opt->iro_src);
+ kfree(np->cork.opt->iro_dst);
kfree(np->cork.opt);
np->cork.opt = NULL;
}
@@ -1495,6 +1515,8 @@ int ip6_push_pending_frames(struct sock *sk)
ipv6_push_frag_opts(skb, opt, &proto);
if (opt && opt->opt_nflen)
ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst);
+ if (opt && opt->iro_dst)
+ final_dst = opt->iro_dst;

skb_push(skb, sizeof(struct ipv6hdr));
skb_reset_network_header(skb);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 45e6efb..1a11bd5 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -828,7 +828,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
if (flowlabel == NULL)
return -EINVAL;
}
- if (!(opt->opt_nflen|opt->opt_flen))
+ if (!(opt->opt_nflen|opt->opt_flen) &&
+ (!opt->iro_src && !opt->iro_dst))
opt = NULL;
}
if (opt == NULL)
--
1.7.1