Subject: DHCP vs. IPsec
To: None <tech-net@netbsd.org>
From: Charles M. Hannum <abuse@spamalicious.com>
List: tech-net
Date: 09/17/2003 19:47:48
So, I use IPsec over my wireless network.  I also use DHCP.

I observed that renewals were not working right from my laptop -- the
server did not seem to see the DHCP REQUESTs, nor did it send any
replies.  Eventually the renewal timed out and it did a new DHCP
DISCOVER sequence and managed to get a new lease, so the network
appeared to function normally.

An iBook running OSX experienced more chaotic behavior, eventually
self-configuring a bogus IP address and not working at all.

On further inspection, I've found that this is due to an assymetry in
the send and receive paths.  All reception is done through BPF, and
cannot handle encrypted packets.  Unicast sends are done through a UDP
socket, and will typically be subject to any IPsec tunneling in place.

Yes, it is possible to tweak the IPsec configuration so that the
outbound packets are not encrypted in this case.  However, that's
irritating, and it does not fix the problem that the path isn't
symmetric.  Furthermore, OSX does not encrypt outgoing DHCP packets
with a similar IPsec configuration, nor does it accept incoming
encrypted packets.

So, I've made the following change to my tree to prevent IPsec
encapsulation of DHCP packets.  With this change, renewals work
correctly from a NetBSD laptop -- and I believe will work correctly
from OSX, but I won't be able to test that for a few days.

This will need a some #ifdefing to go into the master ISC DHCP source,
but I leave that to someone else.


Index: dist/dhcp/common/socket.c
===================================================================
RCS file: /cvsroot/src/dist/dhcp/common/socket.c,v
retrieving revision 1.5
diff -u -r1.5 socket.c
--- dist/dhcp/common/socket.c	2003/02/18 17:08:41	1.5
+++ dist/dhcp/common/socket.c	2003/09/17 09:43:25
@@ -111,6 +111,8 @@
 	struct sockaddr_in name;
 	int sock;
 	int flag;
+	char *buf;
+	char *policy = "out bypass";
 
 #if !defined (HAVE_SO_BINDTODEVICE) && !defined (USE_FALLBACK)
 	/* Make sure only one interface is registered. */
@@ -136,6 +138,13 @@
 	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
 			(char *)&flag, sizeof flag) < 0)
 		log_fatal ("Can't set SO_REUSEADDR option on dhcp socket: %m");
+
+	/* Set a per-socket IPsec policy to prevent encryption. */
+	buf = ipsec_set_policy(policy, strlen(policy));
+	if (setsockopt (sock, IPPROTO_IP, IP_IPSEC_POLICY, buf,
+	    ipsec_get_policylen(buf)) < 0 && errno != ENOPROTOOPT)
+		log_fatal ("Can't set IPsec policy on dhcp socket: %m");
+	free (buf);
 
 	/* Set the BROADCAST option so that we can broadcast DHCP responses.
 	   We shouldn't do this for fallback devices, and we can detect that
Index: dist/dhcp/includes/dhcpd.h
===================================================================
RCS file: /cvsroot/src/dist/dhcp/includes/dhcpd.h,v
retrieving revision 1.5
diff -u -r1.5 dhcpd.h
--- dist/dhcp/includes/dhcpd.h	2003/02/18 17:08:42	1.5
+++ dist/dhcp/includes/dhcpd.h	2003/09/17 09:43:27
@@ -44,6 +44,7 @@
 #ifndef __CYGWIN32__
 #include <sys/types.h>
 #include <netinet/in.h>
+#include <netinet6/ipsec.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <arpa/inet.h>
Index: usr.sbin/dhcp/Makefile.inc
===================================================================
RCS file: /cvsroot/src/usr.sbin/dhcp/Makefile.inc,v
retrieving revision 1.18
diff -u -r1.18 Makefile.inc
--- usr.sbin/dhcp/Makefile.inc	2002/09/18 03:54:28	1.18
+++ usr.sbin/dhcp/Makefile.inc	2003/09/17 09:43:27
@@ -15,8 +15,10 @@
 CPPFLAGS+= -I${DIST} -I${DIST}/includes -Wno-unused
 LDADD+= ${COBJDIR}/libdhcp.a ${MROBJDIR}/libminires.a
 LDADD+= ${OMOBJDIR}/libomapi.a ${DSTOBJDIR}/libdst.a
+LDADD+= -lipsec
 DPADD+= ${COBJDIR}/libdhcp.a ${MROBJDIR}/libminires.a
 DPADD+= ${OMOBJDIR}/libomapi.a ${DSTOBJDIR}/libdst.a
+DPADD+= ${LIBIPSEC}
 
 .if exists(${.CURDIR}/../../Makefile.inc)
 .include "${.CURDIR}/../../Makefile.inc"