steve@umiacs.umd.edu (05/19/89)
If you haven't already, I *strongly* suggest that you look at the results
of the ifconfigs in /etc/rc.boot. Even if everything seems to be set up
correctly, I suspect that the ifconfigs are all failing.
[ Those of you who have seen me prattle on about this before can stop
paying attention now. Long-winded explanation follows. Fine details may
vary, but the gist of what I say below is correct. ]
The time when I had this problem, I traced it down to the following
sequence of events:
1) Diskless client boots, and kernel initializes the network
interface data structures using RARP. The network address
in a 'struct ifnet' is a sockaddr, but RARP treats it as a
sockaddr_in. When the address is assigned to the interface by
the RARP code, the last eight bytes of the sockaddr (the sin_zero
part of a sockaddr_in) aren't zeroed. So now we have as the
interface address:
sa_family: AF_INET
sa_data[0,1]: zero (port doesn't matter)
sa_data[2-5]: client's IP address
sa_data[6-13]: garbage
2) Diskless client tries (in rc.boot) to ifconfig the interface.
Normally, the ifconfig succeeds, and the code path taken by
the SIOCCSIFADDR ioctl makes good and damm sure that the sin_zero
stuff is actually zero. In this case, we'll assume that this is
what's failing.
3) Ftp does a getsockaddr() call on some socket at some point to
get the address it's using. This is stored in the myctladdr variable.
Myctladdr looks like:
sin_family: AF_INET
sin_port: (doesn't matter here)
sin_addr: client's IP address
sin_zero: zeroed
4) Ftp tries to bind the local data port. It copies myctladdr into
another variable (data_addr), sets the port to something or another,
and then tries to bind this address.
5) The kernel tries to find an interface matching this address.
It puts the port number in a temporary, then sets the port number
to zero. It then does something like:
for (each of our interfaces) {
compare (with bcmp) the sockaddr we want to
bind with the address on this interface
if we find a match, use that interface
}
return(EADDRNOTAVAIL);
Note that this is in the network-independent part of the kernel, so
the comparisons *have* to be done on a full sockaddr basis; the
alternative is for the network-independent code to know something
about each possible set of protocols built into the kernel.
Also note that we're comparing AF_INET to AF_INET, zero (port)
to zero (port), the client's IP address to itself... and zeros to
garbage. The one interface that we *should* find in the loop
above fails to match for this reason.
The fix is (for SunOS 3.2, sigh):
RCS file: RCS/if_ether.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -c -r1.2 -r1.3
*** /tmp/,RCSt1a03203 Fri May 19 07:54:30 1989
--- /tmp/,RCSt2a03203 Fri May 19 07:54:31 1989
***************
*** 501,506 ****
--- 501,507 ----
* We need to give the interface a temporary address just
* so it gets initialized. Hopefully, the address won't get used.
*/
+ bzero((caddr_t)&temp, sizeof(temp));
sin->sin_family = AF_INET;
sin->sin_port = 0;
Alternatively, making absolutely sure that the ifconfig works will solve
this problem.
-Steve
Spoken: Steve Miller Domain: steve@mimsy.umd.edu UUCP: uunet!mimsy!steve
Phone: +1-301-454-1808 USPS: UMIACS, Univ. of Maryland, College Park, MD 20742