Subject: kern/36870: FAST_IPSEC cannot handle optional SA -> busy loop
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Wolfgang Stukenbrock <Wolfgang.Stukenbrock@nagler-company.com>
List: netbsd-bugs
Date: 08/31/2007 08:10:01
>Number:         36870
>Category:       kern
>Synopsis:       FAST_IPSEC cannot handle optional SA -> busy loop
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Aug 31 08:10:01 +0000 2007
>Originator:     Wolfgang Stukenbrock
>Release:        NetBSD 3.1
>Organization:
Dr. Nagler & Company GmbH
	
>Environment:
	
	
System: NetBSD test-s0 3.1 NetBSD 3.1 (test-s0) #0: Tue Apr 3 11:33:43 CEST 2007 root@test-s0:/usr/src/sys/arch/i386/compile/test-s0 i386
Architecture: i386
Machine: i386
>Description:
	In netinet/ip_ouput.c IPSEC processing is done if required to each
	outgoing packet in ip_output(). After IPSEC processing, the packet
	is passed to ip_output() again in order to send it over the network.
	To avoid processing the packet again, a check is done, if there is
	a tag attached to the packet that matches the selected rule.
	If a matching tag is present no IPSEC processing will be done, because
	it has been done before.
	Now here are multiple things that are either not correct or has not
	been completly implemented.
	1. There is a lookup for a tag PACKET_TAG_IPSEC_PENDING_TDB in order
	   to see if IPSEC processing has been done before - as the comment
	   says. But this tag will never be attached to any packet in the
	   whole kernel sources.
	2. If a tag with PACKET_TAG_IPSEC_OUT_DONE or
	   PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED is found, only the first policy
	   entry is checked, but ther may be nore than one.
	   If there is no SA attached to the first policy request entry, IPSEC
	   processing is done and no check is done if the tag matches a second,
	   third or whatever policy request.
	   (Here the busy-loop happens if the first request is of type "use"
	   and no matching SA is currently installed.)

	Here we have two different approches here to detect that IPSEC
	processing has been done before.
	I'm not shure what IPSEC specs say about processing. The first check
	assumes that there may at most one IPSEC policy applied to a packet.
	The second one assumes that there may be more than one (if an SA is
	present all the time, it will work ...) and a second policy may be
	applied to a packet after the first policy is done, and so on ...

	From my point of view only the first assumption makes sence, but I may
	be wrong here.
	Reson for my oppinion:
	If I want to setup a compressed ipsec tunnel to connect different
	locations to each other it is more common to me to setup one rule to
	do it. "spdadd ... ipcomp/tunnel/.../use esp/tunnel/..../require;"
	(remark: the ipcomp must be setup as tunnel too so that the packet
	gets decompressed at the other esp-tunnel system. transport will lead
	to a compressed packet send to the final destination and that one
	should know nothing about the tunnel itself.)
	I know know way to setup something like this in several rules, because
	I do not know how to tell the system to apply a compression rule only
	if a SA is present and then not use an esp rule with the same adresses
	specified in them.

	If this assumption is correct the second check does not make sence at
	all - not only due to the fact that only the first request is visited.

	Therefore some corrections are required to the current code.
	I cannot give you a "final" patch, because I do not know in what
	direction we should go. I've added a "workaround" for IPv4 below that
	will enable the system to use a compresses tunnel.

	I would recommend to use the PACKET_TAG_IPSEC_PENDING_TDB tag in the
	final version and allow only at most one IPSEC policy to be applied to
	a packet.

	In the actual implementation - even in the last CVS-Version in the MAIN
	tree - it is impossible to use compression in a ESP-tunnel.
	And compression in the tunnel is very usefull to avoid ESP fragmentation
	and reduce the traffic volume over the WAN!
>How-To-Repeat:
	Setup an ipsec tunnel with compression like the following example:
	  spdadd -n 1.2.3.0/24 10.11.12.0/24 any -P out ipsec
	    ipcomp/tunnel/1.2.3.4-10.11.12.13/use
	    esp/tunnel/1.2.3.4-10.11.12.13/require;
	  add -n 1.2.3.4 10.11.12.13 esp 3456 -E blowfish-cbc "123451234512345";
	Remark: there is no SA for the ipcomp part.
	If you send a packet that matches this rule, the rule is applied
	again and again until the packet is droped due to the MAXPACKET limit
	in a busy loop.
>Fix:
	This fix is only a workaround for IP4.
	Something equal must be applied to the IP6 stuff in netipsec/ipsec.c
	to get that part running.
	see descript above for more information.
	The fix will comment out most of the current loop detection code and
	assumes that if a tag was found IPSEC is done.
	It works fine here - but I use a static key setup. I have no idea if it
	will work with racoon ...


*** ip_output.c	2007/04/24 17:21:13	1.2
--- ip_output.c	2007/08/31 07:26:48
***************
*** 689,694 ****
--- 689,695 ----
  			if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
  			    mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
  				continue;
+ #if 0 /* workaround for busy loop */
  			/*
  			 * Check if policy has an SA associated with it.
  			 * This can happen when an SP has yet to acquire
***************
*** 702,707 ****
--- 703,709 ----
  			    tdbi->proto == sp->req->sav->sah->saidx.proto &&
  			    bcmp(&tdbi->dst, &sp->req->sav->sah->saidx.dst,
  				 sizeof (union sockaddr_union)) == 0) {
+ #endif /* workaround for busy loop */
  				/*
  				 * No IPsec processing is needed, free
  				 * reference to SP.
***************
*** 712,718 ****
--- 714,722 ----
  				KEY_FREESP(&sp), sp = NULL;
  				splx(s);
  				goto spd_done;
+ #if 0 /* workaround for busy loop */
  			}
+ #endif /* workaround for busy loop */
  		}
  
  		/*

>Unformatted: