From patchwork Wed Sep 29 09:06:06 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnaud Ebalard X-Patchwork-Id: 66057 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id D46C4B6F06 for ; Wed, 29 Sep 2010 19:05:20 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755001Ab0I2JFM (ORCPT ); Wed, 29 Sep 2010 05:05:12 -0400 Received: from copper.chdir.org ([88.191.97.87]:38543 "EHLO copper.chdir.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754940Ab0I2JFL (ORCPT ); Wed, 29 Sep 2010 05:05:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=natisbad.org; s=mail; h=From:To:Cc:Subject:In-Reply-To: References:Message-Id:Date:MIME-Version:Content-Type; bh=WHnSYVv 3FGcVN/pJkbbMIdio9em1/OkPvut8sSubKIg=; b=j7r/W74UBaSAlM4U1Q7hKEy wbhYQLXjx62xXTdw+OIwAY1Qg6O1wMguGcTwqocBugHWoq18QG8Rf14ciYY1zoRn ObWyn0NkzF71ckCuTzLy2UmYEEuVF4ei6q6nTOaS3SMNeKAfIYKSwnassdZqrvft CznJjNeyh+/TRBZyRcQQ= Received: from [2001:7a8:78df:2:20d:93ff:fe55:8f79] (helo=small.ssi.corp) by copper.chdir.org with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.69) (envelope-from ) id 1P0sb7-0002Sj-F8; Wed, 29 Sep 2010 11:05:09 +0200 X-Hashcash: 1:20:100929:davem@davemloft.net::M6vbBjK/YwLPk6pb:0000000000000000000000000000000000000000001lBQ X-Hashcash: 1:20:100929:eric.dumazet@gmail.com::JEM8uZKLcGA5d9A7:0000000000000000000000000000000000000009O7+ X-Hashcash: 1:20:100929:herbert@gondor.apana.org.au::ZyMgxj6jb49zEZZs:000000000000000000000000000000000015Zy X-Hashcash: 1:20:100929:yoshfuji@linux-ipv6.org::Ka9ZY9cnnHtxchJY:000000000000000000000000000000000000001+7A X-Hashcash: 1:20:100929:netdev@vger.kernel.org::3QF/2yMk2kFuEFx+:0000000000000000000000000000000000000000hMz From: Arnaud Ebalard To: "David S. Miller" , Eric Dumazet , Herbert Xu , Hideaki YOSHIFUJI Cc: netdev@vger.kernel.org Subject: [PATCHv3 net-next-2.6 5/5] XFRM, IPv6: Add IRO remapping capability via socket ancillary data path In-Reply-To: References: Message-Id: <384554908598fb095c8d49a36bdab57fbb3bf113.1285749610.git.arno@natisbad.org> Date: Wed, 29 Sep 2010 11:06:06 +0200 User-Agent: Gnus/5.110011 (No Gnus v0.11) Emacs/23.1.50 (gnu/linux) MIME-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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)