[PATCH] [RFC] Honor source address in socket.c:send_packet

Hannes Frederic Sowa hannes at stressinduktion.org
Thu Dec 6 00:28:34 UTC 2012


If dhcrelays are employed on routers enabled with dynamic routing
protocols, the selection of the source address of the forwarded dhcp
packet is not deterministic. This hinders the creation of tight packet
filter rules. Instead use the provided "from" argument as source address
and pass it to sendmsg.

Also, this patch adds the ability to use the single socket approach for
linux (only IP_PKTINFO is available).

This patch is a request for comments and was only tested on linux with
dhcrelay and I highly appreciate feedback.
---
 common/discover.c |   3 +-
 common/socket.c   | 170 +++++++++++++++++++++++++++++-------------------------
 2 files changed, 95 insertions(+), 78 deletions(-)

diff --git a/common/discover.c b/common/discover.c
index 1d84219..4e95845 100644
--- a/common/discover.c
+++ b/common/discover.c
@@ -1415,7 +1415,8 @@ isc_result_t got_one (h)
 	if (result < DHCP_FIXED_NON_UDP)
 		return ISC_R_UNEXPECTED;
 
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && \
+	defined(USE_V4_PKTINFO) && !defined(USE_SOCKET_FALLBACK)
 	{
 		/* We retrieve the ifindex from the unused hfrom variable */
 		unsigned int ifindex;
diff --git a/common/socket.c b/common/socket.c
index f95665c..7a348aa 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -78,7 +78,8 @@ static void if_register_multicast(struct interface_info *info);
  * interfaces configured for DHCP if the system has support for IP_PKTINFO
  * and IP_RECVPKTINFO (for example Solaris 11).
  */
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && \
+	defined(USE_V4_PKTINFO) && !defined(USE_SOCKET_FALLBACK)
 static unsigned int global_v4_socket_references = 0;
 static int global_v4_socket = -1;
 #endif
@@ -266,14 +267,19 @@ if_register_socket(struct interface_info *info, int family,
 		log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m");
 #endif
 
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)  && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO))  && defined(USE_V4_PKTINFO)
 	/*
 	 * If we turn on IP_RECVPKTINFO we will be able to receive
 	 * the interface index information of the received packet.
 	 */
 	if (family == AF_INET) {
 		int on = 1;
-		if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, 
+		if (setsockopt(sock, IPPROTO_IP,
+#if defined(IP_PKTINFO)
+				IP_PKTINFO,
+#else
+				IP_RECVPKTINFO,
+#endif
 		               &on, sizeof(on)) != 0) {
 			log_fatal("setsockopt: IPV_RECVPKTINFO: %m");
 		}
@@ -366,7 +372,7 @@ void if_register_receive (info)
 	struct interface_info *info;
 {
 
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && defined(USE_V4_PKTINFO)
 	if (global_v4_socket_references == 0) {
 		global_v4_socket = if_register_socket(info, AF_INET, 0);
 		if (global_v4_socket < 0) {
@@ -401,7 +407,7 @@ void if_register_receive (info)
 void if_deregister_receive (info)
 	struct interface_info *info;
 {
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && defined(USE_V4_PKTINFO)
 	/* Dereference the global v4 socket. */
 	if ((info->rfdesc == global_v4_socket) &&
 	    (info->wfdesc == global_v4_socket) &&
@@ -549,6 +555,40 @@ if_deregister6(struct interface_info *info) {
 }
 #endif /* DHCPv6 */
 
+#if defined(DHCPv6) || \
+	((defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) &&	\
+	 defined(USE_V4_PKTINFO))
+/*
+ * For both send_packet6() and receive_packet6() we need to allocate
+ * space for the cmsg header information.  We do this once and reuse
+ * the buffer.  We also need the control buf for send_packet() and
+ * receive_packet() when we use a single socket and IP_PKTINFO to
+ * send the packet out the correct interface.
+ */
+static void   *control_buf = NULL;
+static size_t  control_buf_len = 0;
+
+static void
+allocate_cmsg_cbuf(void) {
+	control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+	control_buf = dmalloc(control_buf_len, MDL);
+	return;
+}
+
+static int
+prepare_cmsg_cbuf(void) {
+	if (control_buf == NULL) {
+		allocate_cmsg_cbuf();
+		if (control_buf == NULL) {
+			log_error("send_packet6: unable to allocate cmsg header");
+			return(ENOMEM);
+		}
+	}
+	memset(control_buf, 0, control_buf_len);
+	return 0;
+}
+#endif /* DHCPv6, IP_PKTINFO ... */
+
 #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
 	struct interface_info *interface;
@@ -560,24 +600,46 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
 	struct hardware *hto;
 {
 	int result;
+	struct msghdr m;
+	struct iovec v;
+
+	memset(&m, 0, sizeof(m));
+	m.msg_name = to;
+	m.msg_namelen = sizeof(*to);
+	v.iov_base = raw;
+	v.iov_len = len;
+	m.msg_iov = &v;
+	m.msg_iovlen = 1;
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && defined(USE_V4_PKTINFO)
+	struct cmsghdr *cmsg;
+	struct in_pktinfo *pktinfo;
+
+	result = prepare_cmsg_cbuf();
+	if (result)
+		return result;
+
+	m.msg_control = control_buf;
+	m.msg_controllen = control_buf_len;
+	cmsg = CMSG_FIRSTHDR(&m);
+	cmsg->cmsg_level = IPPROTO_IP;
+	cmsg->cmsg_type = IP_PKTINFO;
+	cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+	pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+	memset(pktinfo, 0, sizeof(pktinfo));
+#if defined(IP_PKTINFO)
+	pktinfo->ipi_spec_dst = from;
+#else
+	if (interface->ifp != NULL)
+		pktinfo->ipi_ifindex = interface->ifp->ifr_index;
+#endif
+	m.msg_controllen = cmsg->cmsg_len;
+#endif
+
 #ifdef IGNORE_HOSTUNREACH
 	int retry = 0;
 	do {
 #endif
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
-		struct in_pktinfo pktinfo;
-
-		if (interface->ifp != NULL) {
-			memset(&pktinfo, 0, sizeof (pktinfo));
-			pktinfo.ipi_ifindex = interface->ifp->ifr_index;
-			if (setsockopt(interface->wfdesc, IPPROTO_IP,
-				       IP_PKTINFO, (char *)&pktinfo,
-				       sizeof(pktinfo)) < 0) 
-				log_fatal("setsockopt: IP_PKTINFO: %m");
-		}
-#endif
-		result = sendto (interface -> wfdesc, (char *)raw, len, 0,
-				 (struct sockaddr *)to, sizeof *to);
+		result = sendmsg (interface -> wfdesc, &m, 0);
 #ifdef IGNORE_HOSTUNREACH
 	} while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) &&
 		 result < 0 &&
@@ -646,27 +708,6 @@ static size_t CMSG_SPACE(size_t len) {
 
 #endif /* DHCPv6 */
 
-#if defined(DHCPv6) || \
-	(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
-	 defined(USE_V4_PKTINFO))
-/*
- * For both send_packet6() and receive_packet6() we need to allocate
- * space for the cmsg header information.  We do this once and reuse
- * the buffer.  We also need the control buf for send_packet() and
- * receive_packet() when we use a single socket and IP_PKTINFO to
- * send the packet out the correct interface.
- */
-static void   *control_buf = NULL;
-static size_t  control_buf_len = 0;
-
-static void
-allocate_cmsg_cbuf(void) {
-	control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
-	control_buf = dmalloc(control_buf_len, MDL);
-	return;
-}
-#endif /* DHCPv6, IP_PKTINFO ... */
-
 #ifdef DHCPv6
 /* 
  * For both send_packet6() and receive_packet6() we need to use the 
@@ -701,14 +742,9 @@ ssize_t send_packet6(struct interface_info *interface,
 	 * The space is common between send and receive.
 	 */
 
-	if (control_buf == NULL) {
-		allocate_cmsg_cbuf();
-		if (control_buf == NULL) {
-			log_error("send_packet6: unable to allocate cmsg header");
-			return(ENOMEM);
-		}
-	}
-	memset(control_buf, 0, control_buf_len);
+	result = prepare_cmsg_cbuf();
+	if (result)
+		return result;
 
 	/*
 	 * Initialize our message header structure.
@@ -766,7 +802,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 	struct sockaddr_in *from;
 	struct hardware *hfrom;
 {
-#if !(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO))
+#if !((defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && defined(USE_V4_PKTINFO))
 	SOCKLEN_T flen = sizeof *from;
 #endif
 	int result;
@@ -783,26 +819,16 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 	do {
 #endif
 
-#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+#if (defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) && defined(USE_V4_PKTINFO)
 	struct msghdr m;
 	struct iovec v;
 	struct cmsghdr *cmsg;
 	struct in_pktinfo *pktinfo;
 	unsigned int ifindex;
 
-	/*
-	 * If necessary allocate space for the control message header.
-	 * The space is common between send and receive.
-	 */
-	if (control_buf == NULL) {
-		allocate_cmsg_cbuf();
-		if (control_buf == NULL) {
-			log_error("receive_packet: unable to allocate cmsg "
-				  "header");
-			return(ENOMEM);
-		}
-	}
-	memset(control_buf, 0, control_buf_len);
+	result = prepare_cmsg_cbuf();
+	if (result)
+		return result;
 
 	/*
 	 * Initialize our message header structure.
@@ -897,19 +923,9 @@ receive_packet6(struct interface_info *interface,
 	struct cmsghdr *cmsg;
 	struct in6_pktinfo *pktinfo;
 
-	/*
-	 * If necessary allocate space for the control message header.
-	 * The space is common between send and receive.
-	 */
-	if (control_buf == NULL) {
-		allocate_cmsg_cbuf();
-		if (control_buf == NULL) {
-			log_error("receive_packet6: unable to allocate cmsg "
-				  "header");
-			return(ENOMEM);
-		}
-	}
-	memset(control_buf, 0, control_buf_len);
+	result = prepare_cmsg_cbuf();
+	if (result)
+		return result;
 
 	/*
 	 * Initialize our message header structure.
@@ -1029,7 +1045,7 @@ int supports_multiple_interfaces (ip)
 	struct interface_info *ip;
 {
 #if defined(SO_BINDTODEVICE) || \
-	(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
+	((defined(IP_PKTINFO) || defined(IP_RECVPKTINFO)) &&	\
 	 defined(USE_V4_PKTINFO))
 	return(1);
 #else



More information about the dhcp-workers mailing list