Subject: Re: emcast and PR 7003
To: Jonathan A. Kollasch <jakllsch@kollasch.net>
From: Jonathan Stone <jonathan@Pescadero.dsg.stanford.edu>
List: tech-net
Date: 11/15/2005 11:27:51
hi,

In message <20051115190158.GA16883@vergon.kollasch.net>,
"Jonathan A. Kollasch" writes:

[Omit attached tcpdump showing NetBSD host emitting IP packets with Class-d
source address]

I wrote a more detailed reply on a machine at home. After I went to
slee, that machine went to sleep too; so summarizing from memory...

NetBSD is at fault. Acutely at fault. And probbaly has been since the
1.3I PR which you cited. 

I beleive what's going on is that the Linux app is using a
Linux-supported API (which, if dim memory from ~14 years ago, may well
have originated with Steve Deering's BSD multicast API).

The Linux-centric source is bind()ing its "local" address to a
multicast address.  It may well probably doing a multicast-gropu join
on that same socket.  Linux kernels going back yonks have supported
bind()ing to a broadcast or multicast address; the non-unicast address
is used only for  hashtable (delivery) lookups.

The Linux kernel code (see linux-${VERS}/src/net/ipv4/af_inet.c)
maintains two adressess in the moral equivalent of an inpcb: the
hashtable address, and the address acutally used for outbound traffic.
When the local "hashtable" address is non-unicast, the Linux kernel
sets the second, acutal-outbound address, to INADDR_ANY.


The NetBSD code for in_pcbbind() accepts bind()ing to a non-unicast
"local" address. in_pcbbind() then stores that non-unicast address in
in_laddr, which is used *both* for hashing, *and* for the oubound
source address.

See sys/netinet/udp_usrreq.c:udp_output(): if you bind() to a 
non-unicast address, then you *will* send that address down to ip_output()
as source address.

If your bound local address is multicast, the mincorrect placement of
the code-fragment to drop outbound packetswith a class-D source causes
us to send the bogus packet. Moving that fragment (as per my patch)
will stop us emitting the bogs packet; but won't make your app work.

For that, you need to rework udp_output() to check for an non-unicast
"local" address; if you see one, do a route lookup, find an outbound
interface, and use the address of the outbound interface as your
source address.

Or maye better, rework our in_pcb to keep two "local" addressese, and
hoist the "non-uncast bound address ==> do route lookup, to find and
use local address" smarts up into in_pcbbind().