Hello
In an effort to configure an L2TP/IPsec server on Linux capable of supporting
multiple clients behind a single NAT device I ran into difficulties with pf_key
protocol implementation not being able to exploit all the information
passed to it as a SADB_EXT_ADDRESS_PROXY info. Perhaps as the original source
suggested (/* Nobody uses this, but we try. */) this info has never been used
before.
So I propose the included patch. It changes the following:
1) the amount of information that is stored into the struct xfrm_state's
selector from the SADB_EXT_ADDRESS_PROXY info received from userspace
in the pfkey_msg2xfrm_state() function
The information stored now is:
- the address (stored as the selector's source address, including family)
- the prefix length (stored as the selector's source address prefix length)
- the protocol (stored as selector's protocol)
- the port (stored as selector's source port)
Previously only the address and the prefix length were stored.
2) the conditions under which the SADB_EXT_ADDRESS_PROXY info
is included while converting a struct xfrm_state into a pf_key message
in the pfkey_xfrm_state2msg() function
The conditions now are:
- selector' protcol family is non-zero (ie. the selector is defined)
and
(
- selector's prtocol is non-zero (ie. the protocol is specified)
or
- selector's source port is non-zero (ie. the port is specified)
or
- selector's source address is different from xfrm_state source address
)
Further the case when selector's address is of a different family from
the xfrm_state address is now handled.
3) the way how port information is obtained from struct sadb_address
The port information extraction is now part of
the pfkey_sadb_addr2xfrm_addr() function wich handles that in a protocol
family safe manner, instead of using ((struct sockaddr_in *)x)->sin_port
construct irrespective of the protocol family
NOTES:
- This patch should cause no problems, since as the original source says
nobody uses that info.
- I've also created a patch for racoon (ipsec-tools) to actually pass
that info. Eventually I've been able to establish L2TP/IPSec connections
from multiple clients behind the same NAT to the same L2TP/IPSec linux 2.6
based server.
(The procedure required a manual insertion of certain SPD entries during
the connection establishment but this will hopefuly be handled by
the L2TP daemon automatically soon.)
Here comes the patch (it is against 2.6.17.11):
Signed-off-by: Michal Ruzicka <[EMAIL PROTECTED]>
diff -Naur linux-2.6.17.11.orig/net/key/af_key.c
linux-2.6.17.11/net/key/af_key.c
--- linux-2.6.17.11.orig/net/key/af_key.c 2006-08-23 23:16:33.000000000
+0200
+++ linux-2.6.17.11/net/key/af_key.c 2006-10-18 16:53:48.000000000 +0200
@@ -552,19 +552,28 @@
}
static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
- xfrm_address_t *xaddr)
+ xfrm_address_t *xaddr, __u16 *port)
{
switch (((struct sockaddr*)(addr + 1))->sa_family) {
case AF_INET:
- xaddr->a4 =
- ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
+ {
+ struct sockaddr_in *in = (struct sockaddr_in *)(addr + 1);
+
+ xaddr->a4 = in->sin_addr.s_addr;
+ if (port)
+ *port = in->sin_port;
return AF_INET;
+ }
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
- memcpy(xaddr->a6,
- &((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
- sizeof(struct in6_addr));
+ {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)(addr + 1);
+
+ memcpy(xaddr->a6, &in6->sin6_addr, sizeof(struct in6_addr));
+ if (port)
+ *port = in6->sin6_port;
return AF_INET6;
+ }
#endif
default:
return 0;
@@ -651,6 +660,7 @@
int encrypt_key_size = 0;
int sockaddr_size;
struct xfrm_encap_tmpl *natt = NULL;
+ int proxy_size;
/* address family check */
sockaddr_size = pfkey_sockaddr_size(x->props.family);
@@ -674,14 +684,25 @@
/* identity & sensitivity */
- if ((x->props.family == AF_INET &&
- x->sel.saddr.a4 != x->props.saddr.a4)
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- || (x->props.family == AF_INET6 &&
- memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct
in6_addr)))
-#endif
- )
- size += sizeof(struct sadb_address) + sockaddr_size;
+ if (x->sel.family != 0 &&
+ (x->sel.sport != 0 || x->sel.proto != 0
+ || x->sel.family != x->props.family
+ || (x->sel.family == AF_INET &&
+ x->sel.saddr.a4 != x->props.saddr.a4)
+ /*
+ * the following test is not made dependent on INET6 since
+ * if we get an AF_INET6 address here in situation when we
+ * do not want to support that, than pfkey_sockaddr_size()
+ * returns 0 which results into: return ERR_PTR(-EINVAL);
+ */
+ || (x->sel.family == AF_INET6 &&
+ memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct
in6_addr))))) {
+ proxy_size = pfkey_sockaddr_size(x->sel.family);
+ if (!proxy_size)
+ return ERR_PTR(-EINVAL);
+ size += proxy_size += sizeof(struct sadb_address);
+ } else
+ proxy_size = 0;
if (add_keys) {
if (x->aalg && x->aalg->alg_key_len) {
@@ -790,6 +811,7 @@
lifetime->sadb_lifetime_bytes = x->curlft.bytes;
lifetime->sadb_lifetime_addtime = x->curlft.add_time;
lifetime->sadb_lifetime_usetime = x->curlft.use_time;
+
/* src address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct
sadb_address)+sockaddr_size);
@@ -835,33 +857,15 @@
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
- addr->sadb_address_prefixlen = 32; /* XXX */
addr->sadb_address_reserved = 0;
if (x->props.family == AF_INET) {
+ addr->sadb_address_prefixlen = 32;
+
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->id.daddr.a4;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-
- if (x->sel.saddr.a4 != x->props.saddr.a4) {
- addr = (struct sadb_address*) skb_put(skb,
- sizeof(struct sadb_address)+sockaddr_size);
- addr->sadb_address_len =
- (sizeof(struct sadb_address)+sockaddr_size)/
- sizeof(uint64_t);
- addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
- addr->sadb_address_proto =
- pfkey_proto_from_xfrm(x->sel.proto);
- addr->sadb_address_prefixlen = x->sel.prefixlen_s;
- addr->sadb_address_reserved = 0;
-
- sin = (struct sockaddr_in *) (addr + 1);
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = x->sel.saddr.a4;
- sin->sin_port = x->sel.sport;
- memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
- }
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
@@ -873,20 +877,27 @@
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct
in6_addr));
sin6->sin6_scope_id = 0;
+ }
+#endif
+ else
+ BUG();
- if (memcmp (x->sel.saddr.a6, x->props.saddr.a6,
- sizeof(struct in6_addr))) {
- addr = (struct sadb_address *) skb_put(skb,
- sizeof(struct sadb_address)+sockaddr_size);
- addr->sadb_address_len =
- (sizeof(struct sadb_address)+sockaddr_size)/
- sizeof(uint64_t);
- addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
- addr->sadb_address_proto =
- pfkey_proto_from_xfrm(x->sel.proto);
- addr->sadb_address_prefixlen = x->sel.prefixlen_s;
- addr->sadb_address_reserved = 0;
-
+ if (proxy_size) {
+ addr = (struct sadb_address*) skb_put(skb, proxy_size);
+ addr->sadb_address_len = proxy_size / sizeof(uint64_t);
+ addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
+ addr->sadb_address_proto = pfkey_proto_from_xfrm(x->sel.proto);
+ addr->sadb_address_prefixlen = x->sel.prefixlen_s;
+ addr->sadb_address_reserved = 0;
+ if (x->sel.family == AF_INET) {
+ sin = (struct sockaddr_in *) (addr + 1);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = x->sel.saddr.a4;
+ sin->sin_port = x->sel.sport;
+ memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ else if (x->sel.family == AF_INET6) {
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = x->sel.sport;
@@ -895,10 +906,10 @@
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
- }
#endif
- else
- BUG();
+ else
+ BUG();
+ }
/* auth key */
if (add_keys && auth_key_size) {
@@ -1145,13 +1156,13 @@
/* x->algo.flags = sa->sadb_sa_flags; */
x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *)
ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
- &x->props.saddr);
+ &x->props.saddr, NULL);
if (!x->props.family) {
err = -EAFNOSUPPORT;
goto out;
}
pfkey_sadb_addr2xfrm_addr((struct sadb_address *)
ext_hdrs[SADB_EXT_ADDRESS_DST-1],
- &x->id.daddr);
+ &x->id.daddr, NULL);
if (ext_hdrs[SADB_X_EXT_SA2-1]) {
struct sadb_x_sa2 *sa2 = (void*)ext_hdrs[SADB_X_EXT_SA2-1];
@@ -1164,9 +1175,17 @@
if (ext_hdrs[SADB_EXT_ADDRESS_PROXY-1]) {
struct sadb_address *addr = ext_hdrs[SADB_EXT_ADDRESS_PROXY-1];
- /* Nobody uses this, but we try. */
- x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr);
- x->sel.prefixlen_s = addr->sadb_address_prefixlen;
+ /* racoon uses this */
+ x->sel.family = pfkey_sadb_addr2xfrm_addr(addr, &x->sel.saddr,
+ &x->sel.sport);
+ /* silently ignore unsupported address families */
+ if (x->sel.family) {
+ x->sel.prefixlen_s = addr->sadb_address_prefixlen;
+ x->sel.proto =
+ pfkey_proto_to_xfrm(addr->sadb_address_proto);
+ if (x->sel.sport)
+ x->sel.sport_mask = ~0;
+ }
}
if (ext_hdrs[SADB_X_EXT_NAT_T_TYPE-1]) {
@@ -2128,7 +2147,8 @@
xp->priority = pol->sadb_x_policy_priority;
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
- xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
+ xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr,
+ &xp->selector.sport);
if (!xp->family) {
err = -EINVAL;
goto out;
@@ -2136,12 +2156,11 @@
xp->selector.family = xp->family;
xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.sport)
xp->selector.sport_mask = ~0;
sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
- pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr);
+ pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr, &xp->selector.dport);
xp->selector.prefixlen_d = sa->sadb_address_prefixlen;
/* Amusing, we set this twice. KAME apps appear to set same value
@@ -2149,7 +2168,6 @@
*/
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.dport)
xp->selector.dport_mask = ~0;
@@ -2236,18 +2254,16 @@
memset(&sel, 0, sizeof(sel));
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
- sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
+ sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr, &sel.sport);
sel.prefixlen_s = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.sport)
sel.sport_mask = ~0;
sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1],
- pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
+ pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr, &sel.dport);
sel.prefixlen_d = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
- sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.dport)
sel.dport_mask = ~0;
Thanks
Michal Ruzicka
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html