1139823Simp/*-
225723Stegge * Copyright (c) 1995 Gordon Ross, Adam Glass
325723Stegge * Copyright (c) 1992 Regents of the University of California.
425723Stegge * All rights reserved.
525723Stegge *
625723Stegge * This software was developed by the Computer Systems Engineering group
725723Stegge * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
825723Stegge * contributed to Berkeley.
925723Stegge *
1025723Stegge * Redistribution and use in source and binary forms, with or without
1125723Stegge * modification, are permitted provided that the following conditions
1225723Stegge * are met:
1325723Stegge * 1. Redistributions of source code must retain the above copyright
1425723Stegge *    notice, this list of conditions and the following disclaimer.
1525723Stegge * 2. Redistributions in binary form must reproduce the above copyright
1625723Stegge *    notice, this list of conditions and the following disclaimer in the
1725723Stegge *    documentation and/or other materials provided with the distribution.
1825723Stegge * 3. All advertising materials mentioning features or use of this software
1925723Stegge *    must display the following acknowledgement:
2025723Stegge *	This product includes software developed by the University of
2125723Stegge *	California, Lawrence Berkeley Laboratory and its contributors.
2225723Stegge * 4. Neither the name of the University nor the names of its contributors
2325723Stegge *    may be used to endorse or promote products derived from this software
2425723Stegge *    without specific prior written permission.
2525723Stegge *
2625723Stegge * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2725723Stegge * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2825723Stegge * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2925723Stegge * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3025723Stegge * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3125723Stegge * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3225723Stegge * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3325723Stegge * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3425723Stegge * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3525723Stegge * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3625723Stegge * SUCH DAMAGE.
3725723Stegge *
3825723Stegge * based on:
3925723Stegge *      nfs/krpc_subr.c
4025723Stegge *	$NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $
4125723Stegge */
4225723Stegge
4383651Speter#include <sys/cdefs.h>
4483651Speter__FBSDID("$FreeBSD$");
4583651Speter
46131840Sbrian#include "opt_bootp.h"
47223673Sgber#include "opt_nfs.h"
48253847Sian#include "opt_rootdevname.h"
49131840Sbrian
5025723Stegge#include <sys/param.h>
5125723Stegge#include <sys/systm.h>
52193066Sjamie#include <sys/jail.h>
5325723Stegge#include <sys/kernel.h>
5425723Stegge#include <sys/sockio.h>
5567834Stegge#include <sys/malloc.h>
5625723Stegge#include <sys/mount.h>
5725723Stegge#include <sys/mbuf.h>
5888743Srwatson#include <sys/proc.h>
59253847Sian#include <sys/reboot.h>
6025723Stegge#include <sys/socket.h>
6125723Stegge#include <sys/socketvar.h>
6292219Sluigi#include <sys/sysctl.h>
6334924Sbde#include <sys/uio.h>
6425723Stegge
6525723Stegge#include <net/if.h>
6625723Stegge#include <net/route.h>
6725723Stegge
6825723Stegge#include <netinet/in.h>
69228455Sglebius#include <netinet/in_var.h>
7025723Stegge#include <net/if_types.h>
7125723Stegge#include <net/if_dl.h>
72185571Sbz#include <net/vnet.h>
7325723Stegge
7425723Stegge#include <nfs/nfsproto.h>
7583651Speter#include <nfsclient/nfs.h>
76221032Srmacklem#include <nfs/nfsdiskless.h>
77221032Srmacklem#include <nfs/krpc.h>
7825723Stegge#include <nfs/xdr_subs.h>
7925723Stegge
8025723Stegge
8132609Stegge#define BOOTP_MIN_LEN		300	/* Minimum size of bootp udp packet */
8225723Stegge
8367834Stegge#ifndef BOOTP_SETTLE_DELAY
8467834Stegge#define BOOTP_SETTLE_DELAY 3
8567834Stegge#endif
8667834Stegge
87239318Sgonzo/*
88239318Sgonzo * Wait 10 seconds for interface appearance
89239337Sgonzo * USB ethernet adapters might require some time to pop up
90239318Sgonzo */
91239318Sgonzo#ifndef	BOOTP_IFACE_WAIT_TIMEOUT
92239318Sgonzo#define	BOOTP_IFACE_WAIT_TIMEOUT	10
93239318Sgonzo#endif
94239318Sgonzo
9525723Stegge/*
9625723Stegge * What is the longest we will wait before re-sending a request?
9725723Stegge * Note this is also the frequency of "RPC timeout" messages.
9825723Stegge * The re-send loop count sup linearly to this maximum, so the
9925723Stegge * first complaint will happen after (1+2+3+4+5)=15 seconds.
10025723Stegge */
10125723Stegge#define	MAX_RESEND_DELAY 5	/* seconds */
10225723Stegge
10325723Stegge/* Definitions from RFC951 */
10425723Steggestruct bootp_packet {
10567531Stegge	u_int8_t op;
10667531Stegge	u_int8_t htype;
10767531Stegge	u_int8_t hlen;
10867531Stegge	u_int8_t hops;
10967531Stegge	u_int32_t xid;
11067531Stegge	u_int16_t secs;
11167531Stegge	u_int16_t flags;
11267531Stegge	struct in_addr ciaddr;
11367531Stegge	struct in_addr yiaddr;
11467531Stegge	struct in_addr siaddr;
11567531Stegge	struct in_addr giaddr;
11667531Stegge	unsigned char chaddr[16];
11767531Stegge	char sname[64];
11867531Stegge	char file[128];
11988680Sambrisko	unsigned char vend[1222];
12025723Stegge};
12125723Stegge
12267834Steggestruct bootpc_ifcontext {
123228455Sglebius	STAILQ_ENTRY(bootpc_ifcontext) next;
12467834Stegge	struct bootp_packet call;
12567834Stegge	struct bootp_packet reply;
12667834Stegge	int replylen;
12767834Stegge	int overload;
128228455Sglebius	union {
129228455Sglebius		struct ifreq _ifreq;
130228455Sglebius		struct in_aliasreq _in_alias_req;
131228455Sglebius	} _req;
132228455Sglebius#define	ireq	_req._ifreq
133228455Sglebius#define	iareq	_req._in_alias_req
13467834Stegge	struct ifnet *ifp;
13567834Stegge	struct sockaddr_dl *sdl;
13667834Stegge	struct sockaddr_in myaddr;
13767834Stegge	struct sockaddr_in netmask;
13867834Stegge	struct sockaddr_in gw;
13967834Stegge	int gotgw;
14067834Stegge	int gotnetmask;
14167834Stegge	int gotrootpath;
14267834Stegge	int outstanding;
14367834Stegge	int sentmsg;
14467834Stegge	u_int32_t xid;
14567834Stegge	enum {
14667834Stegge		IF_BOOTP_UNRESOLVED,
14767834Stegge		IF_BOOTP_RESOLVED,
14871915Stegge		IF_BOOTP_FAILED,
14971915Stegge		IF_DHCP_UNRESOLVED,
15067834Stegge		IF_DHCP_OFFERED,
15167834Stegge		IF_DHCP_RESOLVED,
15271915Stegge		IF_DHCP_FAILED,
15367834Stegge	} state;
15471915Stegge	int dhcpquerytype;		/* dhcp type sent */
15571915Stegge	struct in_addr dhcpserver;
15671915Stegge	int gotdhcpserver;
15767834Stegge};
15867834Stegge
15967834Stegge#define TAG_MAXLEN 1024
16067834Steggestruct bootpc_tagcontext {
16167834Stegge	char buf[TAG_MAXLEN + 1];
16267834Stegge	int overload;
16367834Stegge	int badopt;
16467834Stegge	int badtag;
16567834Stegge	int foundopt;
16667834Stegge	int taglen;
16767834Stegge};
16867834Stegge
16967834Steggestruct bootpc_globalcontext {
170228455Sglebius	STAILQ_HEAD(, bootpc_ifcontext) interfaces;
17167834Stegge	u_int32_t xid;
172253847Sian	int any_root_overrides;
17367834Stegge	int gotrootpath;
17467834Stegge	int gotgw;
17567834Stegge	int ifnum;
17667834Stegge	int secs;
17767834Stegge	int starttime;
17867834Stegge	struct bootp_packet reply;
17967834Stegge	int replylen;
18067834Stegge	struct bootpc_ifcontext *setrootfs;
18167834Stegge	struct bootpc_ifcontext *sethostname;
18267834Stegge	struct bootpc_tagcontext tmptag;
18367834Stegge	struct bootpc_tagcontext tag;
18467834Stegge};
18567834Stegge
18625723Stegge#define IPPORT_BOOTPC 68
18725723Stegge#define IPPORT_BOOTPS 67
18825723Stegge
18967834Stegge#define BOOTP_REQUEST 1
19067834Stegge#define BOOTP_REPLY 2
19167834Stegge
19267834Stegge/* Common tags */
19367834Stegge#define TAG_PAD		  0  /* Pad option, implicit length 1 */
19467834Stegge#define TAG_SUBNETMASK	  1  /* RFC 950 subnet mask */
19567834Stegge#define TAG_ROUTERS	  3  /* Routers (in order of preference) */
19667834Stegge#define TAG_HOSTNAME	 12  /* Client host name */
19767834Stegge#define TAG_ROOT	 17  /* Root path */
19867834Stegge
19967834Stegge/* DHCP specific tags */
20067834Stegge#define TAG_OVERLOAD	 52  /* Option Overload */
20167834Stegge#define TAG_MAXMSGSIZE   57  /* Maximum DHCP Message Size */
20267834Stegge
20367834Stegge#define TAG_END		255  /* End Option (i.e. no more options) */
20467834Stegge
20567834Stegge/* Overload values */
20667834Stegge#define OVERLOAD_FILE     1
20767834Stegge#define OVERLOAD_SNAME    2
20867834Stegge
20967834Stegge/* Site specific tags: */
21067834Stegge#define TAG_ROOTOPTS	130
21192219Sluigi#define TAG_COOKIE	134	/* ascii info for userland, via sysctl */
21267834Stegge
21371915Stegge#define TAG_DHCP_MSGTYPE 53
21471915Stegge#define TAG_DHCP_REQ_ADDR 50
21571915Stegge#define TAG_DHCP_SERVERID 54
21671915Stegge#define TAG_DHCP_LEASETIME 51
21771915Stegge
21896825Sambrisko#define TAG_VENDOR_INDENTIFIER 60
21996825Sambrisko
22071915Stegge#define DHCP_NOMSG    0
22171915Stegge#define DHCP_DISCOVER 1
22271915Stegge#define DHCP_OFFER    2
22371915Stegge#define DHCP_REQUEST  3
22471915Stegge#define DHCP_ACK      5
22571915Stegge
226179039Sbenno/* NFS read/write block size */
227179039Sbenno#ifndef BOOTP_BLOCKSIZE
228179039Sbenno#define	BOOTP_BLOCKSIZE	8192
229179039Sbenno#endif
230179039Sbenno
23192219Sluigistatic char bootp_cookie[128];
232228455Sglebiusstatic struct socket *bootp_so;
23392219SluigiSYSCTL_STRING(_kern, OID_AUTO, bootp_cookie, CTLFLAG_RD,
23492219Sluigi	bootp_cookie, 0, "Cookie (T134) supplied by bootp server");
23592219Sluigi
23625723Stegge/* mountd RPC */
23783651Speterstatic int	md_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp,
23883651Speter		    int *fhsizep, struct nfs_args *args, struct thread *td);
239126888Sbrooksstatic int	setfs(struct sockaddr_in *addr, char *path, char *p,
240126888Sbrooks		    const struct in_addr *siaddr);
24183651Speterstatic int	getdec(char **ptr);
242126888Sbrooksstatic int	getip(char **ptr, struct in_addr *ip);
24383651Speterstatic void	mountopts(struct nfs_args *args, char *p);
24483651Speterstatic int	xdr_opaque_decode(struct mbuf **ptr, u_char *buf, int len);
24583651Speterstatic int	xdr_int_decode(struct mbuf **ptr, int *iptr);
24683651Speterstatic void	print_in_addr(struct in_addr addr);
24783651Speterstatic void	print_sin_addr(struct sockaddr_in *addr);
24883651Speterstatic void	clear_sinaddr(struct sockaddr_in *sin);
249118639Sbillfstatic void	allocifctx(struct bootpc_globalcontext *gctx);
25083651Speterstatic void	bootpc_compose_query(struct bootpc_ifcontext *ifctx,
251228455Sglebius		    struct thread *td);
25267834Steggestatic unsigned char *bootpc_tag(struct bootpc_tagcontext *tctx,
25383651Speter		    struct bootp_packet *bp, int len, int tag);
25467834Steggestatic void bootpc_tag_helper(struct bootpc_tagcontext *tctx,
25583651Speter		    unsigned char *start, int len, int tag);
25625723Stegge
25725723Stegge#ifdef BOOTP_DEBUG
25883651Spetervoid bootpboot_p_sa(struct sockaddr *sa, struct sockaddr *ma);
25925723Steggevoid bootpboot_p_rtentry(struct rtentry *rt);
26025723Steggevoid bootpboot_p_tree(struct radix_node *rn);
26125723Steggevoid bootpboot_p_rtlist(void);
26267834Steggevoid bootpboot_p_if(struct ifnet *ifp, struct ifaddr *ifa);
26325723Steggevoid bootpboot_p_iflist(void);
26425723Stegge#endif
26525723Stegge
26683651Speterstatic int	bootpc_call(struct bootpc_globalcontext *gctx,
26783651Speter		    struct thread *td);
26825723Stegge
269228455Sglebiusstatic void	bootpc_fakeup_interface(struct bootpc_ifcontext *ifctx,
270228455Sglebius		    struct thread *td);
27125723Stegge
27283651Speterstatic int	bootpc_adjust_interface(struct bootpc_ifcontext *ifctx,
27383651Speter		    struct bootpc_globalcontext *gctx, struct thread *td);
27425723Stegge
27583651Speterstatic void	bootpc_decode_reply(struct nfsv3_diskless *nd,
27683651Speter		    struct bootpc_ifcontext *ifctx,
27783651Speter		    struct bootpc_globalcontext *gctx);
27867834Stegge
27983651Speterstatic int	bootpc_received(struct bootpc_globalcontext *gctx,
28083651Speter		    struct bootpc_ifcontext *ifctx);
28167834Stegge
28271915Steggestatic __inline int bootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx);
28371915Steggestatic __inline int bootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx);
28471915Steggestatic __inline int bootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx);
28571915Stegge
28667834Stegge/*
28767834Stegge * In order to have multiple active interfaces with address 0.0.0.0
288228455Sglebius * and be able to send data to a selected interface, we first set
289228455Sglebius * mask to /8 on all interfaces, and temporarily set it to /0 when
290228455Sglebius * doing sosend().
29167834Stegge */
29267834Stegge
29325723Stegge#ifdef BOOTP_DEBUG
29467531Steggevoid
29583651Speterbootpboot_p_sa(struct sockaddr *sa, struct sockaddr *ma)
29625723Stegge{
29783651Speter
29867531Stegge	if (sa == NULL) {
29967531Stegge		printf("(sockaddr *) <null>");
30067531Stegge		return;
30167531Stegge	}
30267531Stegge	switch (sa->sa_family) {
30367531Stegge	case AF_INET:
30467531Stegge	{
30567531Stegge		struct sockaddr_in *sin;
30683651Speter
30767531Stegge		sin = (struct sockaddr_in *) sa;
30867834Stegge		printf("inet ");
30967834Stegge		print_sin_addr(sin);
31067531Stegge		if (ma != NULL) {
31167531Stegge			sin = (struct sockaddr_in *) ma;
31267834Stegge			printf(" mask ");
31367834Stegge			print_sin_addr(sin);
31467531Stegge		}
31567531Stegge	}
31667531Stegge	break;
31767531Stegge	case AF_LINK:
31867531Stegge	{
31967531Stegge		struct sockaddr_dl *sli;
32067531Stegge		int i;
32183651Speter
32267531Stegge		sli = (struct sockaddr_dl *) sa;
32367531Stegge		printf("link %.*s ", sli->sdl_nlen, sli->sdl_data);
32467531Stegge		for (i = 0; i < sli->sdl_alen; i++) {
32567531Stegge			if (i > 0)
32667531Stegge				printf(":");
32767531Stegge			printf("%x", ((unsigned char *) LLADDR(sli))[i]);
32867531Stegge		}
32967531Stegge	}
33067531Stegge	break;
33167531Stegge	default:
33267531Stegge		printf("af%d", sa->sa_family);
33367531Stegge	}
33425723Stegge}
33525723Stegge
33667531Steggevoid
33767531Steggebootpboot_p_rtentry(struct rtentry *rt)
33825723Stegge{
33983651Speter
34067531Stegge	bootpboot_p_sa(rt_key(rt), rt_mask(rt));
34167531Stegge	printf(" ");
34267531Stegge	bootpboot_p_sa(rt->rt_gateway, NULL);
34367531Stegge	printf(" ");
34467531Stegge	printf("flags %x", (unsigned short) rt->rt_flags);
345263478Sglebius	printf(" %d", (int) rt->rt_expire);
346121816Sbrooks	printf(" %s\n", rt->rt_ifp->if_xname);
34725723Stegge}
34867531Stegge
34967531Steggevoid
35067531Steggebootpboot_p_tree(struct radix_node *rn)
35125723Stegge{
35283651Speter
35367531Stegge	while (rn != NULL) {
35467534Stegge		if (rn->rn_bit < 0) {
35567531Stegge			if ((rn->rn_flags & RNF_ROOT) != 0) {
35667531Stegge			} else {
35767531Stegge				bootpboot_p_rtentry((struct rtentry *) rn);
35867531Stegge			}
35967531Stegge			rn = rn->rn_dupedkey;
36067531Stegge		} else {
36167534Stegge			bootpboot_p_tree(rn->rn_left);
36267534Stegge			bootpboot_p_tree(rn->rn_right);
36367531Stegge			return;
36467531Stegge		}
36567531Stegge	}
36625723Stegge}
36725723Stegge
36867531Steggevoid
36967531Steggebootpboot_p_rtlist(void)
37025723Stegge{
371193232Sbz	struct radix_node_head *rnh;
37283651Speter
37367531Stegge	printf("Routing table:\n");
374193232Sbz	rnh = rt_tables_get_rnh(0, AF_INET);
375193232Sbz	if (rnh == NULL)
376193232Sbz		return;
377193232Sbz	RADIX_NODE_HEAD_RLOCK(rnh);	/* could sleep XXX */
378193232Sbz	bootpboot_p_tree(rnh->rnh_treetop);
379193232Sbz	RADIX_NODE_HEAD_RUNLOCK(rnh);
38025723Stegge}
38125723Stegge
38267531Steggevoid
38367834Steggebootpboot_p_if(struct ifnet *ifp, struct ifaddr *ifa)
38467834Stegge{
38583651Speter
386121816Sbrooks	printf("%s flags %x, addr ",
387121816Sbrooks	       ifp->if_xname, ifp->if_flags);
38867834Stegge	print_sin_addr((struct sockaddr_in *) ifa->ifa_addr);
38967834Stegge	printf(", broadcast ");
39067834Stegge	print_sin_addr((struct sockaddr_in *) ifa->ifa_dstaddr);
39167834Stegge	printf(", netmask ");
39267834Stegge	print_sin_addr((struct sockaddr_in *) ifa->ifa_netmask);
39367834Stegge	printf("\n");
39467834Stegge}
39567834Stegge
39667834Steggevoid
39767531Steggebootpboot_p_iflist(void)
39825723Stegge{
39967531Stegge	struct ifnet *ifp;
40067531Stegge	struct ifaddr *ifa;
40183651Speter
40267531Stegge	printf("Interface list:\n");
403196481Srwatson	IFNET_RLOCK();
404181803Sbz	for (ifp = TAILQ_FIRST(&V_ifnet);
40567531Stegge	     ifp != NULL;
40667834Stegge	     ifp = TAILQ_NEXT(ifp, if_link)) {
40767531Stegge		for (ifa = TAILQ_FIRST(&ifp->if_addrhead);
40867531Stegge		     ifa != NULL;
40967834Stegge		     ifa = TAILQ_NEXT(ifa, ifa_link))
41067834Stegge			if (ifa->ifa_addr->sa_family == AF_INET)
41167834Stegge				bootpboot_p_if(ifp, ifa);
41225723Stegge	}
413108172Shsu	IFNET_RUNLOCK();
41425723Stegge}
41567834Stegge#endif /* defined(BOOTP_DEBUG) */
41625723Stegge
41767834Steggestatic void
41867834Steggeclear_sinaddr(struct sockaddr_in *sin)
41967834Stegge{
42083651Speter
42167834Stegge	bzero(sin, sizeof(*sin));
42267834Stegge	sin->sin_len = sizeof(*sin);
42367834Stegge	sin->sin_family = AF_INET;
42467834Stegge	sin->sin_addr.s_addr = INADDR_ANY; /* XXX: htonl(INAADDR_ANY) ? */
42567834Stegge	sin->sin_port = 0;
42667834Stegge}
42767834Stegge
428118639Sbillfstatic void
42967834Steggeallocifctx(struct bootpc_globalcontext *gctx)
43067834Stegge{
43167834Stegge	struct bootpc_ifcontext *ifctx;
43283651Speter
433228455Sglebius	ifctx = malloc(sizeof(*ifctx), M_TEMP, M_WAITOK | M_ZERO);
43467834Stegge	ifctx->xid = gctx->xid;
43571915Stegge#ifdef BOOTP_NO_DHCP
43671915Stegge	ifctx->state = IF_BOOTP_UNRESOLVED;
43771915Stegge#else
43871915Stegge	ifctx->state = IF_DHCP_UNRESOLVED;
43971915Stegge#endif
44067834Stegge	gctx->xid += 0x100;
441228455Sglebius	STAILQ_INSERT_TAIL(&gctx->interfaces, ifctx, next);
44267834Stegge}
44367834Stegge
44471915Steggestatic __inline int
44571915Steggebootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx)
44671915Stegge{
44783651Speter
44871915Stegge	if (ifctx->state == IF_BOOTP_RESOLVED ||
44971915Stegge	    ifctx->state == IF_DHCP_RESOLVED)
45071915Stegge		return 1;
45171915Stegge	return 0;
45271915Stegge}
45371915Stegge
45471915Steggestatic __inline int
45571915Steggebootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx)
45671915Stegge{
45783651Speter
45871915Stegge	if (ifctx->state == IF_BOOTP_UNRESOLVED ||
45971915Stegge	    ifctx->state == IF_DHCP_UNRESOLVED)
46071915Stegge		return 1;
46171915Stegge	return 0;
46271915Stegge}
46371915Stegge
46471915Steggestatic __inline int
46571915Steggebootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx)
46671915Stegge{
46783651Speter
46871915Stegge	if (ifctx->state == IF_BOOTP_FAILED ||
46971915Stegge	    ifctx->state == IF_DHCP_FAILED)
47071915Stegge		return 1;
47171915Stegge	return 0;
47271915Stegge}
47371915Stegge
47433181Seivindstatic int
47567834Steggebootpc_received(struct bootpc_globalcontext *gctx,
47683651Speter    struct bootpc_ifcontext *ifctx)
47767834Stegge{
47871915Stegge	unsigned char dhcpreplytype;
47971915Stegge	char *p;
48083651Speter
48167834Stegge	/*
48267834Stegge	 * Need timeout for fallback to less
48367834Stegge	 * desirable alternative.
48467834Stegge	 */
48567834Stegge
48667834Stegge	/* This call used for the side effect (badopt flag) */
48767834Stegge	(void) bootpc_tag(&gctx->tmptag, &gctx->reply,
48867834Stegge			  gctx->replylen,
48967834Stegge			  TAG_END);
49067834Stegge
49167834Stegge	/* If packet is invalid, ignore it */
49267834Stegge	if (gctx->tmptag.badopt != 0)
49367834Stegge		return 0;
49483651Speter
49571915Stegge	p = bootpc_tag(&gctx->tmptag, &gctx->reply,
49671915Stegge		       gctx->replylen, TAG_DHCP_MSGTYPE);
49771915Stegge	if (p != NULL)
49871915Stegge		dhcpreplytype = *p;
49971915Stegge	else
50071915Stegge		dhcpreplytype = DHCP_NOMSG;
50183651Speter
50271915Stegge	switch (ifctx->dhcpquerytype) {
50371915Stegge	case DHCP_DISCOVER:
50471915Stegge		if (dhcpreplytype != DHCP_OFFER 	/* Normal DHCP offer */
50571915Stegge#ifndef BOOTP_FORCE_DHCP
50671915Stegge		    && dhcpreplytype != DHCP_NOMSG	/* Fallback to BOOTP */
50771915Stegge#endif
50871915Stegge			)
50971915Stegge			return 0;
51071915Stegge		break;
51171915Stegge	case DHCP_REQUEST:
51271915Stegge		if (dhcpreplytype != DHCP_ACK)
51371915Stegge			return 0;
51471915Stegge	case DHCP_NOMSG:
51597211Speter		break;
51671915Stegge	}
51783651Speter
51867834Stegge	/* Ignore packet unless it gives us a root tag we didn't have */
51983651Speter
52071915Stegge	if ((ifctx->state == IF_BOOTP_RESOLVED ||
52171915Stegge	     (ifctx->dhcpquerytype == DHCP_DISCOVER &&
52271915Stegge	      (ifctx->state == IF_DHCP_OFFERED ||
52371915Stegge	       ifctx->state == IF_DHCP_RESOLVED))) &&
52467834Stegge	    (bootpc_tag(&gctx->tmptag, &ifctx->reply,
52567834Stegge			ifctx->replylen,
52667834Stegge			TAG_ROOT) != NULL ||
52767834Stegge	     bootpc_tag(&gctx->tmptag, &gctx->reply,
52867834Stegge			gctx->replylen,
52967834Stegge			TAG_ROOT) == NULL))
53067834Stegge		return 0;
53183651Speter
53283651Speter	bcopy(&gctx->reply, &ifctx->reply, gctx->replylen);
53367834Stegge	ifctx->replylen = gctx->replylen;
53483651Speter
53567834Stegge	/* XXX: Only reset if 'perfect' response */
53671915Stegge	if (ifctx->state == IF_BOOTP_UNRESOLVED)
53771915Stegge		ifctx->state = IF_BOOTP_RESOLVED;
53871915Stegge	else if (ifctx->state == IF_DHCP_UNRESOLVED &&
53971915Stegge		 ifctx->dhcpquerytype == DHCP_DISCOVER) {
54071915Stegge		if (dhcpreplytype == DHCP_OFFER)
54171915Stegge			ifctx->state = IF_DHCP_OFFERED;
54271915Stegge		else
54371915Stegge			ifctx->state = IF_BOOTP_RESOLVED;	/* Fallback */
54471915Stegge	} else if (ifctx->state == IF_DHCP_OFFERED &&
54571915Stegge		   ifctx->dhcpquerytype == DHCP_REQUEST)
54671915Stegge		ifctx->state = IF_DHCP_RESOLVED;
54783651Speter
54883651Speter
54971915Stegge	if (ifctx->dhcpquerytype == DHCP_DISCOVER &&
55071915Stegge	    ifctx->state != IF_BOOTP_RESOLVED) {
55171915Stegge		p = bootpc_tag(&gctx->tmptag, &ifctx->reply,
55271915Stegge			       ifctx->replylen, TAG_DHCP_SERVERID);
55371915Stegge		if (p != NULL && gctx->tmptag.taglen == 4) {
55471915Stegge			memcpy(&ifctx->dhcpserver, p, 4);
55571915Stegge			ifctx->gotdhcpserver = 1;
55671915Stegge		} else
55771915Stegge			ifctx->gotdhcpserver = 0;
55871915Stegge		return 1;
55971915Stegge	}
56083651Speter
56167834Stegge	ifctx->gotrootpath = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
56267834Stegge					 ifctx->replylen,
56367834Stegge					 TAG_ROOT) != NULL);
56467834Stegge	ifctx->gotgw = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
56567834Stegge				   ifctx->replylen,
56667834Stegge				   TAG_ROUTERS) != NULL);
56767834Stegge	ifctx->gotnetmask = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
56867834Stegge					ifctx->replylen,
56967834Stegge					TAG_SUBNETMASK) != NULL);
57067834Stegge	return 1;
57167834Stegge}
57267834Stegge
57367834Steggestatic int
57483651Speterbootpc_call(struct bootpc_globalcontext *gctx, struct thread *td)
57525723Stegge{
57667834Stegge	struct sockaddr_in *sin, dst;
57725723Stegge	struct uio auio;
57838482Swollman	struct sockopt sopt;
57925723Stegge	struct iovec aio;
58067834Stegge	int error, on, rcvflg, timo, len;
58167834Stegge	time_t atimo;
58267834Stegge	time_t rtimo;
58338482Swollman	struct timeval tv;
58467834Stegge	struct bootpc_ifcontext *ifctx;
58567834Stegge	int outstanding;
58667834Stegge	int gotrootpath;
58771915Stegge	int retry;
58871915Stegge	const char *s;
58983651Speter
59038482Swollman	tv.tv_sec = 1;
59138482Swollman	tv.tv_usec = 0;
59267834Stegge	bzero(&sopt, sizeof(sopt));
593120755Sjeff	sopt.sopt_dir = SOPT_SET;
59438482Swollman	sopt.sopt_level = SOL_SOCKET;
59538482Swollman	sopt.sopt_name = SO_RCVTIMEO;
59638482Swollman	sopt.sopt_val = &tv;
59738482Swollman	sopt.sopt_valsize = sizeof tv;
59883651Speter
599228455Sglebius	error = sosetopt(bootp_so, &sopt);
60067834Stegge	if (error != 0)
60125723Stegge		goto out;
60283651Speter
60325723Stegge	/*
60425723Stegge	 * Enable broadcast.
60525723Stegge	 */
60638482Swollman	on = 1;
60767834Stegge	sopt.sopt_name = SO_BROADCAST;
60838482Swollman	sopt.sopt_val = &on;
60938482Swollman	sopt.sopt_valsize = sizeof on;
61067834Stegge
611228455Sglebius	error = sosetopt(bootp_so, &sopt);
61267834Stegge	if (error != 0)
61338482Swollman		goto out;
61425723Stegge
61525723Stegge	/*
61667834Stegge	 * Disable routing.
61767834Stegge	 */
61867834Stegge
61967834Stegge	on = 1;
62067834Stegge	sopt.sopt_name = SO_DONTROUTE;
62167834Stegge	sopt.sopt_val = &on;
62267834Stegge	sopt.sopt_valsize = sizeof on;
62367834Stegge
624228455Sglebius	error = sosetopt(bootp_so, &sopt);
62567834Stegge	if (error != 0)
62667834Stegge		goto out;
62783651Speter
62867834Stegge	/*
62925723Stegge	 * Bind the local endpoint to a bootp client port.
63025723Stegge	 */
63167834Stegge	sin = &dst;
63267834Stegge	clear_sinaddr(sin);
63325723Stegge	sin->sin_port = htons(IPPORT_BOOTPC);
634228455Sglebius	error = sobind(bootp_so, (struct sockaddr *)sin, td);
63567834Stegge	if (error != 0) {
63625723Stegge		printf("bind failed\n");
63725723Stegge		goto out;
63825723Stegge	}
63983651Speter
64025723Stegge	/*
64125723Stegge	 * Setup socket address for the server.
64225723Stegge	 */
64367834Stegge	sin = &dst;
64467834Stegge	clear_sinaddr(sin);
64525723Stegge	sin->sin_addr.s_addr = INADDR_BROADCAST;
64625723Stegge	sin->sin_port = htons(IPPORT_BOOTPS);
64783651Speter
64825723Stegge	/*
64925723Stegge	 * Send it, repeatedly, until a reply is received,
65025723Stegge	 * but delay each re-send by an increasing amount.
65125723Stegge	 * If the delay hits the maximum, start complaining.
65225723Stegge	 */
65325723Stegge	timo = 0;
65467834Stegge	rtimo = 0;
65525723Stegge	for (;;) {
65683651Speter
65767834Stegge		outstanding = 0;
65867834Stegge		gotrootpath = 0;
65967834Stegge
660228455Sglebius		STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
66171915Stegge			if (bootpc_ifctx_isresolved(ifctx) != 0 &&
66267834Stegge			    bootpc_tag(&gctx->tmptag, &ifctx->reply,
66367834Stegge				       ifctx->replylen,
66467834Stegge				       TAG_ROOT) != NULL)
66567834Stegge				gotrootpath = 1;
66667834Stegge		}
66783651Speter
668228455Sglebius		STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
669228455Sglebius			struct in_aliasreq *ifra = &ifctx->iareq;
670228455Sglebius			sin = (struct sockaddr_in *)&ifra->ifra_mask;
671228455Sglebius
67267834Stegge			ifctx->outstanding = 0;
67371915Stegge			if (bootpc_ifctx_isresolved(ifctx)  != 0 &&
67467834Stegge			    gotrootpath != 0) {
67567834Stegge				continue;
67667834Stegge			}
67771915Stegge			if (bootpc_ifctx_isfailed(ifctx) != 0)
67867834Stegge				continue;
67925723Stegge
68067834Stegge			outstanding++;
68167834Stegge			ifctx->outstanding = 1;
68267834Stegge
68371915Stegge			/* Proceed to next step in DHCP negotiation */
68471915Stegge			if ((ifctx->state == IF_DHCP_OFFERED &&
68571915Stegge			     ifctx->dhcpquerytype != DHCP_REQUEST) ||
68671915Stegge			    (ifctx->state == IF_DHCP_UNRESOLVED &&
68771915Stegge			     ifctx->dhcpquerytype != DHCP_DISCOVER) ||
68871915Stegge			    (ifctx->state == IF_BOOTP_UNRESOLVED &&
68971915Stegge			     ifctx->dhcpquerytype != DHCP_NOMSG)) {
69071915Stegge				ifctx->sentmsg = 0;
691228455Sglebius				bootpc_compose_query(ifctx, td);
69271915Stegge			}
69383651Speter
69467834Stegge			/* Send BOOTP request (or re-send). */
69583651Speter
69667834Stegge			if (ifctx->sentmsg == 0) {
69771915Stegge				switch(ifctx->dhcpquerytype) {
69871915Stegge				case DHCP_DISCOVER:
69971915Stegge					s = "DHCP Discover";
70071915Stegge					break;
70171915Stegge				case DHCP_REQUEST:
70271915Stegge					s = "DHCP Request";
70371915Stegge					break;
70471915Stegge				case DHCP_NOMSG:
70571915Stegge				default:
70671915Stegge					s = "BOOTP Query";
70771915Stegge					break;
70871915Stegge				}
70971915Stegge				printf("Sending %s packet from "
71067834Stegge				       "interface %s (%*D)\n",
71171915Stegge				       s,
71267834Stegge				       ifctx->ireq.ifr_name,
71367834Stegge				       ifctx->sdl->sdl_alen,
71467834Stegge				       (unsigned char *) LLADDR(ifctx->sdl),
71567834Stegge				       ":");
71667834Stegge				ifctx->sentmsg = 1;
71767834Stegge			}
71867834Stegge
71967834Stegge			aio.iov_base = (caddr_t) &ifctx->call;
72067834Stegge			aio.iov_len = sizeof(ifctx->call);
72183651Speter
72267834Stegge			auio.uio_iov = &aio;
72367834Stegge			auio.uio_iovcnt = 1;
72467834Stegge			auio.uio_segflg = UIO_SYSSPACE;
72567834Stegge			auio.uio_rw = UIO_WRITE;
72667834Stegge			auio.uio_offset = 0;
72767834Stegge			auio.uio_resid = sizeof(ifctx->call);
72883366Sjulian			auio.uio_td = td;
72983651Speter
73067834Stegge			/* Set netmask to 0.0.0.0 */
73167834Stegge			clear_sinaddr(sin);
732228455Sglebius			error = ifioctl(bootp_so, SIOCAIFADDR, (caddr_t)ifra,
733228455Sglebius			    td);
73467834Stegge			if (error != 0)
735228455Sglebius				panic("%s: SIOCAIFADDR, error=%d", __func__,
736228455Sglebius				    error);
73783651Speter
738228455Sglebius			error = sosend(bootp_so, (struct sockaddr *) &dst,
73983366Sjulian				       &auio, NULL, NULL, 0, td);
740228455Sglebius			if (error != 0)
741228455Sglebius				printf("%s: sosend: %d state %08x\n", __func__,
742228455Sglebius				    error, (int )bootp_so->so_state);
74367834Stegge
74467834Stegge			/* Set netmask to 255.0.0.0 */
745228455Sglebius			sin->sin_addr.s_addr = htonl(IN_CLASSA_NET);
746228455Sglebius			error = ifioctl(bootp_so, SIOCAIFADDR, (caddr_t)ifra,
747228455Sglebius			    td);
74867834Stegge			if (error != 0)
749228455Sglebius				panic("%s: SIOCAIFADDR, error=%d", __func__,
750228455Sglebius				    error);
75125723Stegge		}
75283651Speter
75367834Stegge		if (outstanding == 0 &&
75467834Stegge		    (rtimo == 0 || time_second >= rtimo)) {
75567834Stegge			error = 0;
756228455Sglebius			goto out;
75767834Stegge		}
75883651Speter
75925723Stegge		/* Determine new timeout. */
76025723Stegge		if (timo < MAX_RESEND_DELAY)
76125723Stegge			timo++;
76267834Stegge		else {
76371915Stegge			printf("DHCP/BOOTP timeout for server ");
76467834Stegge			print_sin_addr(&dst);
76567834Stegge			printf("\n");
76667834Stegge		}
76783651Speter
76825723Stegge		/*
76925723Stegge		 * Wait for up to timo seconds for a reply.
77025723Stegge		 * The socket receive timeout was set to 1 second.
77125723Stegge		 */
77267834Stegge		atimo = timo + time_second;
77367834Stegge		while (time_second < atimo) {
77467834Stegge			aio.iov_base = (caddr_t) &gctx->reply;
77567834Stegge			aio.iov_len = sizeof(gctx->reply);
77683651Speter
77725723Stegge			auio.uio_iov = &aio;
77825723Stegge			auio.uio_iovcnt = 1;
77925723Stegge			auio.uio_segflg = UIO_SYSSPACE;
78025723Stegge			auio.uio_rw = UIO_READ;
78125723Stegge			auio.uio_offset = 0;
78267834Stegge			auio.uio_resid = sizeof(gctx->reply);
78383366Sjulian			auio.uio_td = td;
78483651Speter
78525723Stegge			rcvflg = 0;
786228455Sglebius			error = soreceive(bootp_so, NULL, &auio,
78767531Stegge					  NULL, NULL, &rcvflg);
78867834Stegge			gctx->secs = time_second - gctx->starttime;
789228455Sglebius			STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
79071915Stegge				if (bootpc_ifctx_isresolved(ifctx) != 0 ||
79171915Stegge				    bootpc_ifctx_isfailed(ifctx) != 0)
79267834Stegge					continue;
79383651Speter
79467834Stegge				ifctx->call.secs = htons(gctx->secs);
79567834Stegge			}
79667834Stegge			if (error == EWOULDBLOCK)
79725723Stegge				continue;
79867834Stegge			if (error != 0)
79925723Stegge				goto out;
80067834Stegge			len = sizeof(gctx->reply) - auio.uio_resid;
80183651Speter
80232609Stegge			/* Do we have the required number of bytes ? */
80332609Stegge			if (len < BOOTP_MIN_LEN)
80425723Stegge				continue;
80567834Stegge			gctx->replylen = len;
80683651Speter
80767834Stegge			/* Is it a reply? */
80867834Stegge			if (gctx->reply.op != BOOTP_REPLY)
80967531Stegge				continue;
81083651Speter
81167834Stegge			/* Is this an answer to our query */
812228455Sglebius			STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
81367834Stegge				if (gctx->reply.xid != ifctx->call.xid)
81467834Stegge					continue;
81583651Speter
81667834Stegge				/* Same HW address size ? */
81767834Stegge				if (gctx->reply.hlen != ifctx->call.hlen)
81867834Stegge					continue;
81983651Speter
82067834Stegge				/* Correct HW address ? */
82167834Stegge				if (bcmp(gctx->reply.chaddr,
82267834Stegge					 ifctx->call.chaddr,
82367834Stegge					 ifctx->call.hlen) != 0)
82467834Stegge					continue;
82525723Stegge
82667834Stegge				break;
82767834Stegge			}
82825723Stegge
82967834Stegge			if (ifctx != NULL) {
83071915Stegge				s =  bootpc_tag(&gctx->tmptag,
83171915Stegge						&gctx->reply,
83271915Stegge						gctx->replylen,
83371915Stegge						TAG_DHCP_MSGTYPE);
83471915Stegge				if (s != NULL) {
83571915Stegge					switch (*s) {
83671915Stegge					case DHCP_OFFER:
83771915Stegge						s = "DHCP Offer";
83871915Stegge						break;
83971915Stegge					case DHCP_ACK:
84071915Stegge						s = "DHCP Ack";
84171915Stegge						break;
84271915Stegge					default:
84371915Stegge						s = "DHCP (unexpected)";
84471915Stegge						break;
84571915Stegge					}
84671915Stegge				} else
84771915Stegge					s = "BOOTP Reply";
84871915Stegge
84971915Stegge				printf("Received %s packet"
85067834Stegge				       " on %s from ",
85171915Stegge				       s,
85267834Stegge				       ifctx->ireq.ifr_name);
85367834Stegge				print_in_addr(gctx->reply.siaddr);
85467834Stegge				if (gctx->reply.giaddr.s_addr !=
85567834Stegge				    htonl(INADDR_ANY)) {
85667834Stegge					printf(" via ");
85767834Stegge					print_in_addr(gctx->reply.giaddr);
85867834Stegge				}
85967834Stegge				if (bootpc_received(gctx, ifctx) != 0) {
86067834Stegge					printf(" (accepted)");
86167834Stegge					if (ifctx->outstanding) {
86267834Stegge						ifctx->outstanding = 0;
86367834Stegge						outstanding--;
86467834Stegge					}
86567834Stegge					/* Network settle delay */
86667834Stegge					if (outstanding == 0)
86767834Stegge						atimo = time_second +
86867834Stegge							BOOTP_SETTLE_DELAY;
86967834Stegge				} else
87067834Stegge					printf(" (ignored)");
871253847Sian				if (ifctx->gotrootpath ||
872253847Sian				    gctx->any_root_overrides) {
87367834Stegge					gotrootpath = 1;
87467834Stegge					rtimo = time_second +
87567834Stegge						BOOTP_SETTLE_DELAY;
876253847Sian					if (ifctx->gotrootpath)
877253847Sian						printf(" (got root path)");
878253847Sian				}
87967834Stegge				printf("\n");
88067834Stegge			}
88125723Stegge		} /* while secs */
88267834Stegge#ifdef BOOTP_TIMEOUT
88367834Stegge		if (gctx->secs > BOOTP_TIMEOUT && BOOTP_TIMEOUT > 0)
88467834Stegge			break;
88567834Stegge#endif
88671915Stegge		/* Force a retry if halfway in DHCP negotiation */
88771915Stegge		retry = 0;
888228455Sglebius		STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
88971915Stegge			if (ifctx->state == IF_DHCP_OFFERED) {
89071915Stegge				if (ifctx->dhcpquerytype == DHCP_DISCOVER)
89171915Stegge					retry = 1;
89271915Stegge				else
89371915Stegge					ifctx->state = IF_DHCP_UNRESOLVED;
89471915Stegge			}
89583651Speter
89671915Stegge		if (retry != 0)
89771915Stegge			continue;
89883651Speter
89967834Stegge		if (gotrootpath != 0) {
90067834Stegge			gctx->gotrootpath = gotrootpath;
90167834Stegge			if (rtimo != 0 && time_second >= rtimo)
90267834Stegge				break;
90367834Stegge		}
90425723Stegge	} /* forever send/receive */
90583651Speter
90683651Speter	/*
90767834Stegge	 * XXX: These are errors of varying seriousness being silently
90867834Stegge	 * ignored
90967834Stegge	 */
91025723Stegge
911228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
91271915Stegge		if (bootpc_ifctx_isresolved(ifctx) == 0) {
91371915Stegge			printf("%s timeout for interface %s\n",
91471915Stegge			       ifctx->dhcpquerytype != DHCP_NOMSG ?
91571915Stegge			       "DHCP" : "BOOTP",
91667834Stegge			       ifctx->ireq.ifr_name);
91767834Stegge		}
918228455Sglebius
91967834Stegge	if (gctx->gotrootpath != 0) {
92067834Stegge#if 0
92167834Stegge		printf("Got a root path, ignoring remaining timeout\n");
92267834Stegge#endif
92367834Stegge		error = 0;
92467834Stegge		goto out;
92567834Stegge	}
926131840Sbrian#ifndef BOOTP_NFSROOT
927228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
928131840Sbrian		if (bootpc_ifctx_isresolved(ifctx) != 0) {
929131840Sbrian			error = 0;
930131840Sbrian			goto out;
93167834Stegge		}
932131840Sbrian#endif
93325723Stegge	error = ETIMEDOUT;
93483651Speter
93567531Steggeout:
936228455Sglebius	return (error);
93725723Stegge}
93825723Stegge
939228455Sglebiusstatic void
940228455Sglebiusbootpc_fakeup_interface(struct bootpc_ifcontext *ifctx, struct thread *td)
94125723Stegge{
942228455Sglebius	struct ifreq *ifr;
943228455Sglebius	struct in_aliasreq *ifra;
94467531Stegge	struct sockaddr_in *sin;
94567531Stegge	int error;
94625723Stegge
947228455Sglebius	ifr = &ifctx->ireq;
948228455Sglebius	ifra = &ifctx->iareq;
94983651Speter
95067531Stegge	/*
95167531Stegge	 * Bring up the interface.
95267531Stegge	 *
95367531Stegge	 * Get the old interface flags and or IFF_UP into them; if
95467531Stegge	 * IFF_UP set blindly, interface selection can be clobbered.
95567531Stegge	 */
956228455Sglebius	error = ifioctl(bootp_so, SIOCGIFFLAGS, (caddr_t)ifr, td);
95767834Stegge	if (error != 0)
958228455Sglebius		panic("%s: SIOCGIFFLAGS, error=%d", __func__, error);
959228455Sglebius	ifr->ifr_flags |= IFF_UP;
960228455Sglebius	error = ifioctl(bootp_so, SIOCSIFFLAGS, (caddr_t)ifr, td);
96167834Stegge	if (error != 0)
962228455Sglebius		panic("%s: SIOCSIFFLAGS, error=%d", __func__, error);
96383651Speter
96467531Stegge	/*
96567531Stegge	 * Do enough of ifconfig(8) so that the chosen interface
966228455Sglebius	 * can talk to the servers. Set address to 0.0.0.0/8 and
967228455Sglebius	 * broadcast address to local broadcast.
96867531Stegge	 */
969228455Sglebius	sin = (struct sockaddr_in *)&ifra->ifra_addr;
97067834Stegge	clear_sinaddr(sin);
971228455Sglebius	sin = (struct sockaddr_in *)&ifra->ifra_mask;
97267834Stegge	clear_sinaddr(sin);
973228455Sglebius	sin->sin_addr.s_addr = htonl(IN_CLASSA_NET);
974228455Sglebius	sin = (struct sockaddr_in *)&ifra->ifra_broadaddr;
97567834Stegge	clear_sinaddr(sin);
97667834Stegge	sin->sin_addr.s_addr = htonl(INADDR_BROADCAST);
977228455Sglebius	error = ifioctl(bootp_so, SIOCAIFADDR, (caddr_t)ifra, td);
97867834Stegge	if (error != 0)
979228455Sglebius		panic("%s: SIOCAIFADDR, error=%d", __func__, error);
980228455Sglebius}
98183651Speter
982228455Sglebiusstatic void
983228455Sglebiusbootpc_shutdown_interface(struct bootpc_ifcontext *ifctx, struct thread *td)
984228455Sglebius{
985228455Sglebius	struct ifreq *ifr;
986228455Sglebius	struct sockaddr_in *sin;
987228455Sglebius	int error;
98883651Speter
989228455Sglebius	ifr = &ifctx->ireq;
99083651Speter
991228455Sglebius	printf("Shutdown interface %s\n", ifctx->ireq.ifr_name);
992228455Sglebius	error = ifioctl(bootp_so, SIOCGIFFLAGS, (caddr_t)ifr, td);
993228455Sglebius	if (error != 0)
994228455Sglebius		panic("%s: SIOCGIFFLAGS, error=%d", __func__, error);
995228455Sglebius	ifr->ifr_flags &= ~IFF_UP;
996228455Sglebius	error = ifioctl(bootp_so, SIOCSIFFLAGS, (caddr_t)ifr, td);
997228455Sglebius	if (error != 0)
998228455Sglebius		panic("%s: SIOCSIFFLAGS, error=%d", __func__, error);
99983651Speter
1000228455Sglebius	sin = (struct sockaddr_in *) &ifr->ifr_addr;
1001228455Sglebius	clear_sinaddr(sin);
1002228455Sglebius	error = ifioctl(bootp_so, SIOCDIFADDR, (caddr_t) ifr, td);
1003228455Sglebius	if (error != 0)
1004228455Sglebius		panic("%s: SIOCDIFADDR, error=%d", __func__, error);
100525723Stegge}
100625723Stegge
100767531Steggestatic int
100867834Steggebootpc_adjust_interface(struct bootpc_ifcontext *ifctx,
100983651Speter    struct bootpc_globalcontext *gctx, struct thread *td)
101025723Stegge{
101167531Stegge	int error;
101267834Stegge	struct sockaddr_in defdst;
101367834Stegge	struct sockaddr_in defmask;
101467531Stegge	struct sockaddr_in *sin;
1015228455Sglebius	struct ifreq *ifr;
1016228455Sglebius	struct in_aliasreq *ifra;
101767834Stegge	struct sockaddr_in *myaddr;
101867834Stegge	struct sockaddr_in *netmask;
101967834Stegge	struct sockaddr_in *gw;
102025723Stegge
1021228455Sglebius	ifr = &ifctx->ireq;
1022228455Sglebius	ifra = &ifctx->iareq;
102367834Stegge	myaddr = &ifctx->myaddr;
102467834Stegge	netmask = &ifctx->netmask;
102567834Stegge	gw = &ifctx->gw;
102667834Stegge
102771915Stegge	if (bootpc_ifctx_isresolved(ifctx) == 0) {
102867834Stegge		/* Shutdown interfaces where BOOTP failed */
1029228455Sglebius		bootpc_shutdown_interface(ifctx, td);
1030228455Sglebius		return (0);
103167531Stegge	}
103225723Stegge
103367834Stegge	printf("Adjusted interface %s\n", ifctx->ireq.ifr_name);
103467531Stegge	/*
103567531Stegge	 * Do enough of ifconfig(8) so that the chosen interface
103667531Stegge	 * can talk to the servers.  (just set the address)
103767531Stegge	 */
1038228455Sglebius	sin = (struct sockaddr_in *) &ifr->ifr_addr;
1039228455Sglebius	clear_sinaddr(sin);
1040228455Sglebius	error = ifioctl(bootp_so, SIOCDIFADDR, (caddr_t) ifr, td);
104167531Stegge	if (error != 0)
1042228455Sglebius		panic("%s: SIOCDIFADDR, error=%d", __func__, error);
104383651Speter
1044228455Sglebius	bcopy(myaddr, &ifra->ifra_addr, sizeof(*myaddr));
1045228455Sglebius	bcopy(netmask, &ifra->ifra_mask, sizeof(*netmask));
1046228455Sglebius	clear_sinaddr(&ifra->ifra_broadaddr);
1047228455Sglebius	ifra->ifra_broadaddr.sin_addr.s_addr = myaddr->sin_addr.s_addr |
1048228455Sglebius	    ~netmask->sin_addr.s_addr;
104983651Speter
1050228455Sglebius	error = ifioctl(bootp_so, SIOCAIFADDR, (caddr_t)ifra, td);
105167531Stegge	if (error != 0)
1052228455Sglebius		panic("%s: SIOCAIFADDR, error=%d", __func__, error);
105383651Speter
105467531Stegge	/* Add new default route */
105525723Stegge
105667834Stegge	if (ifctx->gotgw != 0 || gctx->gotgw == 0) {
105767834Stegge		clear_sinaddr(&defdst);
105867834Stegge		clear_sinaddr(&defmask);
1059178888Sjulian		/* XXX MRT just table 0 */
1060178888Sjulian		error = rtrequest_fib(RTM_ADD,
1061231852Sbz		    (struct sockaddr *) &defdst, (struct sockaddr *) gw,
1062231852Sbz		    (struct sockaddr *) &defmask,
1063231852Sbz		    (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB);
106467834Stegge		if (error != 0) {
1065228455Sglebius			printf("%s: RTM_ADD, error=%d\n", __func__, error);
1066228455Sglebius			return (error);
106767834Stegge		}
106867531Stegge	}
106983651Speter
1070228455Sglebius	return (0);
107125723Stegge}
107225723Stegge
107367531Steggestatic int
1074126888Sbrookssetfs(struct sockaddr_in *addr, char *path, char *p,
1075126888Sbrooks    const struct in_addr *siaddr)
107625723Stegge{
1077126888Sbrooks
1078126888Sbrooks	if (getip(&p, &addr->sin_addr) == 0) {
1079126888Sbrooks		if (siaddr != NULL && *p == '/')
1080126888Sbrooks			bcopy(siaddr, &addr->sin_addr, sizeof(struct in_addr));
1081126888Sbrooks		else
1082126888Sbrooks			return 0;
1083126888Sbrooks	} else {
1084126888Sbrooks		if (*p != ':')
1085126888Sbrooks			return 0;
1086126888Sbrooks		p++;
1087126888Sbrooks	}
1088126888Sbrooks
1089126888Sbrooks	addr->sin_len = sizeof(struct sockaddr_in);
1090126888Sbrooks	addr->sin_family = AF_INET;
1091126888Sbrooks
1092126888Sbrooks	strlcpy(path, p, MNAMELEN);
1093126888Sbrooks	return 1;
1094126888Sbrooks}
1095126888Sbrooks
1096126888Sbrooksstatic int
1097126888Sbrooksgetip(char **ptr, struct in_addr *addr)
1098126888Sbrooks{
1099126888Sbrooks	char *p;
110067531Stegge	unsigned int ip;
110125804Stegge	int val;
110283651Speter
1103126888Sbrooks	p = *ptr;
110467531Stegge	ip = 0;
110567531Stegge	if (((val = getdec(&p)) < 0) || (val > 255))
110667531Stegge		return 0;
110725804Stegge	ip = val << 24;
110867531Stegge	if (*p != '.')
110967531Stegge		return 0;
111025804Stegge	p++;
111167531Stegge	if (((val = getdec(&p)) < 0) || (val > 255))
111267531Stegge		return 0;
111325804Stegge	ip |= (val << 16);
111467531Stegge	if (*p != '.')
111567531Stegge		return 0;
111625804Stegge	p++;
111767531Stegge	if (((val = getdec(&p)) < 0) || (val > 255))
111867531Stegge		return 0;
111925804Stegge	ip |= (val << 8);
112067531Stegge	if (*p != '.')
112167531Stegge		return 0;
112225804Stegge	p++;
112367531Stegge	if (((val = getdec(&p)) < 0) || (val > 255))
112467531Stegge		return 0;
112525804Stegge	ip |= val;
112683651Speter
1127126888Sbrooks	addr->s_addr = htonl(ip);
1128126888Sbrooks	*ptr = p;
112967531Stegge	return 1;
113025804Stegge}
113125804Stegge
113267531Steggestatic int
113367531Steggegetdec(char **ptr)
113425804Stegge{
113567531Stegge	char *p;
113667531Stegge	int ret;
113767531Stegge
113867531Stegge	p = *ptr;
113967531Stegge	ret = 0;
114067531Stegge	if ((*p < '0') || (*p > '9'))
114167531Stegge		return -1;
114225804Stegge	while ((*p >= '0') && (*p <= '9')) {
114367531Stegge		ret = ret * 10 + (*p - '0');
114425804Stegge		p++;
114525723Stegge	}
114625804Stegge	*ptr = p;
114767531Stegge	return ret;
114825723Stegge}
114925723Stegge
115067531Steggestatic void
115167531Steggemountopts(struct nfs_args *args, char *p)
115225804Stegge{
115367834Stegge	args->version = NFS_ARGSVERSION;
1154179039Sbenno	args->rsize = BOOTP_BLOCKSIZE;
1155179039Sbenno	args->wsize = BOOTP_BLOCKSIZE;
115625804Stegge	args->flags = NFSMNT_RSIZE | NFSMNT_WSIZE | NFSMNT_RESVPORT;
115725804Stegge	args->sotype = SOCK_DGRAM;
1158164934Ssam	if (p != NULL)
1159164934Ssam		nfs_parse_options(p, args);
116025804Stegge}
116125804Stegge
116267531Steggestatic int
116367531Steggexdr_opaque_decode(struct mbuf **mptr, u_char *buf, int len)
116426581Stegge{
116567531Stegge	struct mbuf *m;
116667531Stegge	int alignedlen;
116783651Speter
116867531Stegge	m = *mptr;
116967531Stegge	alignedlen = ( len + 3 ) & ~3;
117083651Speter
117167531Stegge	if (m->m_len < alignedlen) {
117267531Stegge		m = m_pullup(m, alignedlen);
117367531Stegge		if (m == NULL) {
117467531Stegge			*mptr = NULL;
117567531Stegge			return EBADRPC;
117667531Stegge		}
117767531Stegge	}
117867531Stegge	bcopy(mtod(m, u_char *), buf, len);
117967531Stegge	m_adj(m, alignedlen);
118067531Stegge	*mptr = m;
118167531Stegge	return 0;
118267531Stegge}
118326581Stegge
118467531Steggestatic int
118567531Steggexdr_int_decode(struct mbuf **mptr, int *iptr)
118626581Stegge{
118767531Stegge	u_int32_t i;
118883651Speter
118967531Stegge	if (xdr_opaque_decode(mptr, (u_char *) &i, sizeof(u_int32_t)) != 0)
119067531Stegge		return EBADRPC;
119167531Stegge	*iptr = fxdr_unsigned(u_int32_t, i);
119267531Stegge	return 0;
119326581Stegge}
119426581Stegge
119567531Steggestatic void
119667834Steggeprint_sin_addr(struct sockaddr_in *sin)
119726581Stegge{
119883651Speter
119967834Stegge	print_in_addr(sin->sin_addr);
120067834Stegge}
120167834Stegge
120267834Steggestatic void
120367834Steggeprint_in_addr(struct in_addr addr)
120467834Stegge{
120567531Stegge	unsigned int ip;
120683651Speter
120767531Stegge	ip = ntohl(addr.s_addr);
120867834Stegge	printf("%d.%d.%d.%d",
120967531Stegge	       ip >> 24, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
121026581Stegge}
121126581Stegge
121267834Steggestatic void
1213228455Sglebiusbootpc_compose_query(struct bootpc_ifcontext *ifctx, struct thread *td)
121467834Stegge{
121571915Stegge	unsigned char *vendp;
121696825Sambrisko	unsigned char vendor_client[64];
121771915Stegge	uint32_t leasetime;
121896825Sambrisko	uint8_t vendor_client_len;
121971915Stegge
122067834Stegge	ifctx->gotrootpath = 0;
122171915Stegge
122267834Stegge	bzero((caddr_t) &ifctx->call, sizeof(ifctx->call));
122383651Speter
122467834Stegge	/* bootpc part */
122567834Stegge	ifctx->call.op = BOOTP_REQUEST; 	/* BOOTREQUEST */
122667834Stegge	ifctx->call.htype = 1;			/* 10mb ethernet */
122767834Stegge	ifctx->call.hlen = ifctx->sdl->sdl_alen;/* Hardware address length */
122867834Stegge	ifctx->call.hops = 0;
122971915Stegge	if (bootpc_ifctx_isunresolved(ifctx) != 0)
123071915Stegge		ifctx->xid++;
123167834Stegge	ifctx->call.xid = txdr_unsigned(ifctx->xid);
123267834Stegge	bcopy(LLADDR(ifctx->sdl), &ifctx->call.chaddr, ifctx->sdl->sdl_alen);
123383651Speter
123471915Stegge	vendp = ifctx->call.vend;
123571915Stegge	*vendp++ = 99;		/* RFC1048 cookie */
123671915Stegge	*vendp++ = 130;
123771915Stegge	*vendp++ = 83;
123871915Stegge	*vendp++ = 99;
123971915Stegge	*vendp++ = TAG_MAXMSGSIZE;
124071915Stegge	*vendp++ = 2;
124171915Stegge	*vendp++ = (sizeof(struct bootp_packet) >> 8) & 255;
124271915Stegge	*vendp++ = sizeof(struct bootp_packet) & 255;
124396825Sambrisko
124496825Sambrisko	snprintf(vendor_client, sizeof(vendor_client), "%s:%s:%s",
124596825Sambrisko		ostype, MACHINE, osrelease);
124696825Sambrisko	vendor_client_len = strlen(vendor_client);
124796825Sambrisko	*vendp++ = TAG_VENDOR_INDENTIFIER;
124896825Sambrisko	*vendp++ = vendor_client_len;
124996825Sambrisko	memcpy(vendp, vendor_client, vendor_client_len);
1250201758Smbr	vendp += vendor_client_len;
125171915Stegge	ifctx->dhcpquerytype = DHCP_NOMSG;
125271915Stegge	switch (ifctx->state) {
125371915Stegge	case IF_DHCP_UNRESOLVED:
125471915Stegge		*vendp++ = TAG_DHCP_MSGTYPE;
125571915Stegge		*vendp++ = 1;
125671915Stegge		*vendp++ = DHCP_DISCOVER;
125771915Stegge		ifctx->dhcpquerytype = DHCP_DISCOVER;
125871915Stegge		ifctx->gotdhcpserver = 0;
125971915Stegge		break;
126071915Stegge	case IF_DHCP_OFFERED:
126171915Stegge		*vendp++ = TAG_DHCP_MSGTYPE;
126271915Stegge		*vendp++ = 1;
126371915Stegge		*vendp++ = DHCP_REQUEST;
126471915Stegge		ifctx->dhcpquerytype = DHCP_REQUEST;
126571915Stegge		*vendp++ = TAG_DHCP_REQ_ADDR;
126671915Stegge		*vendp++ = 4;
126771915Stegge		memcpy(vendp, &ifctx->reply.yiaddr, 4);
126871915Stegge		vendp += 4;
126971915Stegge		if (ifctx->gotdhcpserver != 0) {
127071915Stegge			*vendp++ = TAG_DHCP_SERVERID;
127171915Stegge			*vendp++ = 4;
127271915Stegge			memcpy(vendp, &ifctx->dhcpserver, 4);
127371915Stegge			vendp += 4;
127471915Stegge		}
127571915Stegge		*vendp++ = TAG_DHCP_LEASETIME;
127671915Stegge		*vendp++ = 4;
127771915Stegge		leasetime = htonl(300);
127871915Stegge		memcpy(vendp, &leasetime, 4);
127971915Stegge		vendp += 4;
1280115533Sphk		break;
128171915Stegge	default:
1282115533Sphk		break;
128371915Stegge	}
128471915Stegge	*vendp = TAG_END;
128583651Speter
128667834Stegge	ifctx->call.secs = 0;
1287108470Sschweikh	ifctx->call.flags = htons(0x8000); /* We need a broadcast answer */
128867834Stegge}
128967531Stegge
129067834Steggestatic int
129167834Steggebootpc_hascookie(struct bootp_packet *bp)
129225723Stegge{
129383651Speter
129467834Stegge	return (bp->vend[0] == 99 && bp->vend[1] == 130 &&
129567834Stegge		bp->vend[2] == 83 && bp->vend[3] == 99);
129667834Stegge}
129767834Stegge
129867834Steggestatic void
129967834Steggebootpc_tag_helper(struct bootpc_tagcontext *tctx,
130083651Speter    unsigned char *start, int len, int tag)
130167834Stegge{
130267834Stegge	unsigned char *j;
130367834Stegge	unsigned char *ej;
130467834Stegge	unsigned char code;
130567834Stegge
130667834Stegge	if (tctx->badtag != 0 || tctx->badopt != 0)
130767834Stegge		return;
130883651Speter
130967834Stegge	j = start;
131067834Stegge	ej = j + len;
131183651Speter
131267834Stegge	while (j < ej) {
131367834Stegge		code = *j++;
131467834Stegge		if (code == TAG_PAD)
131567834Stegge			continue;
131667834Stegge		if (code == TAG_END)
131767834Stegge			return;
131867834Stegge		if (j >= ej || j + *j + 1 > ej) {
131967834Stegge			tctx->badopt = 1;
132067834Stegge			return;
132167834Stegge		}
132267834Stegge		len = *j++;
132367834Stegge		if (code == tag) {
132467834Stegge			if (tctx->taglen + len > TAG_MAXLEN) {
132567834Stegge				tctx->badtag = 1;
132667834Stegge				return;
132767834Stegge			}
132867834Stegge			tctx->foundopt = 1;
132967834Stegge			if (len > 0)
133067834Stegge				memcpy(tctx->buf + tctx->taglen,
133167834Stegge				       j, len);
133267834Stegge			tctx->taglen += len;
133367834Stegge		}
133467834Stegge		if (code == TAG_OVERLOAD)
133567834Stegge			tctx->overload = *j;
133683651Speter
133767834Stegge		j += len;
133867834Stegge	}
133967834Stegge}
134067834Stegge
134167834Steggestatic unsigned char *
134267834Steggebootpc_tag(struct bootpc_tagcontext *tctx,
134383651Speter    struct bootp_packet *bp, int len, int tag)
134467834Stegge{
134567834Stegge	tctx->overload = 0;
134667834Stegge	tctx->badopt = 0;
134767834Stegge	tctx->badtag = 0;
134867834Stegge	tctx->foundopt = 0;
134967834Stegge	tctx->taglen = 0;
135067834Stegge
135167834Stegge	if (bootpc_hascookie(bp) == 0)
135267834Stegge		return NULL;
135383651Speter
135467834Stegge	bootpc_tag_helper(tctx, &bp->vend[4],
135567834Stegge			  (unsigned char *) bp + len - &bp->vend[4], tag);
135683651Speter
135767834Stegge	if ((tctx->overload & OVERLOAD_FILE) != 0)
135867834Stegge		bootpc_tag_helper(tctx,
135967834Stegge				  (unsigned char *) bp->file,
136067834Stegge				  sizeof(bp->file),
136167834Stegge				  tag);
136267834Stegge	if ((tctx->overload & OVERLOAD_SNAME) != 0)
136367834Stegge		bootpc_tag_helper(tctx,
136467834Stegge				  (unsigned char *) bp->sname,
136567834Stegge				  sizeof(bp->sname),
136667834Stegge				  tag);
136783651Speter
136867834Stegge	if (tctx->badopt != 0 || tctx->badtag != 0 || tctx->foundopt == 0)
136967834Stegge		return NULL;
137067834Stegge	tctx->buf[tctx->taglen] = '\0';
137167834Stegge	return tctx->buf;
137267834Stegge}
137367834Stegge
137467834Steggestatic void
137583651Speterbootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx,
137683651Speter    struct bootpc_globalcontext *gctx)
137767834Stegge{
1378253847Sian	char *p, *s;
137967531Stegge	unsigned int ip;
138025723Stegge
138167834Stegge	ifctx->gotgw = 0;
138267834Stegge	ifctx->gotnetmask = 0;
138325723Stegge
138467834Stegge	clear_sinaddr(&ifctx->myaddr);
138567834Stegge	clear_sinaddr(&ifctx->netmask);
138667834Stegge	clear_sinaddr(&ifctx->gw);
138783651Speter
138867834Stegge	ifctx->myaddr.sin_addr = ifctx->reply.yiaddr;
138983651Speter
139067834Stegge	ip = ntohl(ifctx->myaddr.sin_addr.s_addr);
139183651Speter
139267834Stegge	printf("%s at ", ifctx->ireq.ifr_name);
139367834Stegge	print_sin_addr(&ifctx->myaddr);
139467834Stegge	printf(" server ");
139567834Stegge	print_in_addr(ifctx->reply.siaddr);
139683651Speter
139767834Stegge	ifctx->gw.sin_addr = ifctx->reply.giaddr;
139867834Stegge	if (ifctx->reply.giaddr.s_addr != htonl(INADDR_ANY)) {
139967834Stegge		printf(" via gateway ");
140067834Stegge		print_in_addr(ifctx->reply.giaddr);
140167834Stegge	}
140225723Stegge
140367834Stegge	/* This call used for the side effect (overload flag) */
140467834Stegge	(void) bootpc_tag(&gctx->tmptag,
140567834Stegge			  &ifctx->reply, ifctx->replylen, TAG_END);
140683651Speter
140767834Stegge	if ((gctx->tmptag.overload & OVERLOAD_SNAME) == 0)
140867834Stegge		if (ifctx->reply.sname[0] != '\0')
140967834Stegge			printf(" server name %s", ifctx->reply.sname);
141067834Stegge	if ((gctx->tmptag.overload & OVERLOAD_FILE) == 0)
141167834Stegge		if (ifctx->reply.file[0] != '\0')
141267834Stegge			printf(" boot file %s", ifctx->reply.file);
141383651Speter
141467834Stegge	printf("\n");
141567834Stegge
141667834Stegge	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
141767834Stegge		       TAG_SUBNETMASK);
141867834Stegge	if (p != NULL) {
141967834Stegge		if (gctx->tag.taglen != 4)
142067834Stegge			panic("bootpc: subnet mask len is %d",
142167834Stegge			      gctx->tag.taglen);
142267834Stegge		bcopy(p, &ifctx->netmask.sin_addr, 4);
142367834Stegge		ifctx->gotnetmask = 1;
142467834Stegge		printf("subnet mask ");
142567834Stegge		print_sin_addr(&ifctx->netmask);
142667834Stegge		printf(" ");
142767834Stegge	}
142883651Speter
142967834Stegge	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
143067834Stegge		       TAG_ROUTERS);
143167834Stegge	if (p != NULL) {
143267834Stegge		/* Routers */
143367834Stegge		if (gctx->tag.taglen % 4)
143467834Stegge			panic("bootpc: Router Len is %d", gctx->tag.taglen);
143567834Stegge		if (gctx->tag.taglen > 0) {
143667834Stegge			bcopy(p, &ifctx->gw.sin_addr, 4);
143767834Stegge			printf("router ");
143867834Stegge			print_sin_addr(&ifctx->gw);
143967834Stegge			printf(" ");
144067834Stegge			ifctx->gotgw = 1;
144167834Stegge			gctx->gotgw = 1;
144267834Stegge		}
144367834Stegge	}
144483651Speter
1445253847Sian	/*
1446253847Sian	 * Choose a root filesystem.  If a value is forced in the environment
1447253847Sian	 * and it contains "nfs:", use it unconditionally.  Otherwise, if the
1448253847Sian	 * kernel is compiled with the ROOTDEVNAME option, then use it if:
1449253847Sian	 *  - The server doesn't provide a pathname.
1450253847Sian	 *  - The boothowto flags include RB_DFLTROOT (user said to override
1451253847Sian	 *    the server value).
1452253847Sian	 */
1453253847Sian	p = NULL;
1454253847Sian	if ((s = getenv("vfs.root.mountfrom")) != NULL) {
1455253847Sian		if ((p = strstr(s, "nfs:")) != NULL)
1456253847Sian			p = strdup(p + 4, M_TEMP);
1457253847Sian		freeenv(s);
1458253847Sian	}
1459253847Sian	if (p == NULL) {
1460253847Sian		p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
146167834Stegge		       TAG_ROOT);
1462253847Sian	}
1463253847Sian#ifdef ROOTDEVNAME
1464253847Sian	if ((p == NULL || (boothowto & RB_DFLTROOT) != 0) &&
1465253847Sian	    (p = strstr(ROOTDEVNAME, "nfs:")) != NULL) {
1466253847Sian		p += 4;
1467253847Sian	}
1468253847Sian#endif
146967834Stegge	if (p != NULL) {
147067834Stegge		if (gctx->setrootfs != NULL) {
147167834Stegge			printf("rootfs %s (ignored) ", p);
147267834Stegge		} else 	if (setfs(&nd->root_saddr,
1473126888Sbrooks				  nd->root_hostnam, p, &ifctx->reply.siaddr)) {
1474126888Sbrooks			if (*p == '/') {
1475126888Sbrooks				printf("root_server ");
1476126888Sbrooks				print_sin_addr(&nd->root_saddr);
1477126888Sbrooks				printf(" ");
1478126888Sbrooks			}
147983651Speter			printf("rootfs %s ", p);
148067834Stegge			gctx->gotrootpath = 1;
148167834Stegge			ifctx->gotrootpath = 1;
148267834Stegge			gctx->setrootfs = ifctx;
148383651Speter
148467834Stegge			p = bootpc_tag(&gctx->tag, &ifctx->reply,
148567834Stegge				       ifctx->replylen,
148667834Stegge				       TAG_ROOTOPTS);
148767834Stegge			if (p != NULL) {
148867834Stegge				mountopts(&nd->root_args, p);
148967834Stegge				printf("rootopts %s ", p);
149067834Stegge			}
149167834Stegge		} else
149283651Speter			panic("Failed to set rootfs to %s", p);
149367834Stegge	}
149467834Stegge
149567834Stegge	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
149667834Stegge		       TAG_HOSTNAME);
149767834Stegge	if (p != NULL) {
149867834Stegge		if (gctx->tag.taglen >= MAXHOSTNAMELEN)
149967834Stegge			panic("bootpc: hostname >= %d bytes",
150067834Stegge			      MAXHOSTNAMELEN);
150167834Stegge		if (gctx->sethostname != NULL) {
150267834Stegge			printf("hostname %s (ignored) ", p);
150367834Stegge		} else {
150467834Stegge			strcpy(nd->my_hostnam, p);
1505193066Sjamie			mtx_lock(&prison0.pr_mtx);
1506194118Sjamie			strcpy(prison0.pr_hostname, p);
1507193066Sjamie			mtx_unlock(&prison0.pr_mtx);
1508193066Sjamie			printf("hostname %s ", p);
150967834Stegge			gctx->sethostname = ifctx;
151067834Stegge		}
151167834Stegge	}
151292219Sluigi	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
151392219Sluigi			TAG_COOKIE);
151492219Sluigi	if (p != NULL) {        /* store in a sysctl variable */
151592219Sluigi		int i, l = sizeof(bootp_cookie) - 1;
151692219Sluigi		for (i = 0; i < l && p[i] != '\0'; i++)
151792219Sluigi			bootp_cookie[i] = p[i];
151892219Sluigi		p[i] = '\0';
151992219Sluigi	}
152067834Stegge
152192219Sluigi
152267834Stegge	printf("\n");
152383651Speter
152467834Stegge	if (ifctx->gotnetmask == 0) {
152567834Stegge		if (IN_CLASSA(ntohl(ifctx->myaddr.sin_addr.s_addr)))
152667834Stegge			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSA_NET);
152767834Stegge		else if (IN_CLASSB(ntohl(ifctx->myaddr.sin_addr.s_addr)))
152867834Stegge			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSB_NET);
152967834Stegge		else
153067834Stegge			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET);
153167834Stegge	}
153267834Stegge	if (ifctx->gotgw == 0) {
153367834Stegge		/* Use proxyarp */
153467834Stegge		ifctx->gw.sin_addr.s_addr = ifctx->myaddr.sin_addr.s_addr;
153567834Stegge	}
153667834Stegge}
153767834Stegge
153867834Steggevoid
153967834Steggebootpc_init(void)
154067834Stegge{
1541228455Sglebius	struct bootpc_ifcontext *ifctx;		/* Interface BOOTP contexts */
154267834Stegge	struct bootpc_globalcontext *gctx; 	/* Global BOOTP context */
154367834Stegge	struct ifnet *ifp;
1544228455Sglebius	struct sockaddr_dl *sdl;
1545228455Sglebius	struct ifaddr *ifa;
1546131840Sbrian	int error;
1547131840Sbrian#ifndef BOOTP_WIRED_TO
1548118639Sbillf	int ifcnt;
1549131840Sbrian#endif
155067531Stegge	struct nfsv3_diskless *nd;
155183366Sjulian	struct thread *td;
1552239337Sgonzo	int timeout;
1553239337Sgonzo	int delay;
155425723Stegge
1555239337Sgonzo	timeout = BOOTP_IFACE_WAIT_TIMEOUT * hz;
1556239337Sgonzo	delay = hz / 10;
1557239337Sgonzo
155867531Stegge	nd = &nfsv3_diskless;
155983366Sjulian	td = curthread;
156083651Speter
156167531Stegge	/*
156267531Stegge	 * If already filled in, don't touch it here
156367531Stegge	 */
156467531Stegge	if (nfs_diskless_valid != 0)
156567531Stegge		return;
156625723Stegge
1567118639Sbillf	gctx = malloc(sizeof(*gctx), M_TEMP, M_WAITOK | M_ZERO);
1568228455Sglebius	STAILQ_INIT(&gctx->interfaces);
156967834Stegge	gctx->xid = ~0xFFFF;
157067834Stegge	gctx->starttime = time_second;
157183651Speter
157267531Stegge	/*
1573253847Sian	 * If ROOTDEVNAME is defined or vfs.root.mountfrom is set then we have
1574253847Sian	 * root-path overrides that can potentially let us boot even if we don't
1575253847Sian	 * get a root path from the server, so we can treat that as a non-error.
1576253847Sian	 */
1577253847Sian#ifdef ROOTDEVNAME
1578253847Sian	gctx->any_root_overrides = 1;
1579253847Sian#else
1580253847Sian	gctx->any_root_overrides = testenv("vfs.root.mountfrom");
1581253847Sian#endif
1582253847Sian
1583253847Sian	/*
158467531Stegge	 * Find a network interface.
158567531Stegge	 */
1586218757Sbz	CURVNET_SET(TD_TO_VNET(td));
1587131840Sbrian#ifdef BOOTP_WIRED_TO
1588228455Sglebius	printf("%s: wired to interface '%s'\n", __func__,
1589131840Sbrian	       __XSTRING(BOOTP_WIRED_TO));
1590131840Sbrian	allocifctx(gctx);
1591131840Sbrian#else
1592131840Sbrian	/*
1593131840Sbrian	 * Preallocate interface context storage, if another interface
1594131840Sbrian	 * attaches and wins the race, it won't be eligible for bootp.
1595131840Sbrian	 */
1596228455Sglebius	ifcnt = 0;
1597131840Sbrian	IFNET_RLOCK();
1598228455Sglebius	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
1599131840Sbrian		if ((ifp->if_flags &
1600131840Sbrian		     (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_BROADCAST)) !=
1601131840Sbrian		    IFF_BROADCAST)
1602131840Sbrian			continue;
1603228455Sglebius		switch (ifp->if_alloctype) {
1604228455Sglebius			case IFT_ETHER:
1605228455Sglebius			case IFT_FDDI:
1606228455Sglebius			case IFT_ISO88025:
1607228455Sglebius				break;
1608228455Sglebius			default:
1609228455Sglebius				continue;
1610228455Sglebius		}
1611131840Sbrian		ifcnt++;
1612131840Sbrian	}
1613131840Sbrian	IFNET_RUNLOCK();
1614131840Sbrian	if (ifcnt == 0)
1615228455Sglebius		panic("%s: no eligible interfaces", __func__);
1616131840Sbrian	for (; ifcnt > 0; ifcnt--)
1617131814Sbrian		allocifctx(gctx);
1618131840Sbrian#endif
1619118639Sbillf
1620239318Sgonzoretry:
1621228455Sglebius	ifctx = STAILQ_FIRST(&gctx->interfaces);
1622118639Sbillf	IFNET_RLOCK();
1623228455Sglebius	TAILQ_FOREACH(ifp, &V_ifnet, if_link) {
1624228455Sglebius		if (ifctx == NULL)
1625228455Sglebius			break;
1626131840Sbrian#ifdef BOOTP_WIRED_TO
1627228455Sglebius		if (strcmp(ifp->if_xname, __XSTRING(BOOTP_WIRED_TO)) != 0)
1628131840Sbrian			continue;
1629131840Sbrian#else
1630131840Sbrian		if ((ifp->if_flags &
1631131840Sbrian		     (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_BROADCAST)) !=
163267834Stegge		    IFF_BROADCAST)
163367834Stegge			continue;
1634228455Sglebius		switch (ifp->if_alloctype) {
1635228455Sglebius			case IFT_ETHER:
1636228455Sglebius			case IFT_FDDI:
1637228455Sglebius			case IFT_ISO88025:
1638228455Sglebius				break;
1639228455Sglebius			default:
1640228455Sglebius				continue;
1641228455Sglebius		}
1642131840Sbrian#endif
1643228455Sglebius		strlcpy(ifctx->ireq.ifr_name, ifp->if_xname,
1644228455Sglebius		    sizeof(ifctx->ireq.ifr_name));
164567834Stegge		ifctx->ifp = ifp;
1646228455Sglebius
1647228455Sglebius		/* Get HW address */
1648228455Sglebius		sdl = NULL;
1649228455Sglebius		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
1650228455Sglebius			if (ifa->ifa_addr->sa_family == AF_LINK) {
1651228455Sglebius				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
1652228455Sglebius				if (sdl->sdl_type == IFT_ETHER)
1653228455Sglebius					break;
1654228455Sglebius			}
1655228455Sglebius		if (sdl == NULL)
1656228455Sglebius			panic("bootpc: Unable to find HW address for %s",
1657228455Sglebius			    ifctx->ireq.ifr_name);
1658228455Sglebius		ifctx->sdl = sdl;
1659228455Sglebius
1660228455Sglebius		ifctx = STAILQ_NEXT(ifctx, next);
166167531Stegge	}
1662108172Shsu	IFNET_RUNLOCK();
1663218757Sbz	CURVNET_RESTORE();
166483651Speter
1665228455Sglebius	if (STAILQ_EMPTY(&gctx->interfaces) ||
1666228455Sglebius	    STAILQ_FIRST(&gctx->interfaces)->ifp == NULL) {
1667239318Sgonzo		if (timeout > 0) {
1668239318Sgonzo			pause("bootpc", delay);
1669239318Sgonzo			timeout -= delay;
1670239318Sgonzo			goto retry;
1671239318Sgonzo		}
1672131840Sbrian#ifdef BOOTP_WIRED_TO
1673228455Sglebius		panic("%s: Could not find interface specified "
1674131840Sbrian		      "by BOOTP_WIRED_TO: "
1675228455Sglebius		      __XSTRING(BOOTP_WIRED_TO), __func__);
1676131840Sbrian#else
1677228455Sglebius		panic("%s: no suitable interface", __func__);
1678131840Sbrian#endif
167967531Stegge	}
168083651Speter
1681228455Sglebius	error = socreate(AF_INET, &bootp_so, SOCK_DGRAM, 0, td->td_ucred, td);
1682228455Sglebius	if (error != 0)
1683228455Sglebius		panic("%s: socreate, error=%d", __func__, error);
168483651Speter
1685228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
1686228455Sglebius		bootpc_fakeup_interface(ifctx, td);
168783651Speter
1688228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
1689228455Sglebius		bootpc_compose_query(ifctx, td);
1690228455Sglebius
169183366Sjulian	error = bootpc_call(gctx, td);
169267531Stegge	if (error != 0) {
1693131840Sbrian		printf("BOOTP call failed\n");
169467531Stegge	}
169583651Speter
169667834Stegge	mountopts(&nd->root_args, NULL);
169783651Speter
1698228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
169971915Stegge		if (bootpc_ifctx_isresolved(ifctx) != 0)
170067834Stegge			bootpc_decode_reply(nd, ifctx, gctx);
170183651Speter
1702131840Sbrian#ifdef BOOTP_NFSROOT
1703253847Sian	if (gctx->gotrootpath == 0 && gctx->any_root_overrides == 0)
170467531Stegge		panic("bootpc: No root path offered");
1705131840Sbrian#endif
170683651Speter
1707228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
170883366Sjulian		bootpc_adjust_interface(ifctx, gctx, td);
170983651Speter
1710228455Sglebius	soclose(bootp_so);
171125723Stegge
1712228455Sglebius	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
171367834Stegge		if (ifctx->gotrootpath != 0)
171467834Stegge			break;
171567834Stegge	if (ifctx == NULL) {
1716228455Sglebius		STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
171771915Stegge			if (bootpc_ifctx_isresolved(ifctx) != 0)
171867834Stegge				break;
171967531Stegge	}
172067834Stegge	if (ifctx == NULL)
172167834Stegge		goto out;
172283651Speter
172367834Stegge	if (gctx->gotrootpath != 0) {
172483651Speter
1725145570Sdes		setenv("boot.netif.name", ifctx->ifp->if_xname);
1726145570Sdes
172767531Stegge		error = md_mount(&nd->root_saddr, nd->root_hostnam,
172867531Stegge				 nd->root_fh, &nd->root_fhsize,
172983366Sjulian				 &nd->root_args, td);
1730253847Sian		if (error != 0) {
1731253847Sian			if (gctx->any_root_overrides == 0)
1732253847Sian				panic("nfs_boot: mount root, error=%d", error);
1733253847Sian			else
1734253847Sian				goto out;
1735253847Sian		}
1736253847Sian		rootdevnames[0] = "nfs:";
1737253847Sian#ifdef NFSCLIENT
1738253847Sian		rootdevnames[1] = "oldnfs:";
1739253847Sian#endif
174067531Stegge		nfs_diskless_valid = 3;
174167531Stegge	}
174283651Speter
174367834Stegge	strcpy(nd->myif.ifra_name, ifctx->ireq.ifr_name);
174467834Stegge	bcopy(&ifctx->myaddr, &nd->myif.ifra_addr, sizeof(ifctx->myaddr));
174567834Stegge	bcopy(&ifctx->myaddr, &nd->myif.ifra_broadaddr, sizeof(ifctx->myaddr));
174667531Stegge	((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr =
174767834Stegge		ifctx->myaddr.sin_addr.s_addr |
174867834Stegge		~ ifctx->netmask.sin_addr.s_addr;
174967834Stegge	bcopy(&ifctx->netmask, &nd->myif.ifra_mask, sizeof(ifctx->netmask));
175083651Speter
175167834Steggeout:
1752228455Sglebius	while((ifctx = STAILQ_FIRST(&gctx->interfaces)) != NULL) {
1753228455Sglebius		STAILQ_REMOVE_HEAD(&gctx->interfaces, next);
175467834Stegge		free(ifctx, M_TEMP);
175567834Stegge	}
175667834Stegge	free(gctx, M_TEMP);
175725723Stegge}
175825723Stegge
175925723Stegge/*
176025723Stegge * RPC: mountd/mount
176125723Stegge * Given a server pathname, get an NFS file handle.
176225723Stegge * Also, sets sin->sin_port to the NFS service port.
176325723Stegge */
176425723Steggestatic int
176583651Spetermd_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp, int *fhsizep,
176683651Speter    struct nfs_args *args, struct thread *td)
176725723Stegge{
176825723Stegge	struct mbuf *m;
176925723Stegge	int error;
177025723Stegge	int authunixok;
177125723Stegge	int authcount;
177225723Stegge	int authver;
177383651Speter
1774195202Sdfr#define	RPCPROG_MNT	100005
1775195202Sdfr#define	RPCMNT_VER1	1
1776195202Sdfr#define RPCMNT_VER3	3
1777195202Sdfr#define	RPCMNT_MOUNT	1
1778195202Sdfr#define	AUTH_SYS	1		/* unix style (uid, gids) */
1779195202Sdfr#define AUTH_UNIX	AUTH_SYS
1780195202Sdfr
1781164934Ssam	/* XXX honor v2/v3 flags in args->flags? */
1782131840Sbrian#ifdef BOOTP_NFSV3
1783131840Sbrian	/* First try NFS v3 */
1784131840Sbrian	/* Get port number for MOUNTD. */
1785131840Sbrian	error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3,
1786131840Sbrian			     &mdsin->sin_port, td);
1787131840Sbrian	if (error == 0) {
1788131840Sbrian		m = xdr_string_encode(path, strlen(path));
178983651Speter
1790131840Sbrian		/* Do RPC to mountd. */
1791131840Sbrian		error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3,
1792131840Sbrian				  RPCMNT_MOUNT, &m, NULL, td);
1793131840Sbrian	}
1794131840Sbrian	if (error == 0) {
1795131840Sbrian		args->flags |= NFSMNT_NFSV3;
1796131840Sbrian	} else {
1797131840Sbrian#endif
179867531Stegge		/* Fallback to NFS v2 */
179983651Speter
180067531Stegge		/* Get port number for MOUNTD. */
180167531Stegge		error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
180283366Sjulian				     &mdsin->sin_port, td);
180367531Stegge		if (error != 0)
180467531Stegge			return error;
180583651Speter
180667531Stegge		m = xdr_string_encode(path, strlen(path));
180783651Speter
180867531Stegge		/* Do RPC to mountd. */
180967531Stegge		error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
181083366Sjulian				  RPCMNT_MOUNT, &m, NULL, td);
181167531Stegge		if (error != 0)
181267531Stegge			return error;	/* message already freed */
1813131840Sbrian
1814131840Sbrian#ifdef BOOTP_NFSV3
181525723Stegge	}
1816131840Sbrian#endif
181725723Stegge
181867531Stegge	if (xdr_int_decode(&m, &error) != 0 || error != 0)
181967531Stegge		goto bad;
182083651Speter
182167531Stegge	if ((args->flags & NFSMNT_NFSV3) != 0) {
182267531Stegge		if (xdr_int_decode(&m, fhsizep) != 0 ||
182367531Stegge		    *fhsizep > NFSX_V3FHMAX ||
182467531Stegge		    *fhsizep <= 0)
182567531Stegge			goto bad;
182667531Stegge	} else
182767531Stegge		*fhsizep = NFSX_V2FH;
182825723Stegge
182967531Stegge	if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
183067531Stegge		goto bad;
183125723Stegge
183225723Stegge	if (args->flags & NFSMNT_NFSV3) {
183367531Stegge		if (xdr_int_decode(&m, &authcount) != 0)
183467531Stegge			goto bad;
183567531Stegge		authunixok = 0;
183667531Stegge		if (authcount < 0 || authcount > 100)
183767531Stegge			goto bad;
183867531Stegge		while (authcount > 0) {
183967531Stegge			if (xdr_int_decode(&m, &authver) != 0)
184067531Stegge				goto bad;
1841195202Sdfr			if (authver == AUTH_UNIX)
184267531Stegge				authunixok = 1;
184367531Stegge			authcount--;
184467531Stegge		}
184567531Stegge		if (authunixok == 0)
184667531Stegge			goto bad;
184725723Stegge	}
184883651Speter
184925723Stegge	/* Set port number for NFS use. */
185067531Stegge	error = krpc_portmap(mdsin, NFS_PROG,
185167531Stegge			     (args->flags &
185267531Stegge			      NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
185383366Sjulian			     &mdsin->sin_port, td);
185483651Speter
185525723Stegge	goto out;
185683651Speter
185725723Steggebad:
185825723Stegge	error = EBADRPC;
185983651Speter
186025723Steggeout:
186125723Stegge	m_freem(m);
186225723Stegge	return error;
186325723Stegge}
1864132808Sphk
1865132808SphkSYSINIT(bootp_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, bootpc_init, NULL);
1866