Subject: kern/394: NetBSD NFS diskless operation doesn't work properly
To: None <gnats-admin>
From: Chuck Cranor <chuck@netbsd.ccrc.wustl.edu>
List: netbsd-bugs
Date: 08/04/1994 18:35:09
>Number:         394
>Category:       kern
>Synopsis:       NetBSD NFS diskless operation doesn't work properly
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    gnats-admin (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Aug  4 18:35:06 1994
>Originator:     Chuck Cranor <chuck@maria.wustl.edu>
>Organization:
Washington University, St. Louis  MO
>Release:        netbsd-current (sup'd aug 3)
>Environment:
diskless
System: NetBSD netbsd.ccrc.wustl.edu 1.0_BETA 
        NetBSD 1.0_BETA (CCRC) #0: Thu Aug 4 01:35:19 CDT 1994 
        chuck@xxx.ccrc.wustl.edu:/usr/src/sys/arch/sparc/compile/CCRC sparc


>Description:
	Two bugs prevent NetBSD from operating correctly in a diskless
	environment.

	[1] NetBSD does not use a priv UDP port to talk to rpc.mountd
		and to mount its root file system
	[2] bind() system call will fail if you specify an IP address
	 	(effects ftp)

>How-To-Repeat:
	[1] Try to boot NetBSD from a sun with rpc.mountd run without "-n"
		and "nfs_portmon" set to one.   Mount will fail with error 72
		(I think).

		It is highly advisable to run your NFS server with nfs_portmon
		turned on, or any user can mount your filesystems with a 
		simple user level socket/UDP program and violate your
		security.

	[2] Fix problem [1], and run "ftp"... connect to a host and
		try to do an "ls" command in ftp.  It will fail 
		in "bind" with EADDRNOTAVAIL.

>Fix:
	[1] SunOS seems to always use priv ports for NFS diskless
		boot up.   Copy that idea.   Patch files 
		/usr/src/sys/nfs/krpc_subr.c and nfs_vfsops.c.


*** 1.1	1994/08/03 15:35:29
--- krpc_subr.c	1994/08/05 01:16:02
***************
*** 215,220 ****
--- 215,238 ----
  	if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
  		goto out;
  
+         {
+ 		struct sockaddr_in *sin;
+ 		u_short tport;
+                 MGET(m, M_WAIT, MT_SONAME);
+                 sin = mtod(m, struct sockaddr_in *);
+                 sin->sin_len = m->m_len = sizeof (struct sockaddr_in); 
+                 sin->sin_family = AF_INET;
+                 sin->sin_addr.s_addr = INADDR_ANY;
+                 tport = IPPORT_RESERVED - 1;
+                 sin->sin_port = htons(tport);
+                 while ((error = sobind(so, m)) == EADDRINUSE &&
+                        --tport > IPPORT_RESERVED / 2)
+                         sin->sin_port = htons(tport);
+                 m_freem(m);
+                 if (error)
+                         goto out;
+         }
+ 
  	/*
  	 * Setup socket address for the server.
  	 */



*** 1.1	1994/08/03 15:25:32
--- nfs_vfsops.c	1994/08/03 15:14:28
***************
*** 303,308 ****
--- 303,309 ----
  	args.sotype   = SOCK_DGRAM;
  	args.fh       = (nfsv2fh_t *)ndmntp->ndm_fh;
  	args.hostname = ndmntp->ndm_host;
+ 	args.flags |= NFSMNT_RESVPORT;
  
  	/* Get mbuf for server sockaddr. */
  	MGET(m, MT_SONAME, M_DONTWAIT);


	[2] ifa_ifwithaddr uses bcmp to compare two sockaddrs to see
		if they are equal.  So, if you allocate a sockaddr_in
		you need to zero it or the bcmp will fail.   The following
		code from netinet/in_pcb.c is what generates EADDRNOTAVAIL
		for the bind() system call.

                if (sin->sin_addr.s_addr != INADDR_ANY) {
                        sin->sin_port = 0;              /* yech... */
                        if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
                                return (EADDRNOTAVAIL);
                }

		In nfs/nfs_boot.c there is code to bring up the ethernet 
		interface that you booted off of to mount the root file 
		system.  It sets the interface address with a sockaddr_in 
		but doesn't zero it (causing ifa_ifwithaddr to always fail).


*** 1.1	1994/08/03 21:06:02
--- nfs_boot.c	1994/08/03 20:55:20
***************
*** 160,165 ****
--- 160,166 ----
  	 */
  	/* Set interface address. */
  	sin = (struct sockaddr_in *)&ireq.ifr_addr;
+ 	bzero(sin, sizeof(*sin)); /* must be zeroed for ifa_ifwithaddr! */
  	sin->sin_len = sizeof(*sin);
  	sin->sin_family = AF_INET;
  	sin->sin_addr.s_addr = my_ip.s_addr;


>Audit-Trail:
>Unformatted:


------------------------------------------------------------------------------