Subject: kern/14597: Bad UDP checksums on IPv4 multicast loopback packets
To: None <>
From: None <>
List: netbsd-bugs
Date: 11/16/2001 02:05:58
>Number:         14597
>Category:       kern
>Synopsis:       Bad UDP checksums on IPv4 multicast loopback packets
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Nov 16 02:07:01 PST 2001
>Originator:     KUROSAWA Takahiro
>Release:        NetBSD 1.5Y as of 2001-10-30
NetBSD fiva 1.5Y NetBSD 1.5Y (FIVA) #5: Wed Nov 14 21:46:43 JST 2001     kurosawa@fiva:/usr/src/sys/arch/i386/compile/FIVA i386
IPv4 multicast loopback that is enabled by setsockopt(IPPROTO_IP, IP_MULTICAST_LOOP)
doesn't work correctly.  It looks that the multicast loopback packets are dropped
due to bad UDP checksums.
Probably this results from the "delayed" checksum calculation doesn't completed for 
multicast loopback packets.

Run the following program.  The program blocks on recvfrom(), that isn't expected 
behavior.  Stop the program and see udp bad checksum count of the "netstat -s" 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <err.h>

static char sendbuf[] = "This is a multicast message.\n";

static char recvbuf[1024];

main(int argc, char *argv[])
	struct sockaddr_in s_in, s_in_recv;
	struct addrinfo *ai, hints;
	int fd, ret;
	u_char loop = 1;
	size_t addrlen;
	struct ip_mreq imr;

	memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_NUMERICHOST;
	hints.ai_family = PF_INET;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_protocol = IPPROTO_UDP;
	ret = getaddrinfo("", "47000", &hints, &ai);
	if (ret)
		errx(1, "getaddrinfo(): %s", gai_strerror(ret));

	fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if (fd < 0)
		err(1, "socket()");

	s_in = *(struct sockaddr_in *)ai->ai_addr;
	s_in.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(fd, (struct sockaddr *)&s_in, sizeof(s_in)) < 0)
		err(1, "bind()");

	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
		       &loop, sizeof(loop)) < 0) 
		err(1, "setsockopt IP_MULTICAST_LOOP");

	imr.imr_multiaddr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
	imr.imr_interface.s_addr = htonl(INADDR_ANY);
	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
		       &imr, sizeof(imr)) < 0) 
		err(1, "setsockopt IP_ADD_MEMBERSHIP");

	if (sendto(fd, sendbuf, sizeof(sendbuf), 0, ai->ai_addr,
	           ai->ai_addrlen) < 0)
		err(1, "sendto()");
	printf("sent: %s", sendbuf);

	addrlen = sizeof(s_in_recv);
	if (recvfrom(fd, recvbuf, sizeof(recvbuf), 0, 
		     (struct sockaddr *)&s_in_recv, &addrlen) < 0)
		err(1, "recvfrom()");
	printf("received: %s", recvbuf);

	return 0;

The following patch will fix the problem:

--- sys/netinet/ip_output.c-	Tue Sep 18 20:23:35 2001
+++ sys/netinet/ip_output.c	Fri Nov 16 12:04:18 2001
@@ -1631,6 +1631,13 @@
 		ip = mtod(copym, struct ip *);
+		if (copym->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) {
+			in_delayed_cksum(copym);
+			copym->m_pkthdr.csum_flags &=
+			    ~(M_CSUM_TCPv4|M_CSUM_UDPv4);
+		}
 		ip->ip_sum = 0;
 		ip->ip_sum = in_cksum(copym, ip->ip_hl << 2);
 		(void) looutput(ifp, copym, sintosa(dst), NULL);