diff mbox

[PATCHv4,net-next-2.6,1/5] XFRM,IPv6: Remove xfrm_spi_hash() dependency on destination address

Message ID 5a0e320544e253cc903cfd3292600b6bec044a5f.1286139129.git.arno@natisbad.org
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Arnaud Ebalard Oct. 4, 2010, 6:25 a.m. UTC
In the new IPsec architecture [RFC4301], "for an SA used to carry
unicast traffic, the Security Parameters Index (SPI) by itself
suffices to specify an SA".  Section 4.1 of [RFC4301] provides
additional guidance on the topic.

In the old IPsec architecture [RFC2401], a SA "is uniquely identified
by a triple consisting of a Security Parameter Index (SPI), an IP
Destination Address and a security protocol (AH or ESP) identifier".

If an IPsec stack only supports the behavior mandated by the old
IPsec architecture, SAD lookup on inbound packets require the use of
both the SPI and the destination address of the SA.

For inbound IPsec traffic, IRO remapping rules may exist on the MN to
remap the destination address (CoA) into the HoA.  In that case, by
design, the address found in the destination address field of the
packet (CoA) does not match the one in the SA (HoA).

At the moment, Linux XFRM stack includes the address when computing
the hash to perform state lookup by SPI. This patch changes XFRM
state hash computation to prevent destination address to be
used. This will later allow finding states for packets w/ mangled
destination addresses.

Signed-off-by: Arnaud Ebalard <arno@natisbad.org>
---
 net/xfrm/xfrm_hash.h  |   21 +--------------------
 net/xfrm/xfrm_state.c |   20 ++++++++------------
 2 files changed, 9 insertions(+), 32 deletions(-)

Comments

Herbert Xu Oct. 4, 2010, 8:33 a.m. UTC | #1
On Mon, Oct 04, 2010 at 08:25:07AM +0200, Arnaud Ebalard wrote:
>
> At the moment, Linux XFRM stack includes the address when computing
> the hash to perform state lookup by SPI. This patch changes XFRM
> state hash computation to prevent destination address to be
> used. This will later allow finding states for packets w/ mangled
> destination addresses.

I'm fine with doing this for inbound SAs.  However, I can't see
how we can do this for outbound SAs where the SPI is chosen by
the remote end.

Incidentally, it appears that our hash could do with some
strengthening.

Cheers,
Arnaud Ebalard Oct. 4, 2010, 8:51 p.m. UTC | #2
Hi,

Herbert Xu <herbert@gondor.apana.org.au> writes:

> On Mon, Oct 04, 2010 at 08:25:07AM +0200, Arnaud Ebalard wrote:
>>
>> At the moment, Linux XFRM stack includes the address when computing
>> the hash to perform state lookup by SPI. This patch changes XFRM
>> state hash computation to prevent destination address to be
>> used. This will later allow finding states for packets w/ mangled
>> destination addresses.
>
> I'm fine with doing this for inbound SAs.  However, I can't see
> how we can do this for outbound SAs where the SPI is chosen by
> the remote end.

The change *does not* make the lookup in the hash table rely only on the
spi, i.e. __xfrm_state_lookup() is still passed the address. It only
removes the destination address from the computation of the hash. This
allows passing NULL to __xfrm_state_lookup() specifically for input path
and make the lookup only based on the SPI. The destination address check
is done later (possibly after IRO remapping).

Except if I really missed something, this has no impact on outbound SA
(other hashtables are used in that case). 

> Incidentally, it appears that our hash could do with some
> strengthening.

After the change, xfrm_spi_hash() would contain:

 	unsigned int h = (__force u32)spi ^ proto;
        return  ((h ^ (h >> 10) ^ (h >> 20)) & hmask)

which seems to spread the bits h correctly into hmask bits (I mean for
the effort ;-) ). Are you thinking about something like changing the
shifts based on the length of the mask?

Cheers,

a+
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Herbert Xu Oct. 5, 2010, 2:11 a.m. UTC | #3
On Mon, Oct 04, 2010 at 10:51:38PM +0200, Arnaud Ebalard wrote:
> 
> Herbert Xu <herbert@gondor.apana.org.au> writes:
>
> > I'm fine with doing this for inbound SAs.  However, I can't see
> > how we can do this for outbound SAs where the SPI is chosen by
> > the remote end.
> 
> The change *does not* make the lookup in the hash table rely only on the
> spi, i.e. __xfrm_state_lookup() is still passed the address. It only
> removes the destination address from the computation of the hash. This
> allows passing NULL to __xfrm_state_lookup() specifically for input path
> and make the lookup only based on the SPI. The destination address check
> is done later (possibly after IRO remapping).
> 
> Except if I really missed something, this has no impact on outbound SA
> (other hashtables are used in that case). 

I'm thinking about the case where each remote end (or one remote
end with many IP addresses) chooses to use a single SPI which then
all gets hashed to the same bucket.

Outbound SAs are hashed into the same SPI hash table as inbound SAs.

> > Incidentally, it appears that our hash could do with some
> > strengthening.
> 
> After the change, xfrm_spi_hash() would contain:
> 
>  	unsigned int h = (__force u32)spi ^ proto;
>         return  ((h ^ (h >> 10) ^ (h >> 20)) & hmask)
> 
> which seems to spread the bits h correctly into hmask bits (I mean for
> the effort ;-) ). Are you thinking about something like changing the
> shifts based on the length of the mask?

What I meant here is that even without your change, it is relatively
easy to cause many SAs to be hashed to the same bucket.

Cheers,
Herbert Xu Oct. 5, 2010, 4:17 a.m. UTC | #4
On Tue, Oct 05, 2010 at 10:11:14AM +0800, Herbert Xu wrote:
>
> I'm thinking about the case where each remote end (or one remote
> end with many IP addresses) chooses to use a single SPI which then
> all gets hashed to the same bucket.
> 
> Outbound SAs are hashed into the same SPI hash table as inbound SAs.

Another solution would be to create a hash table for inbound SAs
only.  Unfortunately I don't think we have anything in our current
user-interface to indicate whether an SA is inbound or outbound.

So to do this you'll need to use a heuristic such as doing a
lookup on the source/destination address at insertion time.

Cheers,
Arnaud Ebalard Oct. 7, 2010, 8:13 p.m. UTC | #5
Hi,

Herbert Xu <herbert@gondor.apana.org.au> writes:

> On Tue, Oct 05, 2010 at 10:11:14AM +0800, Herbert Xu wrote:
>>
>> I'm thinking about the case where each remote end (or one remote
>> end with many IP addresses) chooses to use a single SPI which then
>> all gets hashed to the same bucket.
>> 
>> Outbound SAs are hashed into the same SPI hash table as inbound SAs.
>
> Another solution would be to create a hash table for inbound SAs
> only.  Unfortunately I don't think we have anything in our current
> user-interface to indicate whether an SA is inbound or outbound.
>
> So to do this you'll need to use a heuristic such as doing a
> lookup on the source/destination address at insertion time.

I spent some time scratching my head trying to find a good solution. It
would indeed be perfect to have a specific hash table for inbound
SA. But as you point, this would only be via a heuristic at insertion
time and there are various cases which would not work: a SA can be
installed w/o any of the addresses being configured on the system.

I think I will try the following alternative approach based on your
comments and proposals:

 - drop my patch to change spi hash computation
 - handle destination address remapping during input upon failure of
   xfrm_state_lookup()
 - handle source address remapping as it is currently done in the patch,
   i.e. by comparing received one against x->props.saddr once the
   state found and do 

To support the destination address remapping, I will have to reverse the
logic I currently have for destination remapping states, to allow the
lookup to be done based on the on-wire address (CoA) instead of the
address in the SA (HoA). If a remapping state is found for the on-wire
address, then a new lookup is done using the associated HoA this time.

I think it would make the feature easier less intrusive for the IPsec
stack.

Thanks for your support and patience, Herbert.

a+
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Herbert Xu Oct. 8, 2010, 12:42 a.m. UTC | #6
On Thu, Oct 07, 2010 at 10:13:04PM +0200, Arnaud Ebalard wrote:
> 
> I think I will try the following alternative approach based on your
> comments and proposals:
> 
>  - drop my patch to change spi hash computation
>  - handle destination address remapping during input upon failure of
>    xfrm_state_lookup()
>  - handle source address remapping as it is currently done in the patch,
>    i.e. by comparing received one against x->props.saddr once the
>    state found and do 

I'm fine with this from IPsec's point-of-view.

On the other hand, if it is at all possible to setup these remapping
entries in the SADB beforehand, that would definitely be my
preferred solution.

Cheers,
diff mbox

Patch

diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h
index 8e69533..19eeee7 100644
--- a/net/xfrm/xfrm_hash.h
+++ b/net/xfrm/xfrm_hash.h
@@ -4,16 +4,6 @@ 
 #include <linux/xfrm.h>
 #include <linux/socket.h>
 
-static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)
-{
-	return ntohl(addr->a4);
-}
-
-static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
-{
-	return ntohl(addr->a6[2] ^ addr->a6[3]);
-}
-
 static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
 {
 	u32 sum = (__force u32)daddr->a4 + (__force u32)saddr->a4;
@@ -60,18 +50,9 @@  static inline unsigned __xfrm_src_hash(xfrm_address_t *daddr,
 }
 
 static inline unsigned int
-__xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family,
-		unsigned int hmask)
+__xfrm_spi_hash(__be32 spi, u8 proto, unsigned int hmask)
 {
 	unsigned int h = (__force u32)spi ^ proto;
-	switch (family) {
-	case AF_INET:
-		h ^= __xfrm4_addr_hash(daddr);
-		break;
-	case AF_INET6:
-		h ^= __xfrm6_addr_hash(daddr);
-		break;
-	}
 	return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
 }
 
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index eb96ce5..b6a4d8d 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -30,7 +30,7 @@ 
 
 /* Each xfrm_state may be linked to two tables:
 
-   1. Hash table by (spi,daddr,ah/esp) to find SA by SPI. (input,ctl)
+   1. Hash table by (spi,ah/esp) to find SA by SPI. (input,ctl)
    2. Hash table by (daddr,family,reqid) to find what SAs exist for given
       destination/tunnel endpoint. (output)
  */
@@ -67,9 +67,9 @@  static inline unsigned int xfrm_src_hash(struct net *net,
 }
 
 static inline unsigned int
-xfrm_spi_hash(struct net *net, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
+xfrm_spi_hash(struct net *net, __be32 spi, u8 proto)
 {
-	return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
+	return __xfrm_spi_hash(spi, proto, net->xfrm.state_hmask);
 }
 
 static void xfrm_hash_transfer(struct hlist_head *list,
@@ -95,9 +95,7 @@  static void xfrm_hash_transfer(struct hlist_head *list,
 		hlist_add_head(&x->bysrc, nsrctable+h);
 
 		if (x->id.spi) {
-			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
-					    x->id.proto, x->props.family,
-					    nhashmask);
+			h = __xfrm_spi_hash(x->id.spi, x->id.proto, nhashmask);
 			hlist_add_head(&x->byspi, nspitable+h);
 		}
 	}
@@ -679,7 +677,7 @@  xfrm_init_tempstate(struct xfrm_state *x, struct flowi *fl,
 
 static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark, xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family)
 {
-	unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
+	unsigned int h = xfrm_spi_hash(net, spi, proto);
 	struct xfrm_state *x;
 	struct hlist_node *entry;
 
@@ -868,7 +866,7 @@  found:
 			h = xfrm_src_hash(net, daddr, saddr, encap_family);
 			hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
 			if (x->id.spi) {
-				h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
+				h = xfrm_spi_hash(net, x->id.spi, x->id.proto);
 				hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
 			}
 			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
@@ -942,9 +940,7 @@  static void __xfrm_state_insert(struct xfrm_state *x)
 	hlist_add_head(&x->bysrc, net->xfrm.state_bysrc+h);
 
 	if (x->id.spi) {
-		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
-				  x->props.family);
-
+		h = xfrm_spi_hash(net, x->id.spi, x->id.proto);
 		hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
 	}
 
@@ -1535,7 +1531,7 @@  int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high)
 	}
 	if (x->id.spi) {
 		spin_lock_bh(&xfrm_state_lock);
-		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+		h = xfrm_spi_hash(net, x->id.spi, x->id.proto);
 		hlist_add_head(&x->byspi, net->xfrm.state_byspi+h);
 		spin_unlock_bh(&xfrm_state_lock);