* [PATCH][IPSEC] inter address family IPsec tunnel on the fly
@ 2008-03-05 12:37 Kazunori MIYAZAWA
2008-03-05 21:40 ` David Miller
0 siblings, 1 reply; 13+ messages in thread
From: Kazunori MIYAZAWA @ 2008-03-05 12:37 UTC (permalink / raw)
To: David Miller; +Cc: netdev, usagi-core
Hello David,
This patch fix inter address family ipsec tunneling
when we install IPsec SA via PF_KEY interface
because there are no interface to set the selector.
This patch is for net-2.6
Signed-off-by: Kazunori MIYAZAWA <miyazawa@linux-ipv6.org>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Best regards,
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index eea7785..6a52805 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -386,6 +386,48 @@ enum {
extern int xfrm_register_mode(struct xfrm_mode *mode, int family);
extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
+extern struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family);
+extern void xfrm_put_mode(struct xfrm_mode *mode);
+
+static inline int xfrm_proto2af(int proto)
+{
+ switch (proto) {
+ case IPPROTO_IPIP:
+ return AF_INET;
+ case IPPROTO_IPV6:
+ return AF_INET6;
+ default:
+ return AF_UNSPEC;
+ }
+}
+
+static inline int xfrm_af2proto(unsigned int family)
+{
+ switch(family) {
+ case AF_INET:
+ return IPPROTO_IPIP;
+ case AF_INET6:
+ return IPPROTO_IPV6;
+ default:
+ return 0;
+ }
+}
+
+static inline struct xfrm_mode *xfrm_get_inner_mode(struct xfrm_state *x, int family)
+{
+ if (x->inner_mode)
+ return x->inner_mode;
+ else
+ return xfrm_get_mode(x->props.mode, family);
+}
+
+static inline void xfrm_put_inner_mode(struct xfrm_state *x, struct xfrm_mode *mode)
+{
+ if (x->inner_mode != NULL)
+ xfrm_put_mode(mode);
+}
+
+extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
struct xfrm_tmpl
{
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 8dee617..584e6d7 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -41,7 +41,7 @@ static int xfrm4_mode_tunnel_output(stru
top_iph->ihl = 5;
top_iph->version = 4;
- top_iph->protocol = x->inner_mode->afinfo->proto;
+ top_iph->protocol = xfrm_af2proto(skb->dst->ops->family);
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index d5a58a8..8c3180a 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -56,7 +56,7 @@ int xfrm4_prepare_output(struct xfrm_sta
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 0c742fa..e20529b 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -45,7 +45,7 @@ static int xfrm6_mode_tunnel_output(stru
memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
sizeof(top_iph->flow_lbl));
- top_iph->nexthdr = x->inner_mode->afinfo->proto;
+ top_iph->nexthdr = xfrm_af2proto(skb->dst->ops->family);
dsfield = XFRM_MODE_SKB_CB(skb)->tos;
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 79ccfb0..0af823c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -62,7 +62,7 @@ int xfrm6_prepare_output(struct xfrm_sta
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 8b5f486..7c175e1 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1219,9 +1219,6 @@ static struct xfrm_state * pfkey_msg2xfr
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
- if (!x->sel.family)
- x->sel.family = x->props.family;
-
if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
struct sadb_x_nat_t_type* n_type;
struct xfrm_encap_tmpl *natt;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 62188c6..c81455f 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -82,6 +82,35 @@ int xfrm_parse_spi(struct sk_buff *skb,
return 0;
}
+static inline int xfrm_inner_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ int err;
+
+ inner_mode = xfrm_get_inner_mode(x, xfrm_proto2af(XFRM_MODE_SKB_CB(skb)->protocol));
+ if (!inner_mode)
+ return -EINVAL;
+ err = inner_mode->input(x, skb);
+ xfrm_put_inner_mode(x, inner_mode);
+
+ return err;
+}
+
+static inline int xfrm_inner_input2(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ int err;
+
+ inner_mode = xfrm_get_inner_mode(x, xfrm_proto2af(XFRM_MODE_SKB_CB(skb)->protocol));
+ if (!inner_mode)
+ return -EINVAL;
+ err = inner_mode->input2(x, skb);
+ if (err >= 0)
+ err = inner_mode->afinfo->eth_proto;
+ xfrm_put_inner_mode(x, inner_mode);
+ return err;
+}
+
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
@@ -90,8 +119,10 @@ int xfrm_prepare_input(struct xfrm_state
if (err)
return err;
- skb->protocol = x->inner_mode->afinfo->eth_proto;
- return x->inner_mode->input2(x, skb);
+ err = xfrm_inner_input2(x, skb);
+ if (err >= 0)
+ skb->protocol = err;
+ return 0;
}
EXPORT_SYMBOL(xfrm_prepare_input);
@@ -207,7 +238,8 @@ resume:
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
- if (x->inner_mode->input(x, skb)) {
+ err = xfrm_inner_input(x, skb);
+ if (err) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 569d377..711873e 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -18,6 +18,21 @@ #include <linux/spinlock.h>
#include <net/dst.h>
#include <net/xfrm.h>
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ int err;
+
+ inner_mode = xfrm_get_inner_mode(x, skb->dst->ops->family);
+ if (!inner_mode)
+ return -EINVAL;
+ err = inner_mode->afinfo->extract_output(x, skb);
+ xfrm_put_inner_mode(x, inner_mode);
+
+ return err;
+}
+EXPORT_SYMBOL(xfrm_inner_extract_output);
+
static int xfrm_output2(struct sk_buff *skb);
static int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb)
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 7ba65e8..88894b1 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -344,7 +344,7 @@ int xfrm_unregister_mode(struct xfrm_mod
}
EXPORT_SYMBOL(xfrm_unregister_mode);
-static struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
+struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
{
struct xfrm_state_afinfo *afinfo;
struct xfrm_mode *mode;
@@ -372,7 +372,7 @@ retry:
return mode;
}
-static void xfrm_put_mode(struct xfrm_mode *mode)
+void xfrm_put_mode(struct xfrm_mode *mode)
{
module_put(mode->owner);
}
@@ -796,7 +796,7 @@ xfrm_state_find(xfrm_address_t *daddr, x
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+ if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
continue;
if (!best ||
@@ -1962,13 +1962,15 @@ int xfrm_init_state(struct xfrm_state *x
goto error;
err = -EPROTONOSUPPORT;
- x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
- if (x->inner_mode == NULL)
- goto error;
+ if (x->sel.family != 0) {
+ x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+ if (x->inner_mode == NULL)
+ goto error;
- if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
- family != x->sel.family)
- goto error;
+ if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+ family != x->sel.family)
+ goto error;
+ }
x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index f971ca5..6499a84 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -287,13 +287,6 @@ static void copy_from_user_state(struct
x->props.family = p->family;
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
x->props.flags = p->flags;
-
- /*
- * Set inner address family if the KM left it as zero.
- * See comment in validate_tmpl.
- */
- if (!x->sel.family)
- x->sel.family = p->family;
}
/*
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-05 12:37 [PATCH][IPSEC] inter address family IPsec tunnel on the fly Kazunori MIYAZAWA
@ 2008-03-05 21:40 ` David Miller
2008-03-07 6:32 ` Kazunori MIYAZAWA
0 siblings, 1 reply; 13+ messages in thread
From: David Miller @ 2008-03-05 21:40 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Wed, 5 Mar 2008 21:37:27 +0900
> Hello David,
Hello,
> This patch fix inter address family ipsec tunneling
> when we install IPsec SA via PF_KEY interface
> because there are no interface to set the selector.
>
> This patch is for net-2.6
>
> Signed-off-by: Kazunori MIYAZAWA <miyazawa@linux-ipv6.org>
> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
It seems quite excessive to grab and release the module reference
count during every packet input/output which happens through IPSEC
tunnels.
The whole reason we store the mode information in the state is so that
we only have to grab the reference during IPSEC rule addition, instead
of during packet processing.
Having to export xfrm_mode_{get,put} from xfrm_state.c is a sure
sign of trouble :-)
Is there some way we can simply propagate the correct setting to
x->inner_mode?
I also wonder if the PF_KEY limitation really exists. For example we
will set x->sel.family etc. from the SADB_EXT_ADDRESS_PROXY attribute
if present.
Finally, if the determination can be made in the data path, it
by definition could be made during rule insertion which is much
more efficient and appropriate.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-05 21:40 ` David Miller
@ 2008-03-07 6:32 ` Kazunori MIYAZAWA
2008-03-07 7:19 ` David Miller
0 siblings, 1 reply; 13+ messages in thread
From: Kazunori MIYAZAWA @ 2008-03-07 6:32 UTC (permalink / raw)
To: David Miller; +Cc: netdev, usagi-core
Thank you for reviewing the patch,
David Miller wrote:
> From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
> Date: Wed, 5 Mar 2008 21:37:27 +0900
>
>> Hello David,
>
> Hello,
>
>> This patch fix inter address family ipsec tunneling
>> when we install IPsec SA via PF_KEY interface
>> because there are no interface to set the selector.
>>
>> This patch is for net-2.6
>>
>> Signed-off-by: Kazunori MIYAZAWA <miyazawa@linux-ipv6.org>
>> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
>
> It seems quite excessive to grab and release the module reference
> count during every packet input/output which happens through IPSEC
> tunnels.
>
> The whole reason we store the mode information in the state is so that
> we only have to grab the reference during IPSEC rule addition, instead
> of during packet processing.
>
> Having to export xfrm_mode_{get,put} from xfrm_state.c is a sure
> sign of trouble :-)
>
At the beginning I tried to implement inter address tunnel mode module.
However it was complicated and I gave up it because I could not send
the patches before next release.
Should we give up configure a tunnel mode xfrm_state which has no
selector? If so, we may need to extend PF_KEYv2 interface to set
the selector.
> Is there some way we can simply propagate the correct setting to
> x->inner_mode?
>
Now I have no idea :-<
> I also wonder if the PF_KEY limitation really exists. For example we
> will set x->sel.family etc. from the SADB_EXT_ADDRESS_PROXY attribute
> if present.
>
Yes, we have SADB_EXT_ADDRESS_PROXY. But it is not enough, I think.
xfrm_selector has both src and dst so that we need some way to
specify the address is src or dst.
from RFC2367
The SRC and DST addresses for a security association MUST be in the
same protocol family and MUST always be present or absent together in
a message. The PROXY address MAY be in a different protocol family,
and for most security protocols, represents an actual originator of a
packet. (For example, the inner-packets's source address in a
tunnel.)
The SRC address MUST be a unicast or unspecified (e.g., INADDR_ANY)
address. The DST address can be any valid destination address
(unicast, multicast, or even broadcast). The PROXY address SHOULD be
a unicast address (there are experimental security protocols where
PROXY semantics may be different than described above).
> Finally, if the determination can be made in the data path, it
> by definition could be made during rule insertion which is much
> more efficient and appropriate.
I agree with you.
Anyway I'm considering another way.
Thank you,
--
Kazunori Miyazawa
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-07 6:32 ` Kazunori MIYAZAWA
@ 2008-03-07 7:19 ` David Miller
2008-03-08 14:46 ` Kazunori MIAZAWA
0 siblings, 1 reply; 13+ messages in thread
From: David Miller @ 2008-03-07 7:19 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Fri, 07 Mar 2008 15:32:09 +0900
> David Miller wrote:
> > I also wonder if the PF_KEY limitation really exists. For example we
> > will set x->sel.family etc. from the SADB_EXT_ADDRESS_PROXY attribute
> > if present.
> >
>
> Yes, we have SADB_EXT_ADDRESS_PROXY. But it is not enough, I think.
> xfrm_selector has both src and dst so that we need some way to
> specify the address is src or dst.
>
> from RFC2367
Thank you for this information.
> > Finally, if the determination can be made in the data path, it
> > by definition could be made during rule insertion which is much
> > more efficient and appropriate.
>
> I agree with you.
I am sure there is a simple solution to this problem somewhere,
it is just hiding :-)
Anyways, since I will be in Japan we can discuss alternate
implementation schemes.
Thank you for working on this problem.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-07 7:19 ` David Miller
@ 2008-03-08 14:46 ` Kazunori MIAZAWA
2008-03-08 22:15 ` David Miller
0 siblings, 1 reply; 13+ messages in thread
From: Kazunori MIAZAWA @ 2008-03-08 14:46 UTC (permalink / raw)
To: David Miller; +Cc: netdev, usagi-core
David Miller wrote:
> From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
> Date: Fri, 07 Mar 2008 15:32:09 +0900
>
>> David Miller wrote:
>>> I also wonder if the PF_KEY limitation really exists. For example we
>>> will set x->sel.family etc. from the SADB_EXT_ADDRESS_PROXY attribute
>>> if present.
>>>
>> Yes, we have SADB_EXT_ADDRESS_PROXY. But it is not enough, I think.
>> xfrm_selector has both src and dst so that we need some way to
>> specify the address is src or dst.
>>
>> from RFC2367
>
> Thank you for this information.
>
>>> Finally, if the determination can be made in the data path, it
>>> by definition could be made during rule insertion which is much
>>> more efficient and appropriate.
>> I agree with you.
>
> I am sure there is a simple solution to this problem somewhere,
> it is just hiding :-)
>
I think one solution is xfrm_state has two inner_modes
and switch them when the family is any.
This is just an idea :-p
--
Kazunori Miyazawa
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-08 14:46 ` Kazunori MIAZAWA
@ 2008-03-08 22:15 ` David Miller
2008-03-14 3:23 ` Kazunori MIYAZAWA
0 siblings, 1 reply; 13+ messages in thread
From: David Miller @ 2008-03-08 22:15 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIAZAWA <kazunori@miyazawa.org>
Date: Sat, 08 Mar 2008 23:46:13 +0900
> David Miller wrote:
> > I am sure there is a simple solution to this problem somewhere,
> > it is just hiding :-)
> >
>
> I think one solution is xfrm_state has two inner_modes
> and switch them when the family is any.
>
> This is just an idea :-p
I can't see any immediate problem with this approach.
If you like, give it a try and we can see what it
looks like :)
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-08 22:15 ` David Miller
@ 2008-03-14 3:23 ` Kazunori MIYAZAWA
2008-03-21 11:20 ` David Miller
0 siblings, 1 reply; 13+ messages in thread
From: Kazunori MIYAZAWA @ 2008-03-14 3:23 UTC (permalink / raw)
To: David Miller; +Cc: netdev, usagi-core
On Sat, 08 Mar 2008 14:15:06 -0800 (PST)
David Miller <davem@davemloft.net> wrote:
> From: Kazunori MIAZAWA <kazunori@miyazawa.org>
> Date: Sat, 08 Mar 2008 23:46:13 +0900
>
> > David Miller wrote:
> > > I am sure there is a simple solution to this problem somewhere,
> > > it is just hiding :-)
> > >
> >
> > I think one solution is xfrm_state has two inner_modes
> > and switch them when the family is any.
> >
> > This is just an idea :-p
>
> I can't see any immediate problem with this approach.
>
> If you like, give it a try and we can see what it
> looks like :)
I made the patch for that.
Signed-off-by Kazunori MIYAZAWA <kazunori@miyazawa.org>
---
include/net/xfrm.h | 34 +++++++++++++++++++++++
net/ipv4/xfrm4_mode_tunnel.c | 2 +-
net/ipv4/xfrm4_output.c | 2 +-
net/ipv6/xfrm6_mode_tunnel.c | 2 +-
net/ipv6/xfrm6_output.c | 2 +-
net/key/af_key.c | 6 +++-
net/xfrm/Kconfig | 11 +++++++
net/xfrm/xfrm_input.c | 24 +++++++++++++---
net/xfrm/xfrm_output.c | 20 +++++++++++++-
net/xfrm/xfrm_state.c | 61 +++++++++++++++++++++++++++++++++++++-----
net/xfrm/xfrm_user.c | 5 +++
11 files changed, 152 insertions(+), 17 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index eea7785..0a81d83 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -204,6 +204,9 @@ struct xfrm_state
* transformer. */
const struct xfrm_type *type;
struct xfrm_mode *inner_mode;
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ struct xfrm_mode *inner_mode_iaf;
+#endif
struct xfrm_mode *outer_mode;
/* Security context */
@@ -387,6 +390,29 @@ enum {
extern int xfrm_register_mode(struct xfrm_mode *mode, int family);
extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
+static inline int xfrm_af2proto(unsigned int family)
+{
+ switch(family) {
+ case AF_INET:
+ return IPPROTO_IPIP;
+ case AF_INET6:
+ return IPPROTO_IPV6;
+ default:
+ return 0;
+ }
+}
+
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+static inline struct xfrm_mode *xfrm_ip2inner_mode(struct xfrm_state *x, int ipproto)
+{
+ if ((ipproto == IPPROTO_IPIP && x->props.family == AF_INET) ||
+ (ipproto == IPPROTO_IPV6 && x->props.family == AF_INET6))
+ return x->inner_mode;
+ else
+ return x->inner_mode_iaf;
+}
+#endif
+
struct xfrm_tmpl
{
/* id in template is interpreted as:
@@ -1253,6 +1279,14 @@ extern int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi,
extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output(struct sk_buff *skb);
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
+#else
+static inline int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ return x->inner_mode->afinfo->extract_output(x, skb);
+}
+#endif
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 8dee617..584e6d7 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -41,7 +41,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
top_iph->ihl = 5;
top_iph->version = 4;
- top_iph->protocol = x->inner_mode->afinfo->proto;
+ top_iph->protocol = xfrm_af2proto(skb->dst->ops->family);
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index d5a58a8..8c3180a 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -56,7 +56,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 0c742fa..e20529b 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -45,7 +45,7 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
sizeof(top_iph->flow_lbl));
- top_iph->nexthdr = x->inner_mode->afinfo->proto;
+ top_iph->nexthdr = xfrm_af2proto(skb->dst->ops->family);
dsfield = XFRM_MODE_SKB_CB(skb)->tos;
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 79ccfb0..0af823c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -62,7 +62,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 8b5f486..229456e 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1218,9 +1218,13 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
-
+#ifndef CONFIG_IPSEC_IAF_TUNNEL
if (!x->sel.family)
x->sel.family = x->props.family;
+#else
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
+ x->sel.family = x->props.family;
+#endif
if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
struct sadb_x_nat_t_type* n_type;
diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
index 9201ef8..3d26f7d 100644
--- a/net/xfrm/Kconfig
+++ b/net/xfrm/Kconfig
@@ -72,3 +72,14 @@ config NET_KEY_MIGRATE
If unsure, say N.
+config IPSEC_IAF_TUNNEL
+ bool "IPSEC_IAF_TUNNEL"
+ depends on XFRM
+ ---help---
+ IPsec inter address family tunnel.
+ The kernel supports IPv6 over IPv4 IPsec tunnel and/or
+ IPv4 over IPv6 IPsec tunnel with this.
+ This is compatible with the 2.6.24 kernel.
+
+ If unsure, say Y.
+
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 62188c6..18eff53 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -84,14 +84,21 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
{
+ struct xfrm_mode *inner_mode = x->inner_mode;
int err;
err = x->outer_mode->afinfo->extract_input(x, skb);
if (err)
return err;
-
- skb->protocol = x->inner_mode->afinfo->eth_proto;
- return x->inner_mode->input2(x, skb);
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ }
+#endif
+ skb->protocol = inner_mode->afinfo->eth_proto;
+ return inner_mode->input2(x, skb);
}
EXPORT_SYMBOL(xfrm_prepare_input);
@@ -101,6 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
__be32 seq;
struct xfrm_state *x;
xfrm_address_t *daddr;
+ struct xfrm_mode *inner_mode;
unsigned int family;
int decaps = 0;
int async = 0;
@@ -207,7 +215,15 @@ resume:
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
- if (x->inner_mode->input(x, skb)) {
+ inner_mode = x->inner_mode;
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ goto drop;
+ }
+#endif
+ if (inner_mode->input(x, skb)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 569d377..358600a 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -124,7 +124,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err)
if (!x)
return dst_output(skb);
- err = nf_hook(x->inner_mode->afinfo->family,
+ err = nf_hook(skb->dst->ops->family,
NF_INET_POST_ROUTING, skb,
NULL, skb->dst->dev, xfrm_output2);
if (unlikely(err != 1))
@@ -193,4 +193,22 @@ int xfrm_output(struct sk_buff *skb)
return xfrm_output2(skb);
}
+
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ if (x->sel.family == AF_UNSPEC)
+ inner_mode = xfrm_ip2inner_mode(x,
+ xfrm_af2proto(skb->dst->ops->family));
+ else
+ inner_mode = x->inner_mode;
+
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ return inner_mode->afinfo->extract_output(x, skb);
+}
+#endif
+
EXPORT_SYMBOL_GPL(xfrm_output);
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 7ba65e8..e085cce 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -388,6 +388,10 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
kfree(x->coaddr);
if (x->inner_mode)
xfrm_put_mode(x->inner_mode);
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ if (x->inner_mode_iaf)
+ xfrm_put_mode(x->inner_mode_iaf);
+#endif
if (x->outer_mode)
xfrm_put_mode(x->outer_mode);
if (x->type) {
@@ -523,6 +527,10 @@ struct xfrm_state *xfrm_state_alloc(void)
x->lft.hard_packet_limit = XFRM_INF;
x->replay_maxage = 0;
x->replay_maxdiff = 0;
+ x->inner_mode = NULL;
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ x->inner_mode_iaf = NULL;
+#endif
spin_lock_init(&x->lock);
}
return x;
@@ -796,7 +804,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+ if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
continue;
if (!best ||
@@ -1944,6 +1952,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu)
int xfrm_init_state(struct xfrm_state *x)
{
struct xfrm_state_afinfo *afinfo;
+ struct xfrm_mode *inner_mode;
int family = x->props.family;
int err;
@@ -1962,13 +1971,51 @@ int xfrm_init_state(struct xfrm_state *x)
goto error;
err = -EPROTONOSUPPORT;
- x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
- if (x->inner_mode == NULL)
- goto error;
- if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
- family != x->sel.family)
- goto error;
+ if (x->sel.family != AF_UNSPEC) {
+ inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+ if (x->inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+ family != x->sel.family) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ x->inner_mode = inner_mode;
+ }
+#ifdef CONFIG_IPSEC_IAF_TUNNEL
+ else {
+ struct xfrm_mode *inner_mode_iaf;
+
+ inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+ if (inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
+ if (inner_mode_iaf == NULL)
+ goto error;
+
+ if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode_iaf);
+ goto error;
+ }
+
+ if (x->props.family == AF_INET) {
+ x->inner_mode = inner_mode;
+ x->inner_mode_iaf = inner_mode_iaf;
+ } else {
+ x->inner_mode = inner_mode_iaf;
+ x->inner_mode_iaf = inner_mode;
+ }
+ }
+#endif
x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index f971ca5..f47497f 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -288,12 +288,17 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
x->props.flags = p->flags;
+#ifndef CONFIG_IPSEC_IAF_TUNNEL
/*
* Set inner address family if the KM left it as zero.
* See comment in validate_tmpl.
*/
if (!x->sel.family)
x->sel.family = p->family;
+#else
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
+ x->sel.family = p->family;
+#endif
}
/*
--
1.5.2.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-14 3:23 ` Kazunori MIYAZAWA
@ 2008-03-21 11:20 ` David Miller
2008-03-24 7:16 ` Kazunori MIYAZAWA
0 siblings, 1 reply; 13+ messages in thread
From: David Miller @ 2008-03-21 11:20 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Fri, 14 Mar 2008 12:23:14 +0900
> I made the patch for that.
>
> Signed-off-by Kazunori MIYAZAWA <kazunori@miyazawa.org>
This looks fine.
Because the inner mode is resolved at xfrm creation time, I think the
overall cost is small enough that we can avoid the config option.
And anyways, if something matters for performance we can fix that.
Could you respin this patch with it removed?
Thank you.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-21 11:20 ` David Miller
@ 2008-03-24 7:16 ` Kazunori MIYAZAWA
2008-03-24 7:44 ` Kazunori MIYAZAWA
2008-03-24 7:48 ` David Miller
0 siblings, 2 replies; 13+ messages in thread
From: Kazunori MIYAZAWA @ 2008-03-24 7:16 UTC (permalink / raw)
To: David Miller; +Cc: netdev, usagi-core
On Fri, 21 Mar 2008 04:20:48 -0700 (PDT)
David Miller <davem@davemloft.net> wrote:
> From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
> Date: Fri, 14 Mar 2008 12:23:14 +0900
>
> > I made the patch for that.
> >
> > Signed-off-by Kazunori MIYAZAWA <kazunori@miyazawa.org>
>
> This looks fine.
>
> Because the inner mode is resolved at xfrm creation time, I think the
> overall cost is small enough that we can avoid the config option.
> And anyways, if something matters for performance we can fix that.
>
> Could you respin this patch with it removed?
>
Hi David,
Sorry for my late reply because of weekend :-)
I removed the ifdef from the patch and I roughly tested
with compling and doing IPsec for ICMP(v6) on the kernel.
Best regards,
Signed-off-by Kazunori MIYAZAWA <kazunori@miyazawa.org>
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 619c53b..4e6f956 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -204,6 +204,7 @@ struct xfrm_state
* transformer. */
const struct xfrm_type *type;
struct xfrm_mode *inner_mode;
+ struct xfrm_mode *inner_mode_iaf;
struct xfrm_mode *outer_mode;
/* Security context */
@@ -387,6 +388,27 @@ enum {
extern int xfrm_register_mode(struct xfrm_mode *mode, int family);
extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
+static inline int xfrm_af2proto(unsigned int family)
+{
+ switch(family) {
+ case AF_INET:
+ return IPPROTO_IPIP;
+ case AF_INET6:
+ return IPPROTO_IPV6;
+ default:
+ return 0;
+ }
+}
+
+static inline struct xfrm_mode *xfrm_ip2inner_mode(struct xfrm_state *x, int ipproto)
+{
+ if ((ipproto == IPPROTO_IPIP && x->props.family == AF_INET) ||
+ (ipproto == IPPROTO_IPV6 && x->props.family == AF_INET6))
+ return x->inner_mode;
+ else
+ return x->inner_mode_iaf;
+}
+
struct xfrm_tmpl
{
/* id in template is interpreted as:
@@ -1253,6 +1275,7 @@ extern int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi,
extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output(struct sk_buff *skb);
+extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 8dee617..584e6d7 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -41,7 +41,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
top_iph->ihl = 5;
top_iph->version = 4;
- top_iph->protocol = x->inner_mode->afinfo->proto;
+ top_iph->protocol = xfrm_af2proto(skb->dst->ops->family);
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index d5a58a8..8c3180a 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -56,7 +56,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 0c742fa..e20529b 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -45,7 +45,7 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
sizeof(top_iph->flow_lbl));
- top_iph->nexthdr = x->inner_mode->afinfo->proto;
+ top_iph->nexthdr = xfrm_af2proto(skb->dst->ops->family);
dsfield = XFRM_MODE_SKB_CB(skb)->tos;
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 79ccfb0..0af823c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -62,7 +62,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 8b5f486..e9ef9af 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1219,7 +1219,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
- if (!x->sel.family)
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
x->sel.family = x->props.family;
if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 62188c6..7527940 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -84,14 +84,21 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
{
+ struct xfrm_mode *inner_mode = x->inner_mode;
int err;
err = x->outer_mode->afinfo->extract_input(x, skb);
if (err)
return err;
- skb->protocol = x->inner_mode->afinfo->eth_proto;
- return x->inner_mode->input2(x, skb);
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ }
+
+ skb->protocol = inner_mode->afinfo->eth_proto;
+ return inner_mode->input2(x, skb);
}
EXPORT_SYMBOL(xfrm_prepare_input);
@@ -101,6 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
__be32 seq;
struct xfrm_state *x;
xfrm_address_t *daddr;
+ struct xfrm_mode *inner_mode;
unsigned int family;
int decaps = 0;
int async = 0;
@@ -207,7 +215,15 @@ resume:
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
- if (x->inner_mode->input(x, skb)) {
+ inner_mode = x->inner_mode;
+
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ goto drop;
+ }
+
+ if (inner_mode->input(x, skb)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 569d377..2519129 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -124,7 +124,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err)
if (!x)
return dst_output(skb);
- err = nf_hook(x->inner_mode->afinfo->family,
+ err = nf_hook(skb->dst->ops->family,
NF_INET_POST_ROUTING, skb,
NULL, skb->dst->dev, xfrm_output2);
if (unlikely(err != 1))
@@ -193,4 +193,20 @@ int xfrm_output(struct sk_buff *skb)
return xfrm_output2(skb);
}
+
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ if (x->sel.family == AF_UNSPEC)
+ inner_mode = xfrm_ip2inner_mode(x,
+ xfrm_af2proto(skb->dst->ops->family));
+ else
+ inner_mode = x->inner_mode;
+
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ return inner_mode->afinfo->extract_output(x, skb);
+}
+
EXPORT_SYMBOL_GPL(xfrm_output);
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 7ba65e8..7576ca3 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -388,6 +388,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
kfree(x->coaddr);
if (x->inner_mode)
xfrm_put_mode(x->inner_mode);
+ if (x->inner_mode_iaf)
+ xfrm_put_mode(x->inner_mode_iaf);
if (x->outer_mode)
xfrm_put_mode(x->outer_mode);
if (x->type) {
@@ -523,6 +525,8 @@ struct xfrm_state *xfrm_state_alloc(void)
x->lft.hard_packet_limit = XFRM_INF;
x->replay_maxage = 0;
x->replay_maxdiff = 0;
+ x->inner_mode = NULL;
+ x->inner_mode_iaf = NULL;
spin_lock_init(&x->lock);
}
return x;
@@ -796,7 +800,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+ if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
continue;
if (!best ||
@@ -1944,6 +1948,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu)
int xfrm_init_state(struct xfrm_state *x)
{
struct xfrm_state_afinfo *afinfo;
+ struct xfrm_mode *inner_mode;
int family = x->props.family;
int err;
@@ -1962,13 +1967,48 @@ int xfrm_init_state(struct xfrm_state *x)
goto error;
err = -EPROTONOSUPPORT;
- x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
- if (x->inner_mode == NULL)
- goto error;
- if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
- family != x->sel.family)
- goto error;
+ if (x->sel.family != AF_UNSPEC) {
+ inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+ if (x->inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+ family != x->sel.family) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ x->inner_mode = inner_mode;
+ } else {
+ struct xfrm_mode *inner_mode_iaf;
+
+ inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+ if (inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
+ if (inner_mode_iaf == NULL)
+ goto error;
+
+ if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode_iaf);
+ goto error;
+ }
+
+ if (x->props.family == AF_INET) {
+ x->inner_mode = inner_mode;
+ x->inner_mode_iaf = inner_mode_iaf;
+ } else {
+ x->inner_mode = inner_mode_iaf;
+ x->inner_mode_iaf = inner_mode;
+ }
+ }
x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index f971ca5..5d96f27 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -288,12 +288,9 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
x->props.flags = p->flags;
- /*
- * Set inner address family if the KM left it as zero.
- * See comment in validate_tmpl.
- */
- if (!x->sel.family)
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
x->sel.family = p->family;
+
}
/*
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-24 7:16 ` Kazunori MIYAZAWA
@ 2008-03-24 7:44 ` Kazunori MIYAZAWA
2008-03-24 7:49 ` David Miller
2008-03-24 21:53 ` David Miller
2008-03-24 7:48 ` David Miller
1 sibling, 2 replies; 13+ messages in thread
From: Kazunori MIYAZAWA @ 2008-03-24 7:44 UTC (permalink / raw)
To: Kazunori MIYAZAWA; +Cc: davem, netdev, usagi-core
On Mon, 24 Mar 2008 16:16:46 +0900
Kazunori MIYAZAWA <kazunori@miyazawa.org> wrote:
> On Fri, 21 Mar 2008 04:20:48 -0700 (PDT)
> David Miller <davem@davemloft.net> wrote:
>
> > From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
> > Date: Fri, 14 Mar 2008 12:23:14 +0900
> >
> > > I made the patch for that.
> > >
> > > Signed-off-by Kazunori MIYAZAWA <kazunori@miyazawa.org>
> >
> > This looks fine.
> >
> > Because the inner mode is resolved at xfrm creation time, I think the
> > overall cost is small enough that we can avoid the config option.
> > And anyways, if something matters for performance we can fix that.
> >
> > Could you respin this patch with it removed?
> >
>
> Hi David,
>
> Sorry for my late reply because of weekend :-)
>
> I removed the ifdef from the patch and I roughly tested
> with compling and doing IPsec for ICMP(v6) on the kernel.
>
Sorry, the patch has typo. Please use this.
Signed-off-by: Kazunori MIYAZAWA <kazunori@miyazawa.org>
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 619c53b..4e6f956 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -204,6 +204,7 @@ struct xfrm_state
* transformer. */
const struct xfrm_type *type;
struct xfrm_mode *inner_mode;
+ struct xfrm_mode *inner_mode_iaf;
struct xfrm_mode *outer_mode;
/* Security context */
@@ -387,6 +388,27 @@ enum {
extern int xfrm_register_mode(struct xfrm_mode *mode, int family);
extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family);
+static inline int xfrm_af2proto(unsigned int family)
+{
+ switch(family) {
+ case AF_INET:
+ return IPPROTO_IPIP;
+ case AF_INET6:
+ return IPPROTO_IPV6;
+ default:
+ return 0;
+ }
+}
+
+static inline struct xfrm_mode *xfrm_ip2inner_mode(struct xfrm_state *x, int ipproto)
+{
+ if ((ipproto == IPPROTO_IPIP && x->props.family == AF_INET) ||
+ (ipproto == IPPROTO_IPV6 && x->props.family == AF_INET6))
+ return x->inner_mode;
+ else
+ return x->inner_mode_iaf;
+}
+
struct xfrm_tmpl
{
/* id in template is interpreted as:
@@ -1253,6 +1275,7 @@ extern int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi,
extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output(struct sk_buff *skb);
+extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c
index 8dee617..584e6d7 100644
--- a/net/ipv4/xfrm4_mode_tunnel.c
+++ b/net/ipv4/xfrm4_mode_tunnel.c
@@ -41,7 +41,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
top_iph->ihl = 5;
top_iph->version = 4;
- top_iph->protocol = x->inner_mode->afinfo->proto;
+ top_iph->protocol = xfrm_af2proto(skb->dst->ops->family);
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(XFRM_MODE_SKB_CB(skb)->tos,
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index d5a58a8..8c3180a 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -56,7 +56,7 @@ int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c
index 0c742fa..e20529b 100644
--- a/net/ipv6/xfrm6_mode_tunnel.c
+++ b/net/ipv6/xfrm6_mode_tunnel.c
@@ -45,7 +45,7 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
memcpy(top_iph->flow_lbl, XFRM_MODE_SKB_CB(skb)->flow_lbl,
sizeof(top_iph->flow_lbl));
- top_iph->nexthdr = x->inner_mode->afinfo->proto;
+ top_iph->nexthdr = xfrm_af2proto(skb->dst->ops->family);
dsfield = XFRM_MODE_SKB_CB(skb)->tos;
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 79ccfb0..0af823c 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -62,7 +62,7 @@ int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb)
{
int err;
- err = x->inner_mode->afinfo->extract_output(x, skb);
+ err = xfrm_inner_extract_output(x, skb);
if (err)
return err;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 8b5f486..e9ef9af 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -1219,7 +1219,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
- if (!x->sel.family)
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
x->sel.family = x->props.family;
if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
diff --git a/net/xfrm/Kconfig b/net/xfrm/Kconfig
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 62188c6..7527940 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -84,14 +84,21 @@ int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
{
+ struct xfrm_mode *inner_mode = x->inner_mode;
int err;
err = x->outer_mode->afinfo->extract_input(x, skb);
if (err)
return err;
- skb->protocol = x->inner_mode->afinfo->eth_proto;
- return x->inner_mode->input2(x, skb);
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ }
+
+ skb->protocol = inner_mode->afinfo->eth_proto;
+ return inner_mode->input2(x, skb);
}
EXPORT_SYMBOL(xfrm_prepare_input);
@@ -101,6 +108,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
__be32 seq;
struct xfrm_state *x;
xfrm_address_t *daddr;
+ struct xfrm_mode *inner_mode;
unsigned int family;
int decaps = 0;
int async = 0;
@@ -207,7 +215,15 @@ resume:
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
- if (x->inner_mode->input(x, skb)) {
+ inner_mode = x->inner_mode;
+
+ if (x->sel.family == AF_UNSPEC) {
+ inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
+ if (inner_mode == NULL)
+ goto drop;
+ }
+
+ if (inner_mode->input(x, skb)) {
XFRM_INC_STATS(LINUX_MIB_XFRMINSTATEMODEERROR);
goto drop;
}
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 569d377..2519129 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -124,7 +124,7 @@ int xfrm_output_resume(struct sk_buff *skb, int err)
if (!x)
return dst_output(skb);
- err = nf_hook(x->inner_mode->afinfo->family,
+ err = nf_hook(skb->dst->ops->family,
NF_INET_POST_ROUTING, skb,
NULL, skb->dst->dev, xfrm_output2);
if (unlikely(err != 1))
@@ -193,4 +193,20 @@ int xfrm_output(struct sk_buff *skb)
return xfrm_output2(skb);
}
+
+int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
+{
+ struct xfrm_mode *inner_mode;
+ if (x->sel.family == AF_UNSPEC)
+ inner_mode = xfrm_ip2inner_mode(x,
+ xfrm_af2proto(skb->dst->ops->family));
+ else
+ inner_mode = x->inner_mode;
+
+ if (inner_mode == NULL)
+ return -EAFNOSUPPORT;
+ return inner_mode->afinfo->extract_output(x, skb);
+}
+
EXPORT_SYMBOL_GPL(xfrm_output);
+EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 7ba65e8..d4ca77a 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -388,6 +388,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x)
kfree(x->coaddr);
if (x->inner_mode)
xfrm_put_mode(x->inner_mode);
+ if (x->inner_mode_iaf)
+ xfrm_put_mode(x->inner_mode_iaf);
if (x->outer_mode)
xfrm_put_mode(x->outer_mode);
if (x->type) {
@@ -523,6 +525,8 @@ struct xfrm_state *xfrm_state_alloc(void)
x->lft.hard_packet_limit = XFRM_INF;
x->replay_maxage = 0;
x->replay_maxdiff = 0;
+ x->inner_mode = NULL;
+ x->inner_mode_iaf = NULL;
spin_lock_init(&x->lock);
}
return x;
@@ -796,7 +800,7 @@ xfrm_state_find(xfrm_address_t *daddr, xfrm_address_t *saddr,
selector.
*/
if (x->km.state == XFRM_STATE_VALID) {
- if (!xfrm_selector_match(&x->sel, fl, x->sel.family) ||
+ if ((x->sel.family && !xfrm_selector_match(&x->sel, fl, x->sel.family)) ||
!security_xfrm_state_pol_flow_match(x, pol, fl))
continue;
if (!best ||
@@ -1944,6 +1948,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu)
int xfrm_init_state(struct xfrm_state *x)
{
struct xfrm_state_afinfo *afinfo;
+ struct xfrm_mode *inner_mode;
int family = x->props.family;
int err;
@@ -1962,13 +1967,48 @@ int xfrm_init_state(struct xfrm_state *x)
goto error;
err = -EPROTONOSUPPORT;
- x->inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
- if (x->inner_mode == NULL)
- goto error;
- if (!(x->inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
- family != x->sel.family)
- goto error;
+ if (x->sel.family != AF_UNSPEC) {
+ inner_mode = xfrm_get_mode(x->props.mode, x->sel.family);
+ if (inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL) &&
+ family != x->sel.family) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ x->inner_mode = inner_mode;
+ } else {
+ struct xfrm_mode *inner_mode_iaf;
+
+ inner_mode = xfrm_get_mode(x->props.mode, AF_INET);
+ if (inner_mode == NULL)
+ goto error;
+
+ if (!(inner_mode->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode);
+ goto error;
+ }
+
+ inner_mode_iaf = xfrm_get_mode(x->props.mode, AF_INET6);
+ if (inner_mode_iaf == NULL)
+ goto error;
+
+ if (!(inner_mode_iaf->flags & XFRM_MODE_FLAG_TUNNEL)) {
+ xfrm_put_mode(inner_mode_iaf);
+ goto error;
+ }
+
+ if (x->props.family == AF_INET) {
+ x->inner_mode = inner_mode;
+ x->inner_mode_iaf = inner_mode_iaf;
+ } else {
+ x->inner_mode = inner_mode_iaf;
+ x->inner_mode_iaf = inner_mode;
+ }
+ }
x->type = xfrm_get_type(x->id.proto, family);
if (x->type == NULL)
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index f971ca5..5d96f27 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -288,12 +288,9 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));
x->props.flags = p->flags;
- /*
- * Set inner address family if the KM left it as zero.
- * See comment in validate_tmpl.
- */
- if (!x->sel.family)
+ if (x->props.mode == XFRM_MODE_TRANSPORT)
x->sel.family = p->family;
+
}
/*
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-24 7:16 ` Kazunori MIYAZAWA
2008-03-24 7:44 ` Kazunori MIYAZAWA
@ 2008-03-24 7:48 ` David Miller
1 sibling, 0 replies; 13+ messages in thread
From: David Miller @ 2008-03-24 7:48 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Mon, 24 Mar 2008 16:16:46 +0900
> Sorry for my late reply because of weekend :-)
How were the snow and surf conditions? :-)
> I removed the ifdef from the patch and I roughly tested
> with compling and doing IPsec for ICMP(v6) on the kernel.
Thank you, I will review this soon.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-24 7:44 ` Kazunori MIYAZAWA
@ 2008-03-24 7:49 ` David Miller
2008-03-24 21:53 ` David Miller
1 sibling, 0 replies; 13+ messages in thread
From: David Miller @ 2008-03-24 7:49 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Mon, 24 Mar 2008 16:44:28 +0900
> Sorry, the patch has typo. Please use this.
Ok.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH][IPSEC] inter address family IPsec tunnel on the fly
2008-03-24 7:44 ` Kazunori MIYAZAWA
2008-03-24 7:49 ` David Miller
@ 2008-03-24 21:53 ` David Miller
1 sibling, 0 replies; 13+ messages in thread
From: David Miller @ 2008-03-24 21:53 UTC (permalink / raw)
To: kazunori; +Cc: netdev, usagi-core
From: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Date: Mon, 24 Mar 2008 16:44:28 +0900
> Sorry, the patch has typo. Please use this.
>
> Signed-off-by: Kazunori MIYAZAWA <kazunori@miyazawa.org>
Applied to net-2.6, thank you!
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2008-03-24 21:53 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-03-05 12:37 [PATCH][IPSEC] inter address family IPsec tunnel on the fly Kazunori MIYAZAWA
2008-03-05 21:40 ` David Miller
2008-03-07 6:32 ` Kazunori MIYAZAWA
2008-03-07 7:19 ` David Miller
2008-03-08 14:46 ` Kazunori MIAZAWA
2008-03-08 22:15 ` David Miller
2008-03-14 3:23 ` Kazunori MIYAZAWA
2008-03-21 11:20 ` David Miller
2008-03-24 7:16 ` Kazunori MIYAZAWA
2008-03-24 7:44 ` Kazunori MIYAZAWA
2008-03-24 7:49 ` David Miller
2008-03-24 21:53 ` David Miller
2008-03-24 7:48 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).