lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 5 Mar 2008 21:37:27 +0900
From:	Kazunori MIYAZAWA <kazunori@...azawa.org>
To:	David Miller <davem@...emloft.net>
Cc:	netdev@...r.kernel.org, usagi-core@...ux-ipv6.org
Subject: [PATCH][IPSEC] inter address family IPsec tunnel on the fly

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@...ux-ipv6.org>
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@...ux-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;
 }
 
 /*
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists