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