Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 19 May 1995 12:15:18 +0800 (WST)
From:      Peter Wemm <peter@haywire.dialix.com>
To:        hackers@FreeBSD.org
Subject:   More on "Hmm.. Strange..."
Message-ID:  <Pine.SV4.3.91.950519105053.23362C-100000@haywire.DIALix.COM>

next in thread | raw e-mail | index | archive | help
Well. Something really wierd is going on.  I think it's a bug, and 
potentially serious, as it makes gated practically useless..

There is some interaction with SO_REUSEADDR.. I have not figured out why 
yet, but take the following program (chopped down rwhod.c) which 
deterministically demonstrates the problem.

-----------
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <protocols/rwhod.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <utmp.h>

int
main(argc, argv)
	int argc;
	char *argv[];
{
	int on = 1;
	char *cp;
	struct sockaddr_in sin;
	struct servent *sp;
	struct hostent *hp;
	int s;

	sp = getservbyname("router", "udp");
	if (sp == NULL) {
		fprintf(stderr, "rwhod: man/who: unknown service\n");
		exit(1);
	}

	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		syslog(LOG_ERR, "socket: %m");
		exit(1);
	}

	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
		syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
		exit(1);
	}

	memset(&sin, 0, sizeof(sin));

	sin.sin_family = AF_INET;
	sin.sin_port = sp->s_port;
#if 0
	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		syslog(LOG_ERR, "bind: %m");
		exit(1);
	}
#endif

	hp = gethostbyname(argv[1]);
	if (hp == NULL) {
		fprintf(stderr, "rwhod: %s unknown host\n", argv[1]);
		exit(1);
	}
	sin.sin_addr.s_addr = *((long *)hp->h_addr);

	(void)sendto(s, "hello", 5, 0, (struct sockaddr *)&sin, sizeof(sin));
	(void)sendto(s, "hellothere", 10, SO_REUSEADDR, (struct sockaddr *)&sin, 
sizeof(sin));

}

The first sendto() always works, and the second sendto() sends the 
datagram to the *wrong interface*!

Just a (yet another) reminder of the config:

ed0: flags=8963<UP,BROADCAST,NOTRAILERS,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
	inet 192.203.228.69 netmask 0xfffffff0 broadcast 192.203.228.79
ppp0: flags=151<UP,POINTOPOINT,RUNNING,PROMISC> mtu 1500
	inet 192.203.228.69 --> 192.203.228.3 netmask 0xfffffff0 

In this case, in the above program, the first sendto() sends a 5 byte 
broadcast to 192.203.228.79 on the ethernet (correct!). The second 
sendto(), the 10 byte datagram gets sent to 192.203.228.79 on the PPP 
interface!!!!!  (of which the remote sends it straight back!  after a 
game of ping-pong, an icmp timer exceeded message is sent).

I recompiled gated to not turn on SO_REUSEADDR at all, but there's some 
kernel glue that appears to turn it on inside the kernel if the socket is 
implicated in any multicasting (as gated does with OSPF and RIP-II)..  

I'm not awake enough to interpret the code to understand what's going 
on.. But I thought I'd mention it in case somebody else might recognised 
it, or knew what was going wrong.

Any suggestions what to try next?

-Peter



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.SV4.3.91.950519105053.23362C-100000>