Source to bsd/netinet/in_bootp.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#if NEXT
/*
 * Copyright (c) 1988, by NeXT Inc.
 *
 **********************************************************************
 * HISTORY
 * Tue Mar 18 11:49:14 PST 1997 Dieter Siegmund (dieter) at NeXT
 *	Ported from 4.3 to 4.4Lite (Teflon)
 *
 * Mon Jan 10 15:13:33 PST 1994 Matt Watson (mwatson) at NeXT
 *	Allow "Sexy Net Init"(tm) while netbooting
 *	Function prototype cleanup
 *
 * 09-Apr-90  Bradley Taylor (btaylor) at NeXT
 *	Changes because of new network API (netbuf and netif)
 *
 * 25-May-89  Peter King (king) at NeXT
 *	Added support for "Sexy Net Init" -- NeXT vendor protocol 1.
 *
 * 15-Aug-88  Peter King (king) at NeXT, Inc.
 *	Created.
 **********************************************************************
 */ 

#if	m68k
#else

#import <machine/spl.h>
#import <machine/label_t.h>
#import <sys/param.h>
#import <sys/types.h>
#import <mach/boolean.h>
#import <sys/kernel.h>
#import <sys/errno.h>
#import <sys/file.h>
#import <sys/uio.h>
#import <sys/ioctl.h>
#import <sys/time.h>
#import <sys/mbuf.h>
#import <sys/vnode.h>
#import <sys/socket.h>
#import <sys/socketvar.h>
#import <net/if.h>
#import <net/route.h>
#import <netinet/in.h>
#import <netinet/in_systm.h>
//#import <netinet/in_pcb.h>
#import <netinet/if_ether.h>
#import <netinet/ip.h>
#import <netinet/ip_var.h>
#import <netinet/udp.h>
#import <netinet/udp_var.h>
#import <netinet/ip_icmp.h>
#import <netinet/bootp.h>
#import <dev/kmreg_com.h>

#define	CONSOLE	"/dev/console"

#define	BOOTP_DEBUG 0
#if	BOOTP_DEBUG
#define	dprintf(x) printf x;
#else	BOOTP_DEBUG
#define	dprintf(x)
#endif	BOOTP_DEBUG

static int in_bootp_initnet(volatile struct ifnet *ifp, struct ifreq *ifr,
	struct socket **sop);
			
static struct bootp_packet * in_bootp_buildpacket(volatile struct ifnet *ifp,
	struct sockaddr_in *sin, u_char my_enaddr[6]);

struct mbuf * in_bootp_bptombuf(struct bootp_packet *bp);
	
static void in_bootp_timeout(struct socket **socketflag);
	
static int in_bootp_promisctimeout(boolean_t *ignoreflag);
	
static int in_bootp_getpacket(struct socket *so, caddr_t pp, int psize);

static int in_bootp_sendrequest(volatile struct ifnet *ifp,
	struct socket *so, struct bootp_packet *bp_s, struct bootp *bp_r,
	u_char my_enaddr[6], struct vnode **console_vpp);

static int in_bootp_openconsole(struct vnode **vpp);

static void in_bootp_closeconsole(struct vnode *vp);

static int in_bootp_processreply(struct vnode *vp, struct bootp_packet *bp_s,
	struct bootp *bp_r);

static int in_bootp_setaddress(volatile struct ifnet *ifp, struct ifreq *ifr,
	struct socket *so, struct in_addr *addr);

static struct ifreq in_bootp_makeifreq(volatile struct ifnet *ifp,
	struct sockaddr_in *sin);

static void kmgets(char *cp, char *lp, boolean_t silent);
	
static boolean_t alertShowing = FALSE;

/*
 * Routine: in_bootp
 * Function:
 *	Use the BOOTP protocol to resolve what our IP address should be
 *	on a particular interface.
 * Note:
 *	The ifreq structure returns the newly configured address.  If there
 *	is an error, it can be full of bogus information.
 *
 *	This is called from driver level ioctl routines to assure that
 *	the interface is a 10Meg Ethernet.
 */
int
in_bootp(
	 volatile struct ifnet	*ifp,
	 struct sockaddr_in *sin,
	 u_char my_enaddr[6]
	 )
{
	struct mbuf		*m;
	struct socket		*so;
	register int		error;
	struct bootp_packet	*bp_s = NULL;
	struct bootp		*bp_r = NULL;
	struct vnode		*vp = NULL;
	struct nextvend		*nv_s;
	struct nextvend		*nv_r;
	struct ifreq		ifr;
	struct proc *		procp = current_proc();	/* XXX */

	dprintf(("in_bootp() entered\n"));

	ifr = in_bootp_makeifreq(ifp, sin);
	if (error = in_bootp_initnet(ifp, &ifr, &so)) {
		if (error == -1) {
			/*
			 * This means the interface was already
			 * autoconfigured.  Reset the address to make
			 * sure the netmask is right.
			 */
			error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ifr,
					procp);
			if (error == 0) {
				bcopy(&ifr.ifr_addr, sin, sizeof(*sin));
			}
			soclose(so);
			return (error);
		}
		dprintf(("in_bootp() in_bootp_initnet failed: %d\n", error));
		goto errout;
	}

	/* Build a packet */
	bp_s = in_bootp_buildpacket(ifp, 
				    sin,
				    my_enaddr);
	nv_s = (struct nextvend *)&bp_s->bp_bootp.bp_vend;

	/* Set up place to receive BOOTP response */
	bp_r = (struct bootp *)kalloc(sizeof (*bp_r));
	nv_r = (struct nextvend *)&bp_r->bp_vend;

	while (TRUE) {
		/* Send the request */
	       dprintf(("in_bootp() trying to send request\n"));
		if (error = in_bootp_sendrequest(ifp, so, bp_s, bp_r, 
						 my_enaddr, &vp)) {
			goto errout;
		}
		if (nv_r->nv_opcode == BPOP_OK) {
			break;
		}
	       dprintf(("in_bootp() trying to open console\n"));
		if (vp == NULL && (error = in_bootp_openconsole(&vp))) {
			goto errout;
		}
	       dprintf(("in_bootp() trying to process reply\n"));
		if (error = in_bootp_processreply(vp, bp_s, bp_r)) {
			goto errout;
		}
	}

	/* It's for us. */
	dprintf(("in_bootp() got a response!\n"));
	if (vp) {
		(void) in_bootp_closeconsole(vp);
		vp = NULL;
	}

	error = in_bootp_setaddress(ifp, &ifr, so,
				    (struct in_addr *)&bp_r->bp_yiaddr);
	if (error == 0) {
		bcopy(&ifr.ifr_addr, sin, sizeof(*sin));
	}

errout:	
	if (error && so) {
		/* Make sure the interface is shut down */
		(void) ifioctl(so, SIOCDIFADDR, (caddr_t) &ifr, procp);
		ifr.ifr_flags = ifp->if_flags & ~(IFF_UP);
		(void) ifioctl(so, SIOCSIFFLAGS, (caddr_t) &ifr, procp);
	}
	ifp->if_eflags &= ~(IFEF_AUTOCONF);
	if (so) {
		soclose(so);
	}
	if (bp_s) {
		kfree((caddr_t)bp_s, sizeof (*bp_s));
	}
	if (bp_r) {
		kfree((caddr_t)bp_r, sizeof (*bp_r));
	}
	if (!error) {
		(void) in_bootp_closeconsole(vp);
	}
	dprintf(("in_bootp() returning: %d\n", error));

	return (error);
}

/*
 * Routine: in_bootp_initnet
 * Function:
 *	Set up the interface to handle BOOTP packets.  Return the interface
 *	address in ifr.
 * Returns:
 *	-1:	interface previously autoconfigured
 *	0:	success
 *	>0:	unix error 
 */
int
in_bootp_initnet(ifp, ifr, sop)
	register volatile struct ifnet	*ifp;
	register struct ifreq		*ifr;
	struct socket			**sop;
{
	struct sockaddr_in	lsin;
	struct sockaddr_in	*sin;
	struct mbuf		*m;
	int	error;
	struct proc *		procp = current_proc();	/* XXX */
	
	dprintf(("  in_bootp_initnet() entered\n"));

	/*
	 * Get a socket (wouldn't it be nice to make system calls from
	 * within the kernel?)
	 */
	dprintf(("  in_bootp_initnet() socreate..."));
	if (error = socreate(AF_INET, sop, SOCK_DGRAM, 0)) {
		*sop = NULL;
		dprintf(("failed: %d\n", error));
		return (error);
	}
	dprintf(("ok\n"));

	/*
	 * Bring the interface up if neccessary.
	 */
	if ((ifp->if_flags & IFF_UP) == 0) {
		/* Turn off trailers here, otherwise the server ARP reply
		   will cause the booting client the issue its own ARP
		   reply enabling trailers.
		*/
		ifr->ifr_flags = ifp->if_flags | IFF_UP | IFF_NOTRAILERS;
		dprintf(("  in_bootp_initnet() SIOCSIFFLAGS..."));
		if (error = ifioctl(*sop, SIOCSIFFLAGS, (caddr_t)ifr, procp)) {
			dprintf(("failed: %d\n", error));
			return (error);
		}
		dprintf(("ok\n"));
	} 
	else {
		if (ifp->if_eflags & IFEF_AUTOCONF_DONE) {
		/* If the interface was previously autoconfigured, return */
			dprintf(("  in_bootp_initnet() already done:"));
			if ((error = ifioctl(*sop, SIOCGIFADDR,
					     (caddr_t)ifr, procp))) {
				dprintf((" %d\n", error));
				return (error);
			}
			ifp->if_eflags &= ~(IFEF_AUTOCONF);
			dprintf((" -1\n"));
			return(-1);
		    }
	}

	/*
	 * Assign the all-zeroes address to the interface.
	 */
	bzero((caddr_t)&lsin, sizeof(lsin));
	lsin.sin_family = AF_INET;
	ifr->ifr_addr = *(struct sockaddr *)&lsin;

	dprintf(("  in_bootp_initnet() SIOCSIFADDR..."));
	if (error = ifioctl(*sop, SIOCSIFADDR, (caddr_t)ifr, procp)) {
		dprintf(("failed: %d\n",
			 error));
		return (error);
	}
	dprintf(("ok\n"));

	/*
	 * Now set up the socket to receive the BOOTP response, i.e. bind
	 * a name to it.
	 */
	dprintf(("  in_bootp_initnet() m_get..."));
	m = m_get(M_WAIT, MT_SONAME);
	if (m == NULL) {
		error = ENOBUFS;
		dprintf(("failed: %d\n", error));
		return (error);
	}
	dprintf(("ok\n"));

	m->m_len = sizeof (struct sockaddr_in);
	sin = mtod(m, struct sockaddr_in *);
	sin->sin_family = AF_INET;
	sin->sin_port = htons(IPPORT_BOOTPC);
	sin->sin_addr.s_addr = INADDR_ANY;

	dprintf(("  in_bootp_initnet() sobind..."));
	error = sobind(*sop, m);
	m_freem(m);
	if (error) {
		dprintf(("failed: %d\n", error));
		return (error);
	}
	dprintf(("ok\n"));

	/* We will be doing non-blocking IO on this socket */
	(*sop)->so_state |= SS_NBIO;
	
	dprintf(("  in_bootp_initnet() exiting normally\n"));
	return (0);
}

/*
 * Routine: in_bootp_buildpacket
 * Function:
 *	Put together a BOOTP request packet.  RFC951.
 */
struct bootp_packet *
in_bootp_buildpacket(
		     volatile struct ifnet *ifp,	/* Interface */
		     struct sockaddr_in	*sin, 		/* Local IP address */
		     u_char my_enaddr[6]		/* Local EN address */
		     )
{
	struct bootp_packet	*bp_s;
	struct nextvend		*nv;

	dprintf(("  in_bootp_buildpacket() entered\n"));

	bp_s = (struct bootp_packet *)kalloc(sizeof (*bp_s));
	nv = (struct nextvend *)&bp_s->bp_bootp.bp_vend;

	bzero(bp_s, sizeof (*bp_s));
	bp_s->bp_ip.ip_v = IPVERSION;
	bp_s->bp_ip.ip_hl = sizeof (struct ip) >> 2;
	bp_s->bp_ip.ip_id = htons(ip_id++);
	bp_s->bp_ip.ip_ttl = MAXTTL;
	bp_s->bp_ip.ip_p = IPPROTO_UDP;
	bp_s->bp_ip.ip_src.s_addr = sin->sin_addr.s_addr;
	bp_s->bp_ip.ip_dst.s_addr = INADDR_BROADCAST;
	bp_s->bp_udp.uh_sport = htons(IPPORT_BOOTPC);
	bp_s->bp_udp.uh_dport = htons(IPPORT_BOOTPS);
	bp_s->bp_udp.uh_sum = 0;
	bp_s->bp_bootp.bp_op = BOOTREQUEST;
	bp_s->bp_bootp.bp_htype = ARPHRD_ETHER;
	bp_s->bp_bootp.bp_hlen = 6;
	bp_s->bp_bootp.bp_ciaddr.s_addr = 0;
	bcopy((caddr_t)my_enaddr, bp_s->bp_bootp.bp_chaddr, 6);
	bcopy(VM_NEXT, &nv->nv_magic, 4);

	nv->nv_version = 1;
	nv->nv_opcode = BPOP_OK;
	bp_s->bp_udp.uh_ulen = htons(sizeof (bp_s->bp_udp) +
				     sizeof (bp_s->bp_bootp));
	bp_s->bp_ip.ip_len = htons(sizeof (struct ip) +
				   ntohs(bp_s->bp_udp.uh_ulen));
	bp_s->bp_ip.ip_sum = 0;

	dprintf(("  in_bootp_buildpacket() exiting\n"));

	return (bp_s);
}

/*
 * Routine: in_bootp_bptombuf
 * Function:
 *	Put the outgoing BOOTP packet into an mbuf chain.
 */
struct mbuf *
in_bootp_bptombuf(bp)
	struct bootp_packet	*bp;
{
	register struct ip	*ip;
	struct mbuf		*m;
	int			resid = sizeof (*bp);
	caddr_t			cp = (caddr_t) bp;

	m = (struct mbuf *)m_devget(bp, sizeof(*bp), 0, 0, 0);

	if (m == 0) {
	    printf("in_bootp_bptombuf: m_devget failed\n");
	    return 0;
	}
	m->m_flags |= M_BCAST;

	/* Compute the checksum */
	ip = mtod(m, struct ip *);
	ip->ip_sum = 0;
	ip->ip_sum = in_cksum(m, sizeof (struct ip));
	return (m);
}

/*
 * Routine: in_bootp_timeout
 * Function:
 *	Wakeup the process waiting to receive something on a socket.
 *	Boy wouldn't it be nice if we could do system calls from the
 *	kernel.  Then I could do a select!
 */
void
in_bootp_timeout(socketflag)
	struct socket 		**socketflag;
{
	struct socket *so =	*socketflag;

	dprintf(("\n---->in_bootp_timeout()\n"));

	*socketflag = NULL;
	sowakeup(so, &so->so_rcv);
}

/*
 * Routine: in_bootp_promisctimeout
 * Function:
 *	Flag that it is ok to accept a response from a promiscuous BOOTP
 *	server.
 */
int
in_bootp_promisctimeout(ignoreflag)
	boolean_t	*ignoreflag;
{
	dprintf(("\n---->in_bootp_promisctimeout()\n"));

	*ignoreflag = FALSE;
	return 0;
}
 
/*
 * Routine: in_bootp_getpacket
 * Function:
 *	Wait for an incoming packet on the socket.
 */
int
in_bootp_getpacket(so, pp, psize)
	struct socket		*so;
	caddr_t			pp;
	int			psize;
{
	struct	iovec		aiov;
	struct	uio		auio;
	int			rcvflg;

	aiov.iov_base = pp;
	aiov.iov_len = psize;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_offset = 0;
	auio.uio_resid = psize;
	auio.uio_rw = UIO_READ;
	rcvflg = MSG_WAITALL;

	return (soreceive(so, 0, &auio, 0, 0, &rcvflg));
}

#define INITIAL_DEADLINE	5
/*
 * Routine: in_bootp_sendrequest
 * Function:
 *	Do the actual work of sending a BOOTP request and getting the
 *	response.
 */
int
in_bootp_sendrequest(
	volatile struct ifnet	*ifp,
	struct socket		*so,
	struct bootp_packet	*bp_s,
	struct bootp		*bp_r,
	u_char			my_enaddr[6],
	struct vnode		**console_vpp
) {
	struct mbuf		*m;
	register int		error;
	int			xid;
	struct timeval		tv;
	int			timeo;
	int			timecount;
	unsigned long		totaltime;
	unsigned long		deadline;
	struct socket		*timeflag;
	label_t			jmpbuf;
	struct sockaddr_in	lsin;
	struct nextvend		*nv_s =
		(struct nextvend *) &bp_s->bp_bootp.bp_vend;
	struct nextvend		*nv_r = (struct nextvend *)&bp_r->bp_vend;
	boolean_t		promisc_rcvd = FALSE;
	boolean_t		promisc_ignore;

	dprintf(("  in_bootp_sendrequest() entered\n"));

	/* Create a transaction ID */
	microtime(&tv);
	xid = my_enaddr[5] ^ tv.tv_sec;
	bp_s->bp_bootp.bp_xid = xid;

	/* Address to send to */
	lsin.sin_family = AF_INET;
	lsin.sin_port = htons(IPPORT_BOOTPS);
	lsin.sin_addr.s_addr = INADDR_BROADCAST;

	/*
 	 * timeout/retry policy:
	 * Before a server has responded we retransmit at a random time
	 * in a binary window which is doubled each transmission.  This
	 * avoids flooding the net after, say, a power failure when all
	 * machines are trying to reboot simultaneously.
	 */
	timeo = 1;
	timecount = 0;
	totaltime = 0;
	deadline = INITIAL_DEADLINE;

	while (TRUE) {
		if (timecount == 0) {
			/* Put the outgoing packet in an mbuf */
			m = in_bootp_bptombuf(bp_s);
			dprintf(("  in_bootp_sendrequest() if_output "));
			if (error = (*ifp->if_output)(ifp, m,
					      (struct sockaddr *)&lsin, 0)) {
				dprintf(("failed: %d\n", error));
				goto errout;
			}
			dprintf(("ok\n"));
		}

		/*
		 * Sleep for a second and then check for the abort
		 * character again.
		 */
		timeflag = so;
		timeout(in_bootp_timeout, &timeflag, hz);
keepwaiting:
		dprintf(("  in_bootp_sendrequest(): in_bootp_getpacket..."));
		while (((error = in_bootp_getpacket(so, (caddr_t)bp_r, sizeof (*bp_r)))
			== EWOULDBLOCK) && (timeflag == so)) {
			/* dprintf(("waiting (spl = 0x%x)...", curspl())); */
			sbwait(&so->so_rcv);
		}
		if (error && (error != EWOULDBLOCK)) {
			dprintf(("failed: %d\n", error));
			untimeout(in_bootp_timeout, &timeflag);
			goto errout;
		}
		if (timeflag == NULL) {
			dprintf(("  in_bootp_sendrequest(): "
				 "in_bootp_getpacket...timed out\n"));
			if ((deadline > INITIAL_DEADLINE) && (timecount & 1))
			    printf(".");
			timecount++;
			totaltime++;
			if (timecount == timeo) {
				if (timeo < 64) {
					timeo <<= 1;
				}
				timecount = 0;
			}
			if (totaltime == deadline) {
			        register c = 0;

				if (error=in_bootp_openconsole(console_vpp)) {
					goto errout;
				}
				printf("\nServer %snot responding.\n"
				       "Continue without network? (y/n) ",
				       (deadline == INITIAL_DEADLINE) ? "is " : "still ");
				while (1) {
				    c = kmgetc_silent(0) & 0177;
				    if (c == 'y' || c == 'Y') {
					printf("%c\n", c);
					psignal(current_proc(), SIGINT);
					error = EINTR;
					in_bootp_closeconsole(*console_vpp);
					goto errout;
				    }
				    else if (c == 'n' || c == 'N') {
					break;
				    }
				}
				printf("%c\nWaiting for response.", c);
				deadline += 30;
			}

			continue;
		}

		dprintf(("received packet!\n"));

		/* We have a packet.  Check it out. */
		if (bp_r->bp_xid != xid ||
		    bp_r->bp_op != BOOTREPLY ||
		    bcmp(bp_r->bp_chaddr, my_enaddr, 6) != 0) {
			dprintf(("  in_bootp_sendrequest() not for us\n"));
			goto keepwaiting;
		}

		/*
		 * It's for us.  If it is not an authoritative answer,
		 * wait for a while (10 secs) to see if there is an
		 * authoritative server out there.
		 */
		if (nv_s->nv_opcode == BPOP_OK && nv_r->nv_opcode != BPOP_OK) {
			if (!promisc_rcvd) {
				promisc_rcvd = TRUE;
				promisc_ignore = TRUE;
				timeout(in_bootp_promisctimeout,
					&promisc_ignore, 10 * hz);
				dprintf(("  in_bootp_sendrequest() "
					 "ignoring 1st promisc response\n"));
				goto keepwaiting;
			}
			if (promisc_ignore == TRUE) {
				dprintf(("  in_bootp_sendrequest() "
					 "ignoring promisc response\n"));
				goto keepwaiting;
			}
		}


		/* OK, it's for us */
		dprintf(("  in_bootp_sendrequest() it's for us!\n"));

		untimeout(in_bootp_timeout, &timeflag);

		if (alertShowing) {
			printf("Network responded!\n");
		}
		error = 0;
		goto errout;
	}

	/* We aborted.  Return appropriate error */
	error = ETIMEDOUT;

errout:
	dprintf(("  in_bootp_sendrequest() returning %d\n", error));

	untimeout(in_bootp_promisctimeout, &promisc_ignore);
	return (error);
}

/*
 * Routine: in_bootp_openconsole
 * Function:
 *	Pop up a console window so that we can do some user I/O to
 *	answer the server's questions.
 */
int
in_bootp_openconsole(vpp)
	struct vnode	**vpp;
{
	int	ret = 0;
	struct proc *		procp = current_proc();	/* XXX */

	if (alertShowing == FALSE) {
		ret = alert(60, 8, "Configuring Network", "%L", 0, 0, 0, 0, 0, 0, 0);
		alertShowing = TRUE;
	}
	dprintf(("in_bootp_openconsole() returning: %d\n", ret));
	return(ret);
}

/*
 * Routine: in_bootp_closeconsole
 * Function:
 *	Restore and close and the console window.
 */
void
in_bootp_closeconsole(vp)
	struct vnode *vp;
{
	if (alertShowing)
	    alert_done();
	alertShowing = FALSE;
}

/*
 * Routine: in_bootp_processreply
 * Function:
 *	The heart of "Sexy Net Init".  This routine handles requests from
 *	the BOOTP server to interact with the user.
 */
int
in_bootp_processreply(vp, bp_s, bp_r)
	struct vnode	*vp;
	struct bootp_packet	*bp_s;
	struct bootp		*bp_r;
{
	struct nextvend		*nv_s =
		(struct nextvend *) &bp_s->bp_bootp.bp_vend;
	struct nextvend		*nv_r = (struct nextvend *)&bp_r->bp_vend;
	int			resid;
	int			error;
	int			flush = FREAD;
	boolean_t		noecho = FALSE;
	char			*cp;

	/* Truncate text if needed */
	if (strlen(nv_r->nv_text) > NVMAXTEXT) {
		nv_r->nv_null = '\0';
	}

	/* Write out the server message */
	printf("%s", nv_r->nv_text);

	if (error = kmioctl(0, TIOCFLUSH, &flush, 0)) {
		goto errout;
	}
	switch (nv_r->nv_opcode) {
	    case BPOP_QUERY_NE:
		noecho = TRUE;
		/* Fall through */

	    case BPOP_QUERY:
		error = 0;
		/* Read in the user response */
		kmgets(nv_s->nv_text, nv_s->nv_text, noecho);

		/* If we are not echoing, move the cursor to the next line */
		if (noecho) {
			printf("\n");
		}

		/* Get rid of the CR or LF if either exists. */
		for (cp = (char *)nv_s->nv_text; *cp; cp++) {
			if (*cp == '\n' || *cp == '\r') {
				*cp = '\0';
				break;
			}
		}

		/* Set the destination, opcode and xid */
		bp_s->bp_ip.ip_dst = *(struct in_addr *)&bp_r->bp_siaddr;
		nv_s->nv_opcode = nv_r->nv_opcode;
		nv_s->nv_xid = nv_r->nv_xid;
		break;

	    case BPOP_ERROR:
		/* Reset the destination, opcode and xid */
		bp_s->bp_ip.ip_dst.s_addr = INADDR_BROADCAST;
		nv_s->nv_opcode = BPOP_OK;
		nv_s->nv_xid = 0;
		break;
	}
errout:
	return  (error);
}

/*
 * Routine: in_bootp_setaddress
 * Function:
 *	Set the address of the interface.  Leave the new address in ifr
 *	to be returned to the user.
 */
int
in_bootp_setaddress(ifp, ifr, so, addr)
	register volatile struct ifnet	*ifp;
	register struct ifreq		*ifr;
	struct socket			*so;
	struct in_addr			*addr;
{
	struct sockaddr_in		*sin;
	int				error;
	struct proc *			procp = current_proc();	/* XXX */

	dprintf(("  in_bootp_setaddress() entered\n"));

	ifr->ifr_flags = ifp->if_flags & ~(IFF_UP);
	if (error = ifioctl(so, SIOCSIFFLAGS, (caddr_t) ifr, procp)) {
		dprintf(("  in_bootp_setaddress() SIOCSIFFLAGS failed: %d\n",
			 error));
		return (error);
	}	

	/* Clear the netmask first */
	sin = (struct sockaddr_in *)&(ifr->ifr_addr);
	bzero((caddr_t)sin, sizeof(struct sockaddr_in));
	sin->sin_len = sizeof(*sin);
	sin->sin_family = AF_INET;
	if (error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ifr, procp)) {
		dprintf(("  in_bootp_setaddress() SIOCSIFNETMASK failed: %d\n",
			 error));
		return (error);
	}

	/* Now set the new address */
	sin->sin_addr.s_addr = addr->s_addr;
	if (error = ifioctl(so, SIOCSIFADDR, (caddr_t)ifr, procp)) {
		dprintf(("  in_bootp_setaddress() SIOCSIFADDR failed: %d\n",
			 error));
		return (error);
	}

	/* Flag that we have autoconfigured */
	ifp->if_eflags |= IFEF_AUTOCONF_DONE;

	dprintf(("  in_bootp_setaddress() exiting\n"));

	return (0);
}

/*
 * Routine: in_bootp_makeifreq
 * Function:
 *	Make a valid ifreq struct from interface pointer and an address
 */
struct ifreq
in_bootp_makeifreq(
		   volatile struct ifnet *ifp,
		   struct sockaddr_in *sin
		   )
{
	struct ifreq ifr;
	char *p;
	
	strcpy(ifr.ifr_name, ifp->if_name);
	for (p = ifr.ifr_name; *p; p++) {
	}
	*p++ = ifp->if_unit + '0';
	*p = 0;
	bcopy(sin, &ifr.ifr_addr, sizeof(*sin));
	return (ifr);
}

/*
 * Routine: kmgets (mostly stolen from swapgeneric.{c,m}
 * Function:
 *	Allows printing echoed or non-echoed text.
 */
void
kmgets(cp, lp, silent)
	char *cp, *lp;
	boolean_t silent;
{
	register c = 0;

	for (;;) {
		if (silent) {
			c = kmgetc_silent(0) & 0177;
		} else {
			c = kmgetc(0) & 0177;
		}
		switch (c) {
		case '\n':
		case '\r':
			*lp++ = '\0';
			return;
		case '\177':
			if (lp == cp) {
				cnputc('\b');
				continue;
			}
			cnputc('\b');
			cnputc('\b');
			/* fall into ... */
		case '\b':
			if (lp == cp) {
				cnputc('\b');
				continue;
			}
			cnputc(' ');
			cnputc('\b');
			lp--;
			continue;
		case '@':
		case 'u'&037:
			lp = cp;
			cnputc('\n');
			continue;
		default:
			*lp++ = c;
		}
	}
}

#endif
#endif NEXT