Subject: Re: bootp
To: Jason Thorpe <thorpej@nas.nasa.gov>
From: Tor Egge <Tor.Egge@idt.unit.no>
List: tech-kern
Date: 09/04/1995 01:11:31
----Next_Part(Mon_Sep__4_01:07:54_1995)--
Content-Type: Text/Plain; charset=us-ascii

> I'm thinking of working on a bootp version of nfs_boot.c, but I'd like a 
> bit of input on the semantics first...

I've already done some work on this, and have a working implementation
(sort of).

> Ideally, what I'd like is to have an nfs_boot.c that understands both 
> rarp/bootparams _and_ bootp transparently, without having to set some 
> sort of compile-time option.

This would be an advantage, for generating a generic diskless kernel.

> Looking at nfs_boot_init(), my initial thought is to attempt a bootp 
> request after the call to revarpwhoami(), and then panic if _both_ fail.  
> (Maybe before?)  In any case, if the bootp succeeds, then use it rather 
> than bootparams to get the server/root/swap/gateway info.

I think the best place to try bootp is if revarp fails.  We need to
decide what to do if revarp succeeds -- one option would be to commit
to using rpc.bootparams, but note that the krpc_call code does not
currently implement error returns on excessive time-outs, so in that
case the commitment to using rpc.bootparams would be total.  Another
option would be to try bootp before bootparams (but the code below has
inherited the lack of error returns on timeouts from the krpc_call
code), although that would probably be a too large deviation from
tradition.

> It strikes me as not being a terribly difficult problem to solve, I'd 
> just like some input on it first.

We have a subnet with a number of PCs connected, but with no local
Unix server.  We want to be able to use these PCs as diskless X
terminals, using NetBSD and XFree86.  These machines have 3C509 cards,
and due to the lack of netboot support for this card, we need a local
kernel.  We intend to use a small DOS partition, containing our
modified boot program (pr 1002, not yet incorporated into the tree),
and a suitable kernel.  Since we have no Unix server on this subnet,
we cannot use rarp. Instead we want to use BOOTP gatewayed through the
router connected to this subnet.

So far I've identified a few problems:

  1. With the current network implementation, the client can not
     receive replies to it's still unknown IP address, but only
     broadcasted (255.255.255.255) packets.

     This problem can be solved in three ways:

       i) Broadcast the answer. RFC1542 defines a flag bit that the
          client can set in the request if it needs a broadcast answer.
	
      ii) Modify ip_input.c, so it accepts any ip address if one of
	  the interface addresses is 0.0.0.0.

     iii) Use bpf to capture the reply packet before it hits the IP
          stack..

     I prefer i), but if you have old BOOTP servers or deficient BOOTP
     gateways it may be necessary to use either ii) or iii).  I've not
     implemented iii), but the required code could probably be lifted
     from the user-land bootp client.  BTW, let us note here that the
     NetBSD BOOTP server does not handle the broadcast flag correctly.

  2. The RFCs do not specify fields for specifying the address of the
     NFS server from which to mount the root file system, or swap file
     path name.

     I've used the specified swap server as the root file system
     server, and used tag 128 (additional site-specific information)
     for swap path name.  There must be a better way.  Any ideas?

  3. The length of the path names may require somewhat large bootp
     reply packets. Some bootp daemons only send back packets of the
     same size as the client sent. Thus the client needs to send
     larger packets than the size specified in RFC951. This may lead
     to problems with older implementations of bootp daemons.

To activate the bootp code, you need to specify 

	option BOOTP

and possibly also

	option BOOTP_COMPAT

in your kernel config file, after applying the enclosed patches.  Note
that this currently turns off usage of revarp and rpc.bootparams.
Also note that the patch below contains lots of currently dead
debugging code, I assume that's easily removed.

 - Tor Egge, + a few local language consultants

----Next_Part(Mon_Sep__4_01:07:54_1995)--
Content-Type: Text/Plain; charset=us-ascii
Content-Description: "First version of bootp support"

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 09/03/1995 21:51 UTC by root@ikke.idt.unit.no
# Source directory /usr/TEGGE/bootp
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1233 -rw-r--r-- Makefile
#  22839 -rw-r--r-- bootp_subr.c
#    399 -rw-r--r-- patch.files
#    856 -rw-r--r-- patch.ip_input.c
#    411 -rw-r--r-- patch.nfs_vfsops.c
#
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
echo 'x - extracting Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
IP_INPUT=$(DESTDIR)/usr/src/sys/netinet/ip_input.c
NFS_VFSOPS=$(DESTDIR)/usr/src/sys/nfs/nfs_vfsops.c
FILES=$(DESTDIR)/usr/src/sys/conf/files
BOOTP_SUBR=$(DESTDIR)/usr/src/sys/nfs/bootp_subr.c
all:: tstamp-ip_input.c tstamp-nfs_vfsops.c tstamp-files $(BOOTP_SUBR)
X
$(IP_INPUT): .PRECIOUS
X
tstamp-ip_input.c: $(IP_INPUT)
X	grep BOOTP_COMPAT $(IP_INPUT) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.ip_input.c
X	touch tstamp-ip_input.c
X
$(IP_INPUT): patch.ip_input.c
X	grep BOOTP_COMPAT $(IP_INPUT) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.ip_input.c
X
$(NFS_VFSOPS): .PRECIOUS
X
tstamp-nfs_vfsops.c: $(NFS_VFSOPS)
X	grep BOOTP $(NFS_VFSOPS) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.nfs_vfsops.c
X	touch tstamp-nfs_vfsops.c
X
$(NFS_VFSOPS): patch.nfs_vfsops.c
X	grep BOOTP $(NFS_VFSOPS) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.nfs_vfsops.c
X
X
tstamp-files: $(FILES)
X	grep bootp $(FILES) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.files
X	touch tstamp-files
X
$(FILES): patch.files
X	grep bootp $(FILES) > /dev/null 2>&1 || \
X	( cd $(DESTDIR)/; patch -p ) < patch.files
X
$(BOOTP_SUBR): 	bootp_subr.c
X	cp -p bootp_subr.c $(BOOTP_SUBR) 
X
clean:
X	rm -f tstamp-*
SHAR_EOF
chmod 0644 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 1233 -eq "$Wc_c" ||
	echo 'Makefile: original size 1233, current size' "$Wc_c"
fi
# ============= bootp_subr.c ==============
if test -f 'bootp_subr.c' -a X"$1" != X"-c"; then
	echo 'x - skipping bootp_subr.c (File already exists)'
else
echo 'x - extracting bootp_subr.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'bootp_subr.c' &&
/*
X * Copyright (c) 1995 Gordon Ross, Adam Glass
X * Copyright (c) 1992 Regents of the University of California.
X * All rights reserved.
X *
X * This software was developed by the Computer Systems Engineering group
X * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
X * contributed to Berkeley.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Lawrence Berkeley Laboratory and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * based on:
X *      nfs/krpc_subr.c
X *	$NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $
X */
X
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/reboot.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
X
#include <net/if.h>
#include <net/route.h>
X
#include <netinet/in.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
X
#include <nfs/rpcv2.h>
#include <nfs/nfsv2.h>
#include <nfs/nfs.h>
#include <nfs/nfsdiskless.h>
#include <nfs/krpc.h>
#include <nfs/xdr_subs.h>
X
#define MIN_REPLY_HDR 16	/* xid, dir, astat, errno */
X
/*
X * What is the longest we will wait before re-sending a request?
X * Note this is also the frequency of "RPC timeout" messages.
X * The re-send loop count sup linearly to this maximum, so the
X * first complaint will happen after (1+2+3+4+5)=15 seconds.
X */
#define	MAX_RESEND_DELAY 5	/* seconds */
X
/* Definitions from RFC951 */
struct bootp_packet {
X  u_int8_t op;
X  u_int8_t htype;
X  u_int8_t hlen;
X  u_int8_t hops;
X  u_int32_t xid;
X  u_int16_t secs;
X  u_int16_t flags;
X  struct in_addr ciaddr;
X  struct in_addr yiaddr;
X  struct in_addr siaddr;
X  struct in_addr giaddr;
X  unsigned char chaddr[16];
X  char sname[64];
X  char file[128];
X  unsigned char vend[256];
};
X
#define IPPORT_BOOTPC 68
#define IPPORT_BOOTPS 67
X
/* mountd RPC */
static int md_mount __P((struct sockaddr_in *mdsin, char *path,
X	u_char *fh));
X
void bootpboot_p_sa(sa)
X     struct sockaddr *sa;
{
X  if (!sa) {
X    printf("(sockaddr *) <null>");
X    return;
X  }
X  switch (sa->sa_family) {
X  case AF_INET:
X    {
X      struct sockaddr_in *sin = (struct sockaddr_in *) sa;
X      printf("inet %x",ntohl(sin->sin_addr.s_addr));
X    }
X    break;
X  default:
X    printf("af%d",sa->sa_family);
X  }
}
X
void bootpboot_p_ma(ma)
X     struct sockaddr *ma;
{
X  if (!ma) {
X    printf("<null>");
X    return;
X  }
X  printf("%x",*(int*)ma);
}
X
void bootpboot_p_rtentry(rt)
X     struct rtentry *rt;
{
X  bootpboot_p_sa(rt_key(rt));
X  printf(" ");
X  bootpboot_p_ma(rt->rt_genmask);
X  printf(" ");
X  bootpboot_p_sa(rt->rt_gateway);
X  printf(" ");
X  printf("%x",(unsigned short) rt->rt_flags);
X  printf(" %s%d\n",rt->rt_ifp->if_name,rt->rt_ifp->if_unit);
}
void  bootpboot_p_tree(rn)
X     struct radix_node *rn;
{
X  while (rn) {
X    if (rn->rn_b < 0) {
X      if (rn->rn_flags & RNF_ROOT) {
X      } else {
X	bootpboot_p_rtentry(rn);
X      }
X      rn = rn->rn_dupedkey;
X    } else {
X      bootpboot_p_tree(rn->rn_l);
X      bootpboot_p_tree(rn->rn_r);
X      return;
X    }
X    
X  }
}
X
void bootpboot_p_rtlist(void)
{
X  printf("Routing table:\n");
X  bootpboot_p_tree(rt_tables[AF_INET]->rnh_treetop);
}
X
void bootpboot_p_iflist(void)
{
X  struct ifnet *ifp;
X  struct ifaddr *ifa;
X  printf("Interface list:\n");
X  for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
X    {
X      for (ifa = ifp->if_addrlist.tqh_first ;ifa; 
X	   ifa=ifa->ifa_list.tqe_next)
X	if (ifa->ifa_addr->sa_family == AF_INET ) {
X	  printf("%s%d flags %x, addr %x, bcast %x, net %x\n",
X		 ifp->if_name,ifp->if_unit,
X		 (unsigned short) ifp->if_flags,
X		 ntohl(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr),
X		 ntohl(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr.s_addr),
X		 ntohl(((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr)
X		 );
X	}
X    }
}
X
int
bootpc_call(call,reply,procp)
X     struct bootp_packet *call;
X     struct bootp_packet *reply;	/* output */
X     struct proc *procp;
{
X	struct socket *so;
X	struct sockaddr_in *sin,sa;
X	struct mbuf *m, *nam;
X	struct uio auio;
X	struct iovec aio;
X	int error, rcvflg, timo, secs, len;
X	u_int tport;
X
X	/* Free at end if not null. */
X	nam = NULL;
X
X	/*
X	 * Create socket and set its recieve timeout.
X	 */
X	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
X		goto out;
X
X	m = m_get(M_WAIT, MT_SOOPTS);
X	if (m == NULL) {
X		error = ENOBUFS;
X		goto out;
X	} else {
X		struct timeval *tv;
X		tv = mtod(m, struct timeval *);
X		m->m_len = sizeof(*tv);
X		tv->tv_sec = 1;
X		tv->tv_usec = 0;
X		if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
X			goto out;
X	}
X
X	/*
X	 * Enable broadcast.
X	 */
X	{
X		int *on;
X		m = m_get(M_WAIT, MT_SOOPTS);
X		if (m == NULL) {
X			error = ENOBUFS;
X			goto out;
X		}
X		on = mtod(m, int *);
X		m->m_len = sizeof(*on);
X		*on = 1;
X		if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
X			goto out;
X	}
X
X	/*
X	 * Bind the local endpoint to a bootp client port.
X	 */
X	m = m_getclr(M_WAIT, MT_SONAME);
X	sin = mtod(m, struct sockaddr_in *);
X	sin->sin_len = m->m_len = sizeof(*sin);
X	sin->sin_family = AF_INET;
X	sin->sin_addr.s_addr = INADDR_ANY;
X	sin->sin_port = htons(IPPORT_BOOTPC);
X	error = sobind(so, m);
X	m_freem(m);
X	if (error) {
X		printf("bind failed\n");
X		goto out;
X	}
X
X	/*
X	 * Setup socket address for the server.
X	 */
X	nam = m_get(M_WAIT, MT_SONAME);
X	if (nam == NULL) {
X		error = ENOBUFS;
X		goto out;
X	}
X	sin = mtod(nam, struct sockaddr_in *);
X	sin-> sin_len = sizeof(*sin);
X	sin-> sin_family = AF_INET;
X	sin->sin_addr.s_addr = INADDR_BROADCAST;
X	sin->sin_port = htons(IPPORT_BOOTPS);
X
X	nam->m_len = sizeof(*sin);
X
X	/*
X	 * Send it, repeatedly, until a reply is received,
X	 * but delay each re-send by an increasing amount.
X	 * If the delay hits the maximum, start complaining.
X	 */
X	timo = 0;
X	for (;;) {
X		/* Send RPC request (or re-send). */
X		
X		aio.iov_base = (caddr_t) call;
X		aio.iov_len = sizeof(*call);
X		
X		auio.uio_iov = &aio;
X		auio.uio_iovcnt = 1;
X		auio.uio_segflg = UIO_SYSSPACE;
X		auio.uio_rw = UIO_WRITE;
X		auio.uio_offset = 0;
X		auio.uio_resid = sizeof(*call);
X		auio.uio_procp = procp;
X
X		error = sosend(so, nam, &auio, NULL, NULL, 0);
X		if (error) {
X			printf("bootpc_call: sosend: %d\n", error);
X			goto out;
X		}
X
X		/* Determine new timeout. */
X		if (timo < MAX_RESEND_DELAY)
X			timo++;
X		else
X			printf("RPC timeout for server 0x%x\n",
X			       ntohl(sin->sin_addr.s_addr));
X
X		/*
X		 * Wait for up to timo seconds for a reply.
X		 * The socket receive timeout was set to 1 second.
X		 */
X		secs = timo;
X		while (secs > 0) {
X			aio.iov_base = (caddr_t) reply;
X			aio.iov_len = sizeof(*reply);
X
X			auio.uio_iov = &aio;
X			auio.uio_iovcnt = 1;
X			auio.uio_segflg = UIO_SYSSPACE;
X			auio.uio_rw = UIO_READ;
X			auio.uio_offset = 0;
X			auio.uio_resid = sizeof(*reply);
X			auio.uio_procp = procp;
X			
X			rcvflg = 0;
X			error = soreceive(so, NULL, &auio, NULL, NULL, &rcvflg);
X			if (error == EWOULDBLOCK) {
X				secs--;
X				call->secs=htons(ntohs(call->secs)+1);
X				continue;
X			}
X			if (error)
X				goto out;
X			len = sizeof(*reply) - auio.uio_resid;
X
X			/* Does the reply contain at least a header? */
X			if (len < MIN_REPLY_HDR)
X				continue;
X
X			/* Is it the right reply? */
X			if (reply->op != 2)
X			  continue;
X
X			if (reply->xid != call->xid)
X				continue;
X
X			if (reply->hlen != call->hlen)
X			  continue;
X
X			if (bcmp(reply->chaddr,call->chaddr,call->hlen))
X			  continue;
X
X			goto gotreply;	/* break two levels */
X
X		} /* while secs */
X	} /* forever send/receive */
X
X	error = ETIMEDOUT;
X	goto out;
X
X gotreply:
X out:
X	if (nam) m_freem(nam);
X	soclose(so);
X	return error;
}
X
int 
bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so,
X			struct proc *procp)
{
X  struct sockaddr_in *sin;
X  int error;
X  struct sockaddr_in dst;
X  struct sockaddr_in gw;
X  struct sockaddr_in mask;
X
X  /*
X   * Bring up the interface.
X   *
X   * Get the old interface flags and or IFF_UP into them; if
X   * IFF_UP set blindly, interface selection can be clobbered.
X   */
X  error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_fakeup_interface: GIFFLAGS, error=%d", error);
X  ireq->ifr_flags |= IFF_UP;
X  error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_fakeup_interface: SIFFLAGS, error=%d", error);
X
X  /*
X   * Do enough of ifconfig(8) so that the chosen interface
X   * can talk to the servers.  (just set the address)
X   */
X  
X  /* addr is 0.0.0.0 */
X  
X  sin = (struct sockaddr_in *)&ireq->ifr_addr;
X  bzero((caddr_t)sin, sizeof(*sin));
X  sin->sin_len = sizeof(*sin);
X  sin->sin_family = AF_INET;
X  sin->sin_addr.s_addr = INADDR_ANY;
X  error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_fakeup_interface: set if addr, error=%d", error);
X  
X  /* netmask is 0.0.0.0 */
X  
X  sin = (struct sockaddr_in *)&ireq->ifr_addr;
X  bzero((caddr_t)sin, sizeof(*sin));
X  sin->sin_len = sizeof(*sin);
X  sin->sin_family = AF_INET;
X  sin->sin_addr.s_addr = INADDR_ANY;
X  error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_fakeup_interface: set if net addr, error=%d", error);
X  
X  /* Broadcast is 255.255.255.255 */
X  
X  sin = (struct sockaddr_in *)&ireq->ifr_addr;
X  bzero((caddr_t)sin, sizeof(*sin));
X  sin->sin_len = sizeof(*sin);
X  sin->sin_family = AF_INET;
X  sin->sin_addr.s_addr = INADDR_BROADCAST;
X  error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_fakeup_interface: set if broadcast addr, error=%d", error);
X  
X  
X  /* Add default route to 0.0.0.0 so we can send data */
X  
X  bzero((caddr_t) &dst, sizeof(dst));
X  dst.sin_len=sizeof(dst);
X  dst.sin_family=AF_INET;
X  dst.sin_addr.s_addr = htonl(0);
X  
X  bzero((caddr_t) &gw, sizeof(gw));
X  gw.sin_len=sizeof(gw);
X  gw.sin_family=AF_INET;
X  gw.sin_addr.s_addr = htonl(0x0);
X  
X  bzero((caddr_t) &mask, sizeof(mask));
X  mask.sin_len=sizeof(mask);
X  mask.sin_family=AF_INET;
X  mask.sin_addr.s_addr = htonl(0);
X  
X  error = rtrequest(RTM_ADD, 
X		    (struct sockaddr *) &dst, 
X		    (struct sockaddr *)&gw,
X		    (struct sockaddr *) &mask, 
X		    RTF_UP | RTF_STATIC
X		    , NULL);
X  if (error)
X    printf("bootpc_fakeup_interface: add default route, error=%d\n", error);
X  return error;
}
X
int 
bootpc_adjust_interface(struct ifreq *ireq,struct socket *so,
X			struct sockaddr_in *myaddr,
X			struct sockaddr_in *netmask,
X			struct sockaddr_in *gw,
X			struct proc *procp)
{
X  int error;
X  struct sockaddr_in oldgw;
X  struct sockaddr_in olddst;
X  struct sockaddr_in oldmask;
X  struct sockaddr_in *sin;
X
X  /* Remove old default route to 0.0.0.0 */
X  
X  bzero((caddr_t) &olddst, sizeof(olddst));
X  olddst.sin_len=sizeof(olddst);
X  olddst.sin_family=AF_INET;
X  olddst.sin_addr.s_addr = INADDR_ANY;
X  
X  bzero((caddr_t) &oldgw, sizeof(oldgw));
X  oldgw.sin_len=sizeof(oldgw);
X  oldgw.sin_family=AF_INET;
X  oldgw.sin_addr.s_addr = INADDR_ANY;
X  
X  bzero((caddr_t) &oldmask, sizeof(oldmask));
X  oldmask.sin_len=sizeof(oldmask);
X  oldmask.sin_family=AF_INET;
X  oldmask.sin_addr.s_addr = INADDR_ANY;
X  
X  error = rtrequest(RTM_DELETE, 
X		    (struct sockaddr *) &olddst, 
X		    (struct sockaddr *)&oldgw,
X		    (struct sockaddr *) &oldmask, 
X		    (RTF_UP | RTF_STATIC), NULL);
X  if (error) {
X    printf("nfs_boot: del default route, error=%d\n", error);
X    return error;
X  }
X
X  olddst.sin_addr.s_addr = INADDR_BROADCAST;
X  
X  error = rtrequest(RTM_DELETE, 
X		    (struct sockaddr *) &olddst, 
X		    (struct sockaddr *)&oldgw,
X		    (struct sockaddr *) &oldmask, 
X		    (RTF_UP | RTF_HOST | RTF_STATIC), NULL);
X  if (error) {
X    printf("nfs_boot: del broadcast route, error=%d\n", error);
X    return error;
X  }
X
X  /*
X   * Do enough of ifconfig(8) so that the chosen interface
X   * can talk to the servers.  (just set the address)
X   */
X  bcopy(netmask,&ireq->ifr_addr,sizeof(*netmask));
X  error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp);
X  if (error)
X    panic("nfs_boot: set if netmask, error=%d", error);
X
X  /* Broadcast is with host part of IP address all 1's */
X  
X  sin = (struct sockaddr_in *)&ireq->ifr_addr;
X  bzero((caddr_t)sin, sizeof(*sin));
X  sin->sin_len = sizeof(*sin);
X  sin->sin_family = AF_INET;
X  sin->sin_addr.s_addr = myaddr->sin_addr.s_addr | ~ netmask->sin_addr.s_addr;
X  error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp);
X  if (error)
X    panic("bootpc_call: set if broadcast addr, error=%d", error);
X  
X  bcopy(myaddr,&ireq->ifr_addr,sizeof(*myaddr));
X  error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp);
X  if (error)
X    panic("nfs_boot: set if addr, error=%d", error);
X
X  /* Add new default route */
X
X  error = rtrequest(RTM_ADD, 
X		    (struct sockaddr *) &olddst,
X		    (struct sockaddr *) gw,
X		    (struct sockaddr *) &oldmask,
X		    (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
X  if (error) {
X    printf("nfs_boot: add net route, error=%d\n", error);
X    return error;
X  }
}
X
X
extern char	*nfsbootdevname;
X
int
bootpc_init(struct nfs_diskless *nd,struct proc *procp)
{
X  struct bootp_packet call;
X  struct bootp_packet reply;
X  static u_int32_t xid = ~0xFF;
X  
X  struct ifreq ireq;
X  struct ifnet *ifp;
X  struct socket *so;
X  int error;
X  int code,len;
X  int i,j;
X  char rootpath[65];
X  char swappath[65];
X
X  struct sockaddr_in myaddr;
X  struct sockaddr_in netmask;
X  struct sockaddr_in gw;
X  struct sockaddr_in server;
X  int gotgw=0;
X  int gotnetmask=0;
X  int gotserver=0;
X  int gotrootpath=0;
X  int gotswappath=0;
X
#define EALEN 6
X  unsigned char ea[EALEN];
X  struct ifaddr *ifa;
X  struct sockaddr_dl *sdl = NULL;
X  char *delim;
X  
X  
X  /*
X   * Find a network interface.
X   */
X  if (nfsbootdevname)
X    ifp = ifunit(nfsbootdevname);
X  else
X    for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
X      if ((ifp->if_flags &
X	   (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
X	break;
X  if (ifp == NULL)
X    panic("bootpc_init: no suitable interface");
X  sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
X  printf("bootpc_init: using network interface '%s'\n",
X	 ireq.ifr_name);
X
X  if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
X    panic("nfs_boot: socreate, error=%d", error);
X
X	  
X  bootpc_fakeup_interface(&ireq,so,procp);
X
X  printf("Bootpc testing starting\n");
X  
X  /* Get HW address */
X  
X  for (ifa = ifp->if_addrlist.tqh_first ;ifa; 
X       ifa=ifa->ifa_list.tqe_next)
X    if (ifa->ifa_addr->sa_family == AF_LINK &&
X	(sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) &&
X	sdl->sdl_type == IFT_ETHER)
X      break;
X  
X  if (!sdl)
X    panic("bootpc: Unable to find HW address");
X  if (sdl->sdl_alen != EALEN ) 
X    panic("bootpc: HW address len is %d, expected value is %d",
X	  sdl->sdl_alen,EALEN);
#if 1
X  printf("bootpc hw address is ");
X  delim="";
X  for (j=0;j<sdl->sdl_alen;j++) {
X    printf("%s%x",delim,((unsigned char *)LLADDR(sdl))[j]);
X    delim=":";
X  }
X  printf("\n");
#endif
X
#if 0  
X  bootpboot_p_iflist();
X  bootpboot_p_rtlist();
#endif
X  
X  bzero((caddr_t) &call, sizeof(call));
X
X  /* bootpc part */
X  call.op = 1; 			/* BOOTREQUEST */
X  call.htype= 1;		/* 10mb ethernet */
X  call.hlen=sdl->sdl_alen;	/* Hardware address length */
X  call.hops=0;	
X  xid++;
X  call.xid = txdr_unsigned(xid);
X  bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen);
X  
X  call.vend[0]=99;
X  call.vend[1]=130;
X  call.vend[2]=83;
X  call.vend[3]=99;
X  call.vend[4]=255;
X  
X  call.secs = 0;
X  call.flags = htons(0x8000); /* We need an broadcast answer */
X  
X  error = bootpc_call(&call,&reply,procp);
X  
X  if (error) return error;
X  
X  bzero(&myaddr,sizeof(myaddr));
X  bzero(&netmask,sizeof(netmask));
X  bzero(&gw,sizeof(gw));
X  bzero(&server,sizeof(server));
X
X  myaddr.sin_len = sizeof(myaddr);
X  myaddr.sin_family = AF_INET;
X
X  netmask.sin_len = sizeof(netmask);
X  netmask.sin_family = AF_INET;
X
X  gw.sin_len = sizeof(gw);
X  gw.sin_family= AF_INET;
X
X  server.sin_len = sizeof(gw);
X  server.sin_family= AF_INET;
X
X  printf("My new ip address is %x\n",htonl(reply.yiaddr.s_addr));
X
X  myaddr.sin_addr = reply.yiaddr;
X
X  printf("Server ip address is %x\n",htonl(reply.siaddr.s_addr));
X  printf("Gateway ip address is %x\n",htonl(reply.giaddr.s_addr));
X
X  gw.sin_addr = reply.giaddr;
X
X  if (reply.sname[0])
X    printf("Server name is %s\n",reply.sname);
X  if (reply.file[0])
X    printf("boot file is %s\n",reply.file);
X  if (reply.vend[0]==99 && reply.vend[1]==130 &&
X      reply.vend[2]==83 && reply.vend[3]==99) {
X    j=4;
X    while (j<sizeof(reply.vend)) {
X      code = reply.vend[j];
X      if (reply.vend[j]==255)
X	break;
X      if (reply.vend[j]==0) {
X	j++;
X	continue;
X      }
X      len = reply.vend[j+1];
X      j+=2;
X      if (len+j>=sizeof(reply.vend)) {
X	printf("Truncated field");
X	break;
X      }
X      switch (code) {
X      case 1:
X	if (len!=4) 
X	  panic("bootpc: subnet mask len is %d",len);
X	bcopy(&reply.vend[j],&netmask.sin_addr,4);
X	gotnetmask=1;
X	printf("Subnet mask is %d.%d.%d.%d\n",
X	       reply.vend[j],
X	       reply.vend[j+1],
X	       reply.vend[j+2],
X	       reply.vend[j+3]);
X	break;
X      case 2:
X	/* Time offset */
X	break;
X      case 3:
X	/* Routers */
X	if (len % 4) 
X	  panic("bootpc: Router Len is %d",len);
X	if (len > 0) {
X	  bcopy(&reply.vend[j],&gw.sin_addr,4);
X	  gotgw=1;
X	}
X	for (i=0;i<len;i+=4) {
X	  printf("Router is %d.%d.%d.%d\n",
X		 reply.vend[j+i],
X		 reply.vend[j+i+1],
X		 reply.vend[j+i+2],
X		 reply.vend[j+i+3]);
X	}
X	break;
X      case 6:
X	/* Domain Name servers */
X	if (len % 4) 
X	  panic("bootpc: DNS Len is %d",len);
X	for (i=0;i<len;i+=4) {
X	  printf("DNS server is %d.%d.%d.%d\n",
X		 reply.vend[j+i],
X		 reply.vend[j+i+1],
X		 reply.vend[j+i+2],
X		 reply.vend[j+i+3]);
X	}
X	break;
X      case 16:
X	if (len!=4)
X	  panic("bootpc: swap server len is %d",len);
X	bcopy(&reply.vend[j],&server.sin_addr,4);
X	gotserver=1;
X	printf("Swap server (also used as root path server) is %d.%d.%d.%d\n",
X	       reply.vend[j],
X	       reply.vend[j+1],
X	       reply.vend[j+2],
X	       reply.vend[j+3]);
X	break;
X      case 17:
X	if (len>=sizeof(rootpath))
X	  panic("bootpc: rootpath >=%d bytes",sizeof(rootpath));
X	strncpy(rootpath,&reply.vend[j],len);
X	rootpath[len]=0;
X	gotrootpath=1;
X	printf("Rootpath is %s\n",rootpath);
X	break;
X      case 12:
X	if (len>MAXHOSTNAMELEN)
X	  panic("bootpc: hostname  >=%d bytes",MAXHOSTNAMELEN);
X	strncpy(hostname,&reply.vend[j],len);
X	hostname[len]=0;
X	hostnamelen= len;
X	printf("Hostname is %s\n",hostname);
X	break;
X      case 128:
X	if (len>64)
X	  panic("bootpc: swappath >=%d bytes",sizeof(swappath));
X	strncpy(swappath,&reply.vend[j],len);
X	hostname[len]=0;
X	gotswappath=1;
X	printf("Swappath is %s\n",swappath);
X	break;
X      default:
X	printf("Ignoring field type %d\n",code);
X      }
X      j+=len;
X    }
X  }
X
X
X  if (!gotrootpath)
X    panic("bootpc: No root path offered");
X  if (!gotswappath)
X    panic("bootpc: No swap path offered");
X  if (!gotserver) {
X    server.sin_addr = reply.siaddr ;
X  }
X    
X  if (!gotnetmask) {
X    if (IN_CLASSA(myaddr.sin_addr.s_addr))
X      netmask.sin_addr.s_addr = IN_CLASSA_NET;
X    else if (IN_CLASSB(myaddr.sin_addr.s_addr))
X      netmask.sin_addr.s_addr = IN_CLASSB_NET;
X    else 
X      netmask.sin_addr.s_addr = IN_CLASSC_NET;
X  }
X  if (!gotgw) {
X    /* Use proxyarp */
X    gw.sin_addr.s_addr = myaddr.sin_addr.s_addr;
X  }
X  
X  error = bootpc_adjust_interface(&ireq,so,
X				  &myaddr,&netmask,&gw,procp);
X  
X  soclose(so);
X
#if 0
X  bootpboot_p_iflist();
X  bootpboot_p_rtlist();
#endif
X
X  if (server.sin_addr.s_addr != reply.siaddr.s_addr ) {
X    sprintf(nd->nd_root.ndm_host,"%d.%d.%d.%d",
X	    ((unsigned char *) &server.sin_addr)[0],
X	    ((unsigned char *) &server.sin_addr)[1],
X	    ((unsigned char *) &server.sin_addr)[2],
X	    ((unsigned char *) &server.sin_addr)[3]);
X  } else 
X    strcpy(nd->nd_root.ndm_host,reply.sname);
X
X  strcpy(nd->nd_swap.ndm_host, nd->nd_root.ndm_host);
X
X  bcopy(&server, &nd->nd_root.ndm_saddr,sizeof(server));
X
X  bcopy(&server, &nd->nd_swap.ndm_saddr,sizeof(server));
X
X  error = md_mount(&nd->nd_root.ndm_saddr, rootpath, nd->nd_root.ndm_fh);
X  if (error)
X    panic("nfs_boot: mountd root, error=%d", error);
X
X  error = md_mount(&nd->nd_swap.ndm_saddr, swappath, nd->nd_swap.ndm_fh);
X  if (error)
X    panic("nfs_boot: mountd swap, error=%d", error);
X
X  return error;
}
X
/*
X * RPC: mountd/mount
X * Given a server pathname, get an NFS file handle.
X * Also, sets sin->sin_port to the NFS service port.
X */
static int
md_mount(mdsin, path, fhp)
X	struct sockaddr_in *mdsin;		/* mountd server address */
X	char *path;
X	u_char *fhp;
{
X	/* The RPC structures */
X	struct rdata {
X		u_int32_t	errno;
X		u_char	fh[NFS_FHSIZE];
X	} *rdata;
X	struct mbuf *m;
X	int error;
X
X	/* Get port number for MOUNTD. */
X	error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
X						 &mdsin->sin_port);
X	if (error) return error;
X
X	m = xdr_string_encode(path, strlen(path));
X
X	/* Do RPC to mountd. */
X	error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
X			RPCMNT_MOUNT, &m, NULL);
X	if (error)
X		return error;	/* message already freed */
X
X	if (m->m_len < sizeof(*rdata)) {
X		m = m_pullup(m, sizeof(*rdata));
X		if (m == NULL)
X			goto bad;
X	}
X	rdata = mtod(m, struct rdata *);
X	error = fxdr_unsigned(u_int32_t, rdata->errno);
X	if (error)
X		goto bad;
X	bcopy(rdata->fh, fhp, NFS_FHSIZE);
X
X	/* Set port number for NFS use. */
X	error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
X						 &mdsin->sin_port);
X	goto out;
X
bad:
X	error = EBADRPC;
X
out:
X	m_freem(m);
X	return error;
}
SHAR_EOF
chmod 0644 bootp_subr.c ||
echo 'restore of bootp_subr.c failed'
Wc_c="`wc -c < 'bootp_subr.c'`"
test 22839 -eq "$Wc_c" ||
	echo 'bootp_subr.c: original size 22839, current size' "$Wc_c"
fi
# ============= patch.files ==============
if test -f 'patch.files' -a X"$1" != X"-c"; then
	echo 'x - skipping patch.files (File already exists)'
else
echo 'x - extracting patch.files (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'patch.files' &&
*** /usr/src/sys/conf/files.orig	Sun Sep  3 13:15:58 1995
--- usr/src/sys/conf/files	Sun Sep  3 13:17:57 1995
***************
*** 272,277 ****
--- 272,278 ----
X  file netns/spp_debug.c			ns
X  file netns/spp_usrreq.c			ns
X  file nfs/krpc_subr.c			nfsclient
+ file nfs/bootp_subr.c			nfsclient bootp
X  file nfs/nfs_bio.c			nfsclient
X  file nfs/nfs_boot.c			nfsclient
X  file nfs/nfs_node.c			nfsclient
SHAR_EOF
chmod 0644 patch.files ||
echo 'restore of patch.files failed'
Wc_c="`wc -c < 'patch.files'`"
test 399 -eq "$Wc_c" ||
	echo 'patch.files: original size 399, current size' "$Wc_c"
fi
# ============= patch.ip_input.c ==============
if test -f 'patch.ip_input.c' -a X"$1" != X"-c"; then
	echo 'x - skipping patch.ip_input.c (File already exists)'
else
echo 'x - extracting patch.ip_input.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'patch.ip_input.c' &&
*** /usr/src/sys/netinet/ip_input.c.orig	Sun Aug 13 09:13:59 1995
--- usr/src/sys/netinet/ip_input.c	Sun Sep  3 13:20:57 1995
***************
*** 230,237 ****
X  	 * Check our list of addresses, to see if the packet is for us.
X  	 */
X  	for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) {
! 		if (ip->ip_dst.s_addr == ia->ia_addr.sin_addr.s_addr)
! 			goto ours;
X  		if (
X  #ifdef	DIRECTED_BROADCAST
X  		    ia->ia_ifp == m->m_pkthdr.rcvif &&
--- 230,241 ----
X  	 * Check our list of addresses, to see if the packet is for us.
X  	 */
X  	for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) {
! 		if (ip->ip_dst.s_addr == ia->ia_addr.sin_addr.s_addr 
! #ifdef BOOTP_COMPAT
! 		    || ia->ia_addr.sin_addr.s_addr == INADDR_ANY
! #endif
! 	)
! 		  goto ours;
X  		if (
X  #ifdef	DIRECTED_BROADCAST
X  		    ia->ia_ifp == m->m_pkthdr.rcvif &&
SHAR_EOF
chmod 0644 patch.ip_input.c ||
echo 'restore of patch.ip_input.c failed'
Wc_c="`wc -c < 'patch.ip_input.c'`"
test 856 -eq "$Wc_c" ||
	echo 'patch.ip_input.c: original size 856, current size' "$Wc_c"
fi
# ============= patch.nfs_vfsops.c ==============
if test -f 'patch.nfs_vfsops.c' -a X"$1" != X"-c"; then
	echo 'x - skipping patch.nfs_vfsops.c (File already exists)'
else
echo 'x - extracting patch.nfs_vfsops.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'patch.nfs_vfsops.c' &&
*** /usr/src/sys/nfs/nfs_vfsops.c.orig	Sun Sep  3 13:19:43 1995
--- usr/src/sys/nfs/nfs_vfsops.c	Sun Sep  3 13:18:59 1995
***************
*** 191,197 ****
--- 191,201 ----
X  	 * Side effect:  Finds and configures a network interface.
X  	 */
X  	bzero((caddr_t) &nd, sizeof(nd));
+ #ifdef BOOTP
+ 	bootpc_init(&nd,procp);
+ #else
X  	nfs_boot_init(&nd, procp);
+ #endif
X  
X  	/*
X  	 * Create the root mount point.
SHAR_EOF
chmod 0644 patch.nfs_vfsops.c ||
echo 'restore of patch.nfs_vfsops.c failed'
Wc_c="`wc -c < 'patch.nfs_vfsops.c'`"
test 411 -eq "$Wc_c" ||
	echo 'patch.nfs_vfsops.c: original size 411, current size' "$Wc_c"
fi
exit 0

----Next_Part(Mon_Sep__4_01:07:54_1995)----