Subject: kern/14597: Bad UDP checksums on IPv4 multicast loopback packets
To: None <gnats-bugs@gnats.netbsd.org>
From: None <fwkg7679@mb.infoweb.ne.jp>
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
>Closed-Date:
>Last-Modified:
>Originator:     KUROSAWA Takahiro
>Release:        NetBSD 1.5Y as of 2001-10-30
>Organization:
>Environment:
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
>Description:
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.

>How-To-Repeat:
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" 
output.

#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];

int
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("224.255.222.239", "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;
}

>Fix:
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 *);
 		HTONS(ip->ip_len);
 		HTONS(ip->ip_off);
+
+		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);

>Release-Note:
>Audit-Trail:
>Unformatted: