Package: isc-dhcp-client
Version: 4.2.4-4
Followup-For: Bug #671707

Dear Maintainer,
After switching the type of network interface of KVM virtual host
(Debian Sid) to virtio, I have got:

Internet Systems Consortium DHCP Client 4.2.4
Copyright 2004-2012 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/

Listening on LPF/eth0/00:16:3e:52:3d:8b
Sending on   LPF/eth0/00:16:3e:52:3d:8b
Sending on   Socket/fallback
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 8
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 21
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 14
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 10
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 8
5 bad udp checksums in 5 packets
No DHCPOFFERS received.
Unable to obtain a lease on first try.  Exiting.
Failed to bring up eth0.

I have configured a static bridge br0 and dnsmasq serving DHCP requests
of my virtual hosts.

I found this bug-report after some googling. I have tried to rebuild
isc-dhcp package with a patch dhcp-4.2.2-xen-checksum.patch from Fedora
project (attached) and the resulting dhclient works ok.
The patch is extracted from 
http://mirror.karneval.cz/pub/linux/fedora/linux/releases/17/Fedora/source/SRPMS/d/dhcp-4.2.4-0.4.rc1.fc17.src.rpm
I have refreshed the patch using quilt.

Can you add this patch into the Debian package too?

Thanks for your work!
Cheers
-- 
Zito

-- System Information:
Debian Release: 7.0
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.2.0-4-amd64 (SMP w/1 CPU core)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

Versions of packages isc-dhcp-client depends on:
ii  debianutils      4.3.4
ii  iproute          20120521-3
ii  isc-dhcp-common  4.2.4-4
ii  libc6            2.16-0experimental1

isc-dhcp-client recommends no packages.

Versions of packages isc-dhcp-client suggests:
pn  avahi-autoipd  <none>
pn  resolvconf     <none>

-- no debconf information
Index: isc-dhcp-4.2.4/common/bpf.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/bpf.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/bpf.c	2013-01-16 01:03:10.000000000 +0100
@@ -485,7 +485,7 @@
 		offset = decode_udp_ip_header (interface,
 					       interface -> rbuf,
 					       interface -> rbuf_offset,
-  					       from, hdr.bh_caplen, &paylen);
+  					       from, hdr.bh_caplen, &paylen, 0);
 
 		/* If the IP or UDP checksum was bad, skip the packet... */
 		if (offset < 0) {
Index: isc-dhcp-4.2.4/common/dlpi.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/dlpi.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/dlpi.c	2013-01-16 01:03:10.000000000 +0100
@@ -693,7 +693,7 @@
 	length -= offset;
 #endif
 	offset = decode_udp_ip_header (interface, dbuf, bufix,
-				       from, length, &paylen);
+				       from, length, &paylen, 0);
 
 	/*
 	 * If the IP or UDP checksum was bad, skip the packet...
Index: isc-dhcp-4.2.4/common/lpf.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/lpf.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/lpf.c	2013-01-16 01:03:10.000000000 +0100
@@ -29,19 +29,33 @@
 #include "dhcpd.h"
 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
 #include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <sys/uio.h>
 #include <errno.h>
 
 #include <asm/types.h>
 #include <linux/filter.h>
 #include <linux/if_ether.h>
+#include <linux/if_packet.h>
 #include <netinet/in_systm.h>
-#include <net/if_packet.h>
 #include "includes/netinet/ip.h"
 #include "includes/netinet/udp.h"
 #include "includes/netinet/if_ether.h"
 #include <net/if.h>
 
+#ifndef PACKET_AUXDATA
+#define PACKET_AUXDATA 8
+
+struct tpacket_auxdata
+{
+	__u32		tp_status;
+	__u32		tp_len;
+	__u32		tp_snaplen;
+	__u16		tp_mac;
+	__u16		tp_net;
+};
+#endif
+
 /* Reinitializes the specified interface after an address change.   This
    is not required for packet-filter APIs. */
 
@@ -67,10 +81,14 @@
 	struct interface_info *info;
 {
 	int sock;
-	struct sockaddr sa;
+	union {
+		struct sockaddr_ll ll;
+		struct sockaddr common;
+	} sa;
+	struct ifreq ifr;
 
 	/* Make an LPF socket. */
-	if ((sock = socket(PF_PACKET, SOCK_PACKET,
+	if ((sock = socket(PF_PACKET, SOCK_RAW,
 			   htons((short)ETH_P_ALL))) < 0) {
 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
@@ -85,11 +103,17 @@
 		log_fatal ("Open a socket for LPF: %m");
 	}
 
+	memset (&ifr, 0, sizeof ifr);
+	strncpy (ifr.ifr_name, (const char *)info -> ifp, sizeof ifr.ifr_name);
+	ifr.ifr_name[IFNAMSIZ-1] = '\0';
+	if (ioctl (sock, SIOCGIFINDEX, &ifr))
+		log_fatal ("Failed to get interface index: %m");
+
 	/* Bind to the interface name */
 	memset (&sa, 0, sizeof sa);
-	sa.sa_family = AF_PACKET;
-	strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data);
-	if (bind (sock, &sa, sizeof sa)) {
+	sa.ll.sll_family = AF_PACKET;
+	sa.ll.sll_ifindex = ifr.ifr_ifindex;
+	if (bind (sock, &sa.common, sizeof sa)) {
 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
 		    errno == EAFNOSUPPORT || errno == EINVAL) {
@@ -171,9 +195,18 @@
 void if_register_receive (info)
 	struct interface_info *info;
 {
+	int val;
+
 	/* Open a LPF device and hang it on this interface... */
 	info -> rfdesc = if_register_lpf (info);
 
+	val = 1;
+	if (setsockopt (info -> rfdesc, SOL_PACKET, PACKET_AUXDATA, &val,
+			sizeof val) < 0) {
+		if (errno != ENOPROTOOPT)
+			log_fatal ("Failed to set auxiliary packet data: %m");
+	}
+
 #if defined (HAVE_TR_SUPPORT)
 	if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
 		lpf_tr_filter_setup (info);
@@ -295,7 +328,6 @@
 	double hh [16];
 	double ih [1536 / sizeof (double)];
 	unsigned char *buf = (unsigned char *)ih;
-	struct sockaddr_pkt sa;
 	int result;
 	int fudge;
 
@@ -316,17 +348,7 @@
 				(unsigned char *)raw, len);
 	memcpy (buf + ibufp, raw, len);
 
-	/* For some reason, SOCK_PACKET sockets can't be connected,
-	   so we have to do a sentdo every time. */
-	memset (&sa, 0, sizeof sa);
-	sa.spkt_family = AF_PACKET;
-	strncpy ((char *)sa.spkt_device,
-		 (const char *)interface -> ifp, sizeof sa.spkt_device);
-	sa.spkt_protocol = htons(ETH_P_IP);
-
-	result = sendto (interface -> wfdesc,
-			 buf + fudge, ibufp + len - fudge, 0, 
-			 (const struct sockaddr *)&sa, sizeof sa);
+	result = write (interface -> wfdesc, buf + fudge, ibufp + len - fudge);
 	if (result < 0)
 		log_error ("send_packet: %m");
 	return result;
@@ -343,14 +365,35 @@
 {
 	int length = 0;
 	int offset = 0;
+	int nocsum = 0;
 	unsigned char ibuf [1536];
 	unsigned bufix = 0;
 	unsigned paylen;
+	unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+	struct iovec iov = {
+		.iov_base = ibuf,
+		.iov_len = sizeof ibuf,
+	};
+	struct msghdr msg = {
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+		.msg_control = cmsgbuf,
+		.msg_controllen = sizeof(cmsgbuf),
+	};
+	struct cmsghdr *cmsg;
 
-	length = read (interface -> rfdesc, ibuf, sizeof ibuf);
+	length = recvmsg (interface -> rfdesc, &msg, 0);
 	if (length <= 0)
 		return length;
 
+	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+		if (cmsg->cmsg_level == SOL_PACKET &&
+		    cmsg->cmsg_type == PACKET_AUXDATA) {
+			struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
+			nocsum = aux->tp_status & TP_STATUS_CSUMNOTREADY;
+		}
+	}
+
 	bufix = 0;
 	/* Decode the physical header... */
 	offset = decode_hw_header (interface, ibuf, bufix, hfrom);
@@ -367,7 +410,7 @@
 
 	/* Decode the IP and UDP headers... */
 	offset = decode_udp_ip_header (interface, ibuf, bufix, from,
-				       (unsigned)length, &paylen);
+				       (unsigned)length, &paylen, nocsum);
 
 	/* If the IP or UDP checksum was bad, skip the packet... */
 	if (offset < 0)
Index: isc-dhcp-4.2.4/common/nit.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/nit.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/nit.c	2013-01-16 01:03:10.000000000 +0100
@@ -369,7 +369,7 @@
 
 	/* Decode the IP and UDP headers... */
 	offset = decode_udp_ip_header (interface, ibuf, bufix,
-				       from, length, &paylen);
+				       from, length, &paylen, 0);
 
 	/* If the IP or UDP checksum was bad, skip the packet... */
 	if (offset < 0)
Index: isc-dhcp-4.2.4/common/packet.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/packet.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/packet.c	2013-01-16 01:03:10.000000000 +0100
@@ -226,7 +226,7 @@
 decode_udp_ip_header(struct interface_info *interface,
 		     unsigned char *buf, unsigned bufix,
 		     struct sockaddr_in *from, unsigned buflen,
-		     unsigned *rbuflen)
+		     unsigned *rbuflen, int nocsum)
 {
   unsigned char *data;
   struct ip ip;
@@ -342,7 +342,7 @@
   }
 
   udp_packets_seen++;
-  if (usum && usum != sum) {
+  if (!nocsum && usum && usum != sum) {
 	  udp_packets_bad_checksum++;
 	  if (udp_packets_seen > 4 &&
 	      (udp_packets_seen / udp_packets_bad_checksum) < 2) {
Index: isc-dhcp-4.2.4/common/upf.c
===================================================================
--- isc-dhcp-4.2.4.orig/common/upf.c	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/common/upf.c	2013-01-16 01:03:10.000000000 +0100
@@ -320,7 +320,7 @@
 
 	/* Decode the IP and UDP headers... */
 	offset = decode_udp_ip_header (interface, ibuf, bufix,
-				       from, length, &paylen);
+				       from, length, &paylen, 0);
 
 	/* If the IP or UDP checksum was bad, skip the packet... */
 	if (offset < 0)
Index: isc-dhcp-4.2.4/includes/dhcpd.h
===================================================================
--- isc-dhcp-4.2.4.orig/includes/dhcpd.h	2013-01-16 01:03:10.000000000 +0100
+++ isc-dhcp-4.2.4/includes/dhcpd.h	2013-01-16 01:03:10.000000000 +0100
@@ -2805,7 +2805,7 @@
 			  unsigned, struct hardware *);
 ssize_t decode_udp_ip_header (struct interface_info *, unsigned char *,
 			      unsigned, struct sockaddr_in *,
-			      unsigned, unsigned *);
+			      unsigned, unsigned *, int);
 
 /* ethernet.c */
 void assemble_ethernet_header (struct interface_info *, unsigned char *,

Reply via email to