xref: /netbsd-src/sys/fs/nfs/common/bootp_subr.c (revision 0342d5080b4aa2e64ad6d1da61f9e3e072466754)
1*0342d508Spgoyette /*	$NetBSD: bootp_subr.c,v 1.3 2016/12/13 22:52:46 pgoyette Exp $	*/
26ca35587Sdholland /*-
36ca35587Sdholland  * Copyright (c) 1995 Gordon Ross, Adam Glass
46ca35587Sdholland  * Copyright (c) 1992 Regents of the University of California.
56ca35587Sdholland  * All rights reserved.
66ca35587Sdholland  *
76ca35587Sdholland  * This software was developed by the Computer Systems Engineering group
86ca35587Sdholland  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
96ca35587Sdholland  * contributed to Berkeley.
106ca35587Sdholland  *
116ca35587Sdholland  * Redistribution and use in source and binary forms, with or without
126ca35587Sdholland  * modification, are permitted provided that the following conditions
136ca35587Sdholland  * are met:
146ca35587Sdholland  * 1. Redistributions of source code must retain the above copyright
156ca35587Sdholland  *    notice, this list of conditions and the following disclaimer.
166ca35587Sdholland  * 2. Redistributions in binary form must reproduce the above copyright
176ca35587Sdholland  *    notice, this list of conditions and the following disclaimer in the
186ca35587Sdholland  *    documentation and/or other materials provided with the distribution.
196ca35587Sdholland  * 3. All advertising materials mentioning features or use of this software
206ca35587Sdholland  *    must display the following acknowledgement:
216ca35587Sdholland  *	This product includes software developed by the University of
226ca35587Sdholland  *	California, Lawrence Berkeley Laboratory and its contributors.
236ca35587Sdholland  * 4. Neither the name of the University nor the names of its contributors
246ca35587Sdholland  *    may be used to endorse or promote products derived from this software
256ca35587Sdholland  *    without specific prior written permission.
266ca35587Sdholland  *
276ca35587Sdholland  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
286ca35587Sdholland  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
296ca35587Sdholland  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
306ca35587Sdholland  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
316ca35587Sdholland  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
326ca35587Sdholland  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
336ca35587Sdholland  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
346ca35587Sdholland  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
356ca35587Sdholland  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
366ca35587Sdholland  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
376ca35587Sdholland  * SUCH DAMAGE.
386ca35587Sdholland  *
396ca35587Sdholland  * based on:
406ca35587Sdholland  *      nfs/krpc_subr.c
416ca35587Sdholland  *	NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp
426ca35587Sdholland  */
436ca35587Sdholland 
446ca35587Sdholland #include <sys/cdefs.h>
45e81f0ea2Spgoyette /* __FBSDID("FreeBSD: head/sys/nfs/bootp_subr.c 297326 2016-03-27 23:16:37Z ian "); */
46*0342d508Spgoyette __RCSID("$NetBSD: bootp_subr.c,v 1.3 2016/12/13 22:52:46 pgoyette Exp $");
476ca35587Sdholland 
48c68a8278Spgoyette #ifdef _KERNEL_OPT
49c68a8278Spgoyette #include "opt_newnfs.h"
50c68a8278Spgoyette #endif
516ca35587Sdholland 
526ca35587Sdholland #include <sys/param.h>
536ca35587Sdholland #include <sys/systm.h>
54e81f0ea2Spgoyette #include <sys/endian.h>
556ca35587Sdholland #include <sys/kernel.h>
566ca35587Sdholland #include <sys/sockio.h>
576ca35587Sdholland #include <sys/malloc.h>
586ca35587Sdholland #include <sys/mount.h>
596ca35587Sdholland #include <sys/mbuf.h>
606ca35587Sdholland #include <sys/proc.h>
616ca35587Sdholland #include <sys/reboot.h>
626ca35587Sdholland #include <sys/socket.h>
636ca35587Sdholland #include <sys/socketvar.h>
646ca35587Sdholland #include <sys/sysctl.h>
656ca35587Sdholland #include <sys/uio.h>
66*0342d508Spgoyette #include <sys/kauth.h>
67*0342d508Spgoyette #include <sys/lwp.h>
68*0342d508Spgoyette #include <sys/vnode.h>
69*0342d508Spgoyette #include <sys/mutex.h>
70*0342d508Spgoyette 
71*0342d508Spgoyette #if 0
72*0342d508Spgoyette #include <src/include/rpc/auth.h>
73*0342d508Spgoyette #endif
746ca35587Sdholland 
756ca35587Sdholland #include <net/if.h>
766ca35587Sdholland #include <net/route.h>
77e81f0ea2Spgoyette #ifdef BOOTP_DEBUG
78e81f0ea2Spgoyette #include <net/route_var.h>
79e81f0ea2Spgoyette #endif
806ca35587Sdholland 
816ca35587Sdholland #include <netinet/in.h>
826ca35587Sdholland #include <netinet/in_var.h>
836ca35587Sdholland #include <net/if_types.h>
846ca35587Sdholland #include <net/if_dl.h>
856ca35587Sdholland 
86c68a8278Spgoyette #include <fs/nfs/common/nfsproto.h>
87*0342d508Spgoyette #include <fs/nfs/common/nfsport.h>
88*0342d508Spgoyette #include <fs/nfs/common/nfs.h>
89*0342d508Spgoyette #include <fs/nfs/client/nfsmount.h>
90*0342d508Spgoyette #include <fs/nfs/client/nfsnode.h>
91c68a8278Spgoyette #include <fs/nfs/client/nfs.h>
92c68a8278Spgoyette #include <fs/nfs/common/nfsdiskless.h>
93c68a8278Spgoyette #include <fs/nfs/common/krpc.h>
94c68a8278Spgoyette #include <fs/nfs/common/xdr_subs.h>
956ca35587Sdholland 
966ca35587Sdholland 
976ca35587Sdholland #define BOOTP_MIN_LEN		300	/* Minimum size of bootp udp packet */
986ca35587Sdholland 
996ca35587Sdholland #ifndef BOOTP_SETTLE_DELAY
1006ca35587Sdholland #define BOOTP_SETTLE_DELAY 3
1016ca35587Sdholland #endif
1026ca35587Sdholland 
1036ca35587Sdholland /*
1046ca35587Sdholland  * Wait 10 seconds for interface appearance
1056ca35587Sdholland  * USB ethernet adapters might require some time to pop up
1066ca35587Sdholland  */
1076ca35587Sdholland #ifndef	BOOTP_IFACE_WAIT_TIMEOUT
1086ca35587Sdholland #define	BOOTP_IFACE_WAIT_TIMEOUT	10
1096ca35587Sdholland #endif
1106ca35587Sdholland 
1116ca35587Sdholland /*
1126ca35587Sdholland  * What is the longest we will wait before re-sending a request?
1136ca35587Sdholland  * Note this is also the frequency of "RPC timeout" messages.
1146ca35587Sdholland  * The re-send loop count sup linearly to this maximum, so the
1156ca35587Sdholland  * first complaint will happen after (1+2+3+4+5)=15 seconds.
1166ca35587Sdholland  */
1176ca35587Sdholland #define	MAX_RESEND_DELAY 5	/* seconds */
1186ca35587Sdholland 
1196ca35587Sdholland /* Definitions from RFC951 */
1206ca35587Sdholland struct bootp_packet {
1216ca35587Sdholland 	u_int8_t op;
1226ca35587Sdholland 	u_int8_t htype;
1236ca35587Sdholland 	u_int8_t hlen;
1246ca35587Sdholland 	u_int8_t hops;
1256ca35587Sdholland 	u_int32_t xid;
1266ca35587Sdholland 	u_int16_t secs;
1276ca35587Sdholland 	u_int16_t flags;
1286ca35587Sdholland 	struct in_addr ciaddr;
1296ca35587Sdholland 	struct in_addr yiaddr;
1306ca35587Sdholland 	struct in_addr siaddr;
1316ca35587Sdholland 	struct in_addr giaddr;
1326ca35587Sdholland 	unsigned char chaddr[16];
1336ca35587Sdholland 	char sname[64];
1346ca35587Sdholland 	char file[128];
1356ca35587Sdholland 	unsigned char vend[1222];
1366ca35587Sdholland };
1376ca35587Sdholland 
1386ca35587Sdholland struct bootpc_ifcontext {
1396ca35587Sdholland 	STAILQ_ENTRY(bootpc_ifcontext) next;
1406ca35587Sdholland 	struct bootp_packet call;
1416ca35587Sdholland 	struct bootp_packet reply;
1426ca35587Sdholland 	int replylen;
1436ca35587Sdholland 	int overload;
1446ca35587Sdholland 	union {
1456ca35587Sdholland 		struct ifreq _ifreq;
1466ca35587Sdholland 		struct in_aliasreq _in_alias_req;
1476ca35587Sdholland 	} _req;
1486ca35587Sdholland #define	ireq	_req._ifreq
1496ca35587Sdholland #define	iareq	_req._in_alias_req
1506ca35587Sdholland 	struct ifnet *ifp;
1516ca35587Sdholland 	struct sockaddr_dl *sdl;
1526ca35587Sdholland 	struct sockaddr_in myaddr;
1536ca35587Sdholland 	struct sockaddr_in netmask;
1546ca35587Sdholland 	struct sockaddr_in gw;
1556ca35587Sdholland 	int gotgw;
1566ca35587Sdholland 	int gotnetmask;
1576ca35587Sdholland 	int gotrootpath;
1586ca35587Sdholland 	int outstanding;
1596ca35587Sdholland 	int sentmsg;
1606ca35587Sdholland 	u_int32_t xid;
1616ca35587Sdholland 	enum {
1626ca35587Sdholland 		IF_BOOTP_UNRESOLVED,
1636ca35587Sdholland 		IF_BOOTP_RESOLVED,
1646ca35587Sdholland 		IF_BOOTP_FAILED,
1656ca35587Sdholland 		IF_DHCP_UNRESOLVED,
1666ca35587Sdholland 		IF_DHCP_OFFERED,
1676ca35587Sdholland 		IF_DHCP_RESOLVED,
1686ca35587Sdholland 		IF_DHCP_FAILED,
1696ca35587Sdholland 	} state;
1706ca35587Sdholland 	int dhcpquerytype;		/* dhcp type sent */
1716ca35587Sdholland 	struct in_addr dhcpserver;
1726ca35587Sdholland 	int gotdhcpserver;
173e81f0ea2Spgoyette 	uint16_t mtu;
1746ca35587Sdholland };
1756ca35587Sdholland 
1766ca35587Sdholland #define TAG_MAXLEN 1024
1776ca35587Sdholland struct bootpc_tagcontext {
1786ca35587Sdholland 	char buf[TAG_MAXLEN + 1];
1796ca35587Sdholland 	int overload;
1806ca35587Sdholland 	int badopt;
1816ca35587Sdholland 	int badtag;
1826ca35587Sdholland 	int foundopt;
1836ca35587Sdholland 	int taglen;
1846ca35587Sdholland };
1856ca35587Sdholland 
1866ca35587Sdholland struct bootpc_globalcontext {
1876ca35587Sdholland 	STAILQ_HEAD(, bootpc_ifcontext) interfaces;
1886ca35587Sdholland 	u_int32_t xid;
1896ca35587Sdholland 	int any_root_overrides;
1906ca35587Sdholland 	int gotrootpath;
1916ca35587Sdholland 	int gotgw;
1926ca35587Sdholland 	int ifnum;
1936ca35587Sdholland 	int secs;
1946ca35587Sdholland 	int starttime;
1956ca35587Sdholland 	struct bootp_packet reply;
1966ca35587Sdholland 	int replylen;
1976ca35587Sdholland 	struct bootpc_ifcontext *setrootfs;
1986ca35587Sdholland 	struct bootpc_ifcontext *sethostname;
1996ca35587Sdholland 	struct bootpc_tagcontext tmptag;
2006ca35587Sdholland 	struct bootpc_tagcontext tag;
2016ca35587Sdholland };
2026ca35587Sdholland 
2036ca35587Sdholland #define IPPORT_BOOTPC 68
2046ca35587Sdholland #define IPPORT_BOOTPS 67
2056ca35587Sdholland 
2066ca35587Sdholland #define BOOTP_REQUEST 1
2076ca35587Sdholland #define BOOTP_REPLY 2
2086ca35587Sdholland 
2096ca35587Sdholland /* Common tags */
2106ca35587Sdholland #define TAG_PAD		  0  /* Pad option, implicit length 1 */
2116ca35587Sdholland #define TAG_SUBNETMASK	  1  /* RFC 950 subnet mask */
2126ca35587Sdholland #define TAG_ROUTERS	  3  /* Routers (in order of preference) */
2136ca35587Sdholland #define TAG_HOSTNAME	 12  /* Client host name */
2146ca35587Sdholland #define TAG_ROOT	 17  /* Root path */
215e81f0ea2Spgoyette #define TAG_INTF_MTU	 26  /* Interface MTU Size (RFC2132) */
2166ca35587Sdholland 
2176ca35587Sdholland /* DHCP specific tags */
2186ca35587Sdholland #define TAG_OVERLOAD	 52  /* Option Overload */
2196ca35587Sdholland #define TAG_MAXMSGSIZE   57  /* Maximum DHCP Message Size */
2206ca35587Sdholland 
2216ca35587Sdholland #define TAG_END		255  /* End Option (i.e. no more options) */
2226ca35587Sdholland 
2236ca35587Sdholland /* Overload values */
2246ca35587Sdholland #define OVERLOAD_FILE     1
2256ca35587Sdholland #define OVERLOAD_SNAME    2
2266ca35587Sdholland 
2276ca35587Sdholland /* Site specific tags: */
2286ca35587Sdholland #define TAG_ROOTOPTS	130
2296ca35587Sdholland #define TAG_COOKIE	134	/* ascii info for userland, via sysctl */
2306ca35587Sdholland 
2316ca35587Sdholland #define TAG_DHCP_MSGTYPE 53
2326ca35587Sdholland #define TAG_DHCP_REQ_ADDR 50
2336ca35587Sdholland #define TAG_DHCP_SERVERID 54
2346ca35587Sdholland #define TAG_DHCP_LEASETIME 51
2356ca35587Sdholland 
2366ca35587Sdholland #define TAG_VENDOR_INDENTIFIER 60
2376ca35587Sdholland 
2386ca35587Sdholland #define DHCP_NOMSG    0
2396ca35587Sdholland #define DHCP_DISCOVER 1
2406ca35587Sdholland #define DHCP_OFFER    2
2416ca35587Sdholland #define DHCP_REQUEST  3
2426ca35587Sdholland #define DHCP_ACK      5
2436ca35587Sdholland 
2446ca35587Sdholland /* NFS read/write block size */
2456ca35587Sdholland #ifndef BOOTP_BLOCKSIZE
2466ca35587Sdholland #define	BOOTP_BLOCKSIZE	8192
2476ca35587Sdholland #endif
2486ca35587Sdholland 
2496ca35587Sdholland static char bootp_cookie[128];
2506ca35587Sdholland static struct socket *bootp_so;
251*0342d508Spgoyette 
252*0342d508Spgoyette #if 0	/* Need to init sysctl variables in the module initialization code */
2536ca35587Sdholland SYSCTL_STRING(_kern, OID_AUTO, bootp_cookie, CTLFLAG_RD,
2546ca35587Sdholland 	bootp_cookie, 0, "Cookie (T134) supplied by bootp server");
255*0342d508Spgoyette #endif
2566ca35587Sdholland 
2576ca35587Sdholland /* mountd RPC */
2586ca35587Sdholland static int	md_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp,
259*0342d508Spgoyette 		    int *fhsizep, struct nfs_args *args, struct lwp *td);
260*0342d508Spgoyette static int	setmyfs(struct sockaddr_in *addr, char *path, char *p,
2616ca35587Sdholland 		    const struct in_addr *siaddr);
2626ca35587Sdholland static int	getdec(char **ptr);
2636ca35587Sdholland static int	getip(char **ptr, struct in_addr *ip);
2646ca35587Sdholland static void	mountopts(struct nfs_args *args, char *p);
2656ca35587Sdholland static int	xdr_opaque_decode(struct mbuf **ptr, u_char *buf, int len);
2666ca35587Sdholland static int	xdr_int_decode(struct mbuf **ptr, int *iptr);
2676ca35587Sdholland static void	print_in_addr(struct in_addr addr);
2686ca35587Sdholland static void	print_sin_addr(struct sockaddr_in *addr);
2696ca35587Sdholland static void	clear_sinaddr(struct sockaddr_in *sin);
2706ca35587Sdholland static void	allocifctx(struct bootpc_globalcontext *gctx);
2716ca35587Sdholland static void	bootpc_compose_query(struct bootpc_ifcontext *ifctx,
272*0342d508Spgoyette 		    struct lwp *td);
2736ca35587Sdholland static unsigned char *bootpc_tag(struct bootpc_tagcontext *tctx,
2746ca35587Sdholland 		    struct bootp_packet *bp, int len, int tag);
2756ca35587Sdholland static void bootpc_tag_helper(struct bootpc_tagcontext *tctx,
2766ca35587Sdholland 		    unsigned char *start, int len, int tag);
2776ca35587Sdholland 
2786ca35587Sdholland #ifdef BOOTP_DEBUG
2796ca35587Sdholland void bootpboot_p_sa(struct sockaddr *sa, struct sockaddr *ma);
2806ca35587Sdholland void bootpboot_p_rtentry(struct rtentry *rt);
2816ca35587Sdholland void bootpboot_p_tree(struct radix_node *rn);
2826ca35587Sdholland void bootpboot_p_rtlist(void);
2836ca35587Sdholland void bootpboot_p_if(struct ifnet *ifp, struct ifaddr *ifa);
2846ca35587Sdholland void bootpboot_p_iflist(void);
2856ca35587Sdholland #endif
2866ca35587Sdholland 
2876ca35587Sdholland static int	bootpc_call(struct bootpc_globalcontext *gctx,
288*0342d508Spgoyette 		    struct lwp *td);
2896ca35587Sdholland 
2906ca35587Sdholland static void	bootpc_fakeup_interface(struct bootpc_ifcontext *ifctx,
291*0342d508Spgoyette 		    struct lwp *td);
2926ca35587Sdholland 
293e81f0ea2Spgoyette static void	bootpc_adjust_interface(struct bootpc_ifcontext *ifctx,
294*0342d508Spgoyette 		    struct bootpc_globalcontext *gctx, struct lwp *td);
2956ca35587Sdholland 
2966ca35587Sdholland static void	bootpc_decode_reply(struct nfsv3_diskless *nd,
2976ca35587Sdholland 		    struct bootpc_ifcontext *ifctx,
2986ca35587Sdholland 		    struct bootpc_globalcontext *gctx);
2996ca35587Sdholland 
3006ca35587Sdholland static int	bootpc_received(struct bootpc_globalcontext *gctx,
3016ca35587Sdholland 		    struct bootpc_ifcontext *ifctx);
3026ca35587Sdholland 
3036ca35587Sdholland static __inline int bootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx);
3046ca35587Sdholland static __inline int bootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx);
3056ca35587Sdholland static __inline int bootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx);
3066ca35587Sdholland 
3076ca35587Sdholland /*
3086ca35587Sdholland  * In order to have multiple active interfaces with address 0.0.0.0
3096ca35587Sdholland  * and be able to send data to a selected interface, we first set
3106ca35587Sdholland  * mask to /8 on all interfaces, and temporarily set it to /0 when
3116ca35587Sdholland  * doing sosend().
3126ca35587Sdholland  */
3136ca35587Sdholland 
3146ca35587Sdholland #ifdef BOOTP_DEBUG
3156ca35587Sdholland void
bootpboot_p_sa(struct sockaddr * sa,struct sockaddr * ma)3166ca35587Sdholland bootpboot_p_sa(struct sockaddr *sa, struct sockaddr *ma)
3176ca35587Sdholland {
3186ca35587Sdholland 
3196ca35587Sdholland 	if (sa == NULL) {
3206ca35587Sdholland 		printf("(sockaddr *) <null>");
3216ca35587Sdholland 		return;
3226ca35587Sdholland 	}
3236ca35587Sdholland 	switch (sa->sa_family) {
3246ca35587Sdholland 	case AF_INET:
3256ca35587Sdholland 	{
3266ca35587Sdholland 		struct sockaddr_in *sin;
3276ca35587Sdholland 
3286ca35587Sdholland 		sin = (struct sockaddr_in *) sa;
3296ca35587Sdholland 		printf("inet ");
3306ca35587Sdholland 		print_sin_addr(sin);
3316ca35587Sdholland 		if (ma != NULL) {
3326ca35587Sdholland 			sin = (struct sockaddr_in *) ma;
3336ca35587Sdholland 			printf(" mask ");
3346ca35587Sdholland 			print_sin_addr(sin);
3356ca35587Sdholland 		}
3366ca35587Sdholland 	}
3376ca35587Sdholland 	break;
3386ca35587Sdholland 	case AF_LINK:
3396ca35587Sdholland 	{
3406ca35587Sdholland 		struct sockaddr_dl *sli;
3416ca35587Sdholland 		int i;
3426ca35587Sdholland 
3436ca35587Sdholland 		sli = (struct sockaddr_dl *) sa;
3446ca35587Sdholland 		printf("link %.*s ", sli->sdl_nlen, sli->sdl_data);
3456ca35587Sdholland 		for (i = 0; i < sli->sdl_alen; i++) {
3466ca35587Sdholland 			if (i > 0)
3476ca35587Sdholland 				printf(":");
3486ca35587Sdholland 			printf("%x", ((unsigned char *) LLADDR(sli))[i]);
3496ca35587Sdholland 		}
3506ca35587Sdholland 	}
3516ca35587Sdholland 	break;
3526ca35587Sdholland 	default:
3536ca35587Sdholland 		printf("af%d", sa->sa_family);
3546ca35587Sdholland 	}
3556ca35587Sdholland }
3566ca35587Sdholland 
3576ca35587Sdholland void
bootpboot_p_rtentry(struct rtentry * rt)3586ca35587Sdholland bootpboot_p_rtentry(struct rtentry *rt)
3596ca35587Sdholland {
3606ca35587Sdholland 
3616ca35587Sdholland 	bootpboot_p_sa(rt_key(rt), rt_mask(rt));
3626ca35587Sdholland 	printf(" ");
3636ca35587Sdholland 	bootpboot_p_sa(rt->rt_gateway, NULL);
3646ca35587Sdholland 	printf(" ");
3656ca35587Sdholland 	printf("flags %x", (unsigned short) rt->rt_flags);
366e81f0ea2Spgoyette 	printf(" %d", (int) rt->rt_expire);
3676ca35587Sdholland 	printf(" %s\n", rt->rt_ifp->if_xname);
3686ca35587Sdholland }
3696ca35587Sdholland 
3706ca35587Sdholland void
bootpboot_p_tree(struct radix_node * rn)3716ca35587Sdholland bootpboot_p_tree(struct radix_node *rn)
3726ca35587Sdholland {
3736ca35587Sdholland 
3746ca35587Sdholland 	while (rn != NULL) {
3756ca35587Sdholland 		if (rn->rn_bit < 0) {
3766ca35587Sdholland 			if ((rn->rn_flags & RNF_ROOT) != 0) {
3776ca35587Sdholland 			} else {
3786ca35587Sdholland 				bootpboot_p_rtentry((struct rtentry *) rn);
3796ca35587Sdholland 			}
3806ca35587Sdholland 			rn = rn->rn_dupedkey;
3816ca35587Sdholland 		} else {
3826ca35587Sdholland 			bootpboot_p_tree(rn->rn_left);
3836ca35587Sdholland 			bootpboot_p_tree(rn->rn_right);
3846ca35587Sdholland 			return;
3856ca35587Sdholland 		}
3866ca35587Sdholland 	}
3876ca35587Sdholland }
3886ca35587Sdholland 
3896ca35587Sdholland void
bootpboot_p_rtlist(void)3906ca35587Sdholland bootpboot_p_rtlist(void)
3916ca35587Sdholland {
392e81f0ea2Spgoyette 	struct rib_head *rnh;
3936ca35587Sdholland 
3946ca35587Sdholland 	printf("Routing table:\n");
3956ca35587Sdholland 	rnh = rt_tables_get_rnh(0, AF_INET);
3966ca35587Sdholland 	if (rnh == NULL)
3976ca35587Sdholland 		return;
398e81f0ea2Spgoyette 	RIB_RLOCK(rnh);	/* could sleep XXX */
3996ca35587Sdholland 	bootpboot_p_tree(rnh->rnh_treetop);
400e81f0ea2Spgoyette 	RIB_RUNLOCK(rnh);
4016ca35587Sdholland }
4026ca35587Sdholland 
4036ca35587Sdholland void
bootpboot_p_if(struct ifnet * ifp,struct ifaddr * ifa)4046ca35587Sdholland bootpboot_p_if(struct ifnet *ifp, struct ifaddr *ifa)
4056ca35587Sdholland {
4066ca35587Sdholland 
4076ca35587Sdholland 	printf("%s flags %x, addr ",
4086ca35587Sdholland 	       ifp->if_xname, ifp->if_flags);
4096ca35587Sdholland 	print_sin_addr((struct sockaddr_in *) ifa->ifa_addr);
4106ca35587Sdholland 	printf(", broadcast ");
4116ca35587Sdholland 	print_sin_addr((struct sockaddr_in *) ifa->ifa_dstaddr);
4126ca35587Sdholland 	printf(", netmask ");
4136ca35587Sdholland 	print_sin_addr((struct sockaddr_in *) ifa->ifa_netmask);
4146ca35587Sdholland 	printf("\n");
4156ca35587Sdholland }
4166ca35587Sdholland 
4176ca35587Sdholland void
bootpboot_p_iflist(void)4186ca35587Sdholland bootpboot_p_iflist(void)
4196ca35587Sdholland {
4206ca35587Sdholland 	struct ifnet *ifp;
4216ca35587Sdholland 	struct ifaddr *ifa;
4226ca35587Sdholland 
4236ca35587Sdholland 	printf("Interface list:\n");
4246ca35587Sdholland 	IFNET_RLOCK();
4256ca35587Sdholland 	for (ifp = TAILQ_FIRST(&V_ifnet);
4266ca35587Sdholland 	     ifp != NULL;
4276ca35587Sdholland 	     ifp = TAILQ_NEXT(ifp, if_link)) {
4286ca35587Sdholland 		for (ifa = TAILQ_FIRST(&ifp->if_addrhead);
4296ca35587Sdholland 		     ifa != NULL;
4306ca35587Sdholland 		     ifa = TAILQ_NEXT(ifa, ifa_link))
4316ca35587Sdholland 			if (ifa->ifa_addr->sa_family == AF_INET)
4326ca35587Sdholland 				bootpboot_p_if(ifp, ifa);
4336ca35587Sdholland 	}
4346ca35587Sdholland 	IFNET_RUNLOCK();
4356ca35587Sdholland }
4366ca35587Sdholland #endif /* defined(BOOTP_DEBUG) */
4376ca35587Sdholland 
4386ca35587Sdholland static void
clear_sinaddr(struct sockaddr_in * sin)4396ca35587Sdholland clear_sinaddr(struct sockaddr_in *sin)
4406ca35587Sdholland {
4416ca35587Sdholland 
4426ca35587Sdholland 	bzero(sin, sizeof(*sin));
4436ca35587Sdholland 	sin->sin_len = sizeof(*sin);
4446ca35587Sdholland 	sin->sin_family = AF_INET;
4456ca35587Sdholland 	sin->sin_addr.s_addr = INADDR_ANY; /* XXX: htonl(INAADDR_ANY) ? */
4466ca35587Sdholland 	sin->sin_port = 0;
4476ca35587Sdholland }
4486ca35587Sdholland 
4496ca35587Sdholland static void
allocifctx(struct bootpc_globalcontext * gctx)4506ca35587Sdholland allocifctx(struct bootpc_globalcontext *gctx)
4516ca35587Sdholland {
4526ca35587Sdholland 	struct bootpc_ifcontext *ifctx;
4536ca35587Sdholland 
4546ca35587Sdholland 	ifctx = malloc(sizeof(*ifctx), M_TEMP, M_WAITOK | M_ZERO);
4556ca35587Sdholland 	ifctx->xid = gctx->xid;
4566ca35587Sdholland #ifdef BOOTP_NO_DHCP
4576ca35587Sdholland 	ifctx->state = IF_BOOTP_UNRESOLVED;
4586ca35587Sdholland #else
4596ca35587Sdholland 	ifctx->state = IF_DHCP_UNRESOLVED;
4606ca35587Sdholland #endif
4616ca35587Sdholland 	gctx->xid += 0x100;
4626ca35587Sdholland 	STAILQ_INSERT_TAIL(&gctx->interfaces, ifctx, next);
4636ca35587Sdholland }
4646ca35587Sdholland 
4656ca35587Sdholland static __inline int
bootpc_ifctx_isresolved(struct bootpc_ifcontext * ifctx)4666ca35587Sdholland bootpc_ifctx_isresolved(struct bootpc_ifcontext *ifctx)
4676ca35587Sdholland {
4686ca35587Sdholland 
4696ca35587Sdholland 	if (ifctx->state == IF_BOOTP_RESOLVED ||
4706ca35587Sdholland 	    ifctx->state == IF_DHCP_RESOLVED)
4716ca35587Sdholland 		return 1;
4726ca35587Sdholland 	return 0;
4736ca35587Sdholland }
4746ca35587Sdholland 
4756ca35587Sdholland static __inline int
bootpc_ifctx_isunresolved(struct bootpc_ifcontext * ifctx)4766ca35587Sdholland bootpc_ifctx_isunresolved(struct bootpc_ifcontext *ifctx)
4776ca35587Sdholland {
4786ca35587Sdholland 
4796ca35587Sdholland 	if (ifctx->state == IF_BOOTP_UNRESOLVED ||
4806ca35587Sdholland 	    ifctx->state == IF_DHCP_UNRESOLVED)
4816ca35587Sdholland 		return 1;
4826ca35587Sdholland 	return 0;
4836ca35587Sdholland }
4846ca35587Sdholland 
4856ca35587Sdholland static __inline int
bootpc_ifctx_isfailed(struct bootpc_ifcontext * ifctx)4866ca35587Sdholland bootpc_ifctx_isfailed(struct bootpc_ifcontext *ifctx)
4876ca35587Sdholland {
4886ca35587Sdholland 
4896ca35587Sdholland 	if (ifctx->state == IF_BOOTP_FAILED ||
4906ca35587Sdholland 	    ifctx->state == IF_DHCP_FAILED)
4916ca35587Sdholland 		return 1;
4926ca35587Sdholland 	return 0;
4936ca35587Sdholland }
4946ca35587Sdholland 
4956ca35587Sdholland static int
bootpc_received(struct bootpc_globalcontext * gctx,struct bootpc_ifcontext * ifctx)4966ca35587Sdholland bootpc_received(struct bootpc_globalcontext *gctx,
4976ca35587Sdholland     struct bootpc_ifcontext *ifctx)
4986ca35587Sdholland {
4996ca35587Sdholland 	unsigned char dhcpreplytype;
5006ca35587Sdholland 	char *p;
5016ca35587Sdholland 
5026ca35587Sdholland 	/*
5036ca35587Sdholland 	 * Need timeout for fallback to less
5046ca35587Sdholland 	 * desirable alternative.
5056ca35587Sdholland 	 */
5066ca35587Sdholland 
5076ca35587Sdholland 	/* This call used for the side effect (badopt flag) */
5086ca35587Sdholland 	(void) bootpc_tag(&gctx->tmptag, &gctx->reply,
5096ca35587Sdholland 			  gctx->replylen,
5106ca35587Sdholland 			  TAG_END);
5116ca35587Sdholland 
5126ca35587Sdholland 	/* If packet is invalid, ignore it */
5136ca35587Sdholland 	if (gctx->tmptag.badopt != 0)
5146ca35587Sdholland 		return 0;
5156ca35587Sdholland 
5166ca35587Sdholland 	p = bootpc_tag(&gctx->tmptag, &gctx->reply,
5176ca35587Sdholland 		       gctx->replylen, TAG_DHCP_MSGTYPE);
5186ca35587Sdholland 	if (p != NULL)
5196ca35587Sdholland 		dhcpreplytype = *p;
5206ca35587Sdholland 	else
5216ca35587Sdholland 		dhcpreplytype = DHCP_NOMSG;
5226ca35587Sdholland 
5236ca35587Sdholland 	switch (ifctx->dhcpquerytype) {
5246ca35587Sdholland 	case DHCP_DISCOVER:
5256ca35587Sdholland 		if (dhcpreplytype != DHCP_OFFER 	/* Normal DHCP offer */
5266ca35587Sdholland #ifndef BOOTP_FORCE_DHCP
5276ca35587Sdholland 		    && dhcpreplytype != DHCP_NOMSG	/* Fallback to BOOTP */
5286ca35587Sdholland #endif
5296ca35587Sdholland 			)
5306ca35587Sdholland 			return 0;
5316ca35587Sdholland 		break;
5326ca35587Sdholland 	case DHCP_REQUEST:
5336ca35587Sdholland 		if (dhcpreplytype != DHCP_ACK)
5346ca35587Sdholland 			return 0;
5356ca35587Sdholland 	case DHCP_NOMSG:
5366ca35587Sdholland 		break;
5376ca35587Sdholland 	}
5386ca35587Sdholland 
5396ca35587Sdholland 	/* Ignore packet unless it gives us a root tag we didn't have */
5406ca35587Sdholland 
5416ca35587Sdholland 	if ((ifctx->state == IF_BOOTP_RESOLVED ||
5426ca35587Sdholland 	     (ifctx->dhcpquerytype == DHCP_DISCOVER &&
5436ca35587Sdholland 	      (ifctx->state == IF_DHCP_OFFERED ||
5446ca35587Sdholland 	       ifctx->state == IF_DHCP_RESOLVED))) &&
5456ca35587Sdholland 	    (bootpc_tag(&gctx->tmptag, &ifctx->reply,
5466ca35587Sdholland 			ifctx->replylen,
5476ca35587Sdholland 			TAG_ROOT) != NULL ||
5486ca35587Sdholland 	     bootpc_tag(&gctx->tmptag, &gctx->reply,
5496ca35587Sdholland 			gctx->replylen,
5506ca35587Sdholland 			TAG_ROOT) == NULL))
5516ca35587Sdholland 		return 0;
5526ca35587Sdholland 
5536ca35587Sdholland 	bcopy(&gctx->reply, &ifctx->reply, gctx->replylen);
5546ca35587Sdholland 	ifctx->replylen = gctx->replylen;
5556ca35587Sdholland 
5566ca35587Sdholland 	/* XXX: Only reset if 'perfect' response */
5576ca35587Sdholland 	if (ifctx->state == IF_BOOTP_UNRESOLVED)
5586ca35587Sdholland 		ifctx->state = IF_BOOTP_RESOLVED;
5596ca35587Sdholland 	else if (ifctx->state == IF_DHCP_UNRESOLVED &&
5606ca35587Sdholland 		 ifctx->dhcpquerytype == DHCP_DISCOVER) {
5616ca35587Sdholland 		if (dhcpreplytype == DHCP_OFFER)
5626ca35587Sdholland 			ifctx->state = IF_DHCP_OFFERED;
5636ca35587Sdholland 		else
5646ca35587Sdholland 			ifctx->state = IF_BOOTP_RESOLVED;	/* Fallback */
5656ca35587Sdholland 	} else if (ifctx->state == IF_DHCP_OFFERED &&
5666ca35587Sdholland 		   ifctx->dhcpquerytype == DHCP_REQUEST)
5676ca35587Sdholland 		ifctx->state = IF_DHCP_RESOLVED;
5686ca35587Sdholland 
5696ca35587Sdholland 
5706ca35587Sdholland 	if (ifctx->dhcpquerytype == DHCP_DISCOVER &&
5716ca35587Sdholland 	    ifctx->state != IF_BOOTP_RESOLVED) {
5726ca35587Sdholland 		p = bootpc_tag(&gctx->tmptag, &ifctx->reply,
5736ca35587Sdholland 			       ifctx->replylen, TAG_DHCP_SERVERID);
5746ca35587Sdholland 		if (p != NULL && gctx->tmptag.taglen == 4) {
5756ca35587Sdholland 			memcpy(&ifctx->dhcpserver, p, 4);
5766ca35587Sdholland 			ifctx->gotdhcpserver = 1;
5776ca35587Sdholland 		} else
5786ca35587Sdholland 			ifctx->gotdhcpserver = 0;
5796ca35587Sdholland 		return 1;
5806ca35587Sdholland 	}
5816ca35587Sdholland 
5826ca35587Sdholland 	ifctx->gotrootpath = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
5836ca35587Sdholland 					 ifctx->replylen,
5846ca35587Sdholland 					 TAG_ROOT) != NULL);
5856ca35587Sdholland 	ifctx->gotgw = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
5866ca35587Sdholland 				   ifctx->replylen,
5876ca35587Sdholland 				   TAG_ROUTERS) != NULL);
5886ca35587Sdholland 	ifctx->gotnetmask = (bootpc_tag(&gctx->tmptag, &ifctx->reply,
5896ca35587Sdholland 					ifctx->replylen,
5906ca35587Sdholland 					TAG_SUBNETMASK) != NULL);
5916ca35587Sdholland 	return 1;
5926ca35587Sdholland }
5936ca35587Sdholland 
5946ca35587Sdholland static int
bootpc_call(struct bootpc_globalcontext * gctx,struct lwp * td)595*0342d508Spgoyette bootpc_call(struct bootpc_globalcontext *gctx, struct lwp *td)
5966ca35587Sdholland {
5976ca35587Sdholland 	struct sockaddr_in *sin, dst;
5986ca35587Sdholland 	struct uio auio;
5996ca35587Sdholland 	struct sockopt sopt;
6006ca35587Sdholland 	struct iovec aio;
6016ca35587Sdholland 	int error, on, rcvflg, timo, len;
6026ca35587Sdholland 	time_t atimo;
6036ca35587Sdholland 	time_t rtimo;
6046ca35587Sdholland 	struct timeval tv;
6056ca35587Sdholland 	struct bootpc_ifcontext *ifctx;
6066ca35587Sdholland 	int outstanding;
6076ca35587Sdholland 	int gotrootpath;
6086ca35587Sdholland 	int retry;
6096ca35587Sdholland 	const char *s;
6106ca35587Sdholland 
6116ca35587Sdholland 	tv.tv_sec = 1;
6126ca35587Sdholland 	tv.tv_usec = 0;
613*0342d508Spgoyette 	sockopt_init(&sopt, SOL_SOCKET, SO_RCVTIMEO, sizeof(tv));
614*0342d508Spgoyette 	error = sockopt_set(&sopt, &tv, sizeof(tv));
615*0342d508Spgoyette 	sockopt_destroy(&sopt);
6166ca35587Sdholland 
6176ca35587Sdholland 	if (error != 0)
6186ca35587Sdholland 		goto out;
6196ca35587Sdholland 
6206ca35587Sdholland 	/*
6216ca35587Sdholland 	 * Enable broadcast.
6226ca35587Sdholland 	 */
6236ca35587Sdholland 	on = 1;
624*0342d508Spgoyette 	sockopt_init(&sopt, SOL_SOCKET, SO_BROADCAST, sizeof(on));
625*0342d508Spgoyette 	error = sockopt_set(&sopt, &on, sizeof(on));
626*0342d508Spgoyette 	sockopt_destroy(&sopt);
6276ca35587Sdholland 
6286ca35587Sdholland 	if (error != 0)
6296ca35587Sdholland 		goto out;
6306ca35587Sdholland 
6316ca35587Sdholland 	/*
6326ca35587Sdholland 	 * Disable routing.
6336ca35587Sdholland 	 */
6346ca35587Sdholland 
6356ca35587Sdholland 	on = 1;
636*0342d508Spgoyette 	sockopt_init(&sopt, SOL_SOCKET, SO_DONTROUTE, sizeof(on));
637*0342d508Spgoyette 	error = sockopt_set(&sopt, &on, sizeof(on));
638*0342d508Spgoyette 	sockopt_destroy(&sopt);
6396ca35587Sdholland 
6406ca35587Sdholland 	if (error != 0)
6416ca35587Sdholland 		goto out;
6426ca35587Sdholland 
6436ca35587Sdholland 	/*
6446ca35587Sdholland 	 * Bind the local endpoint to a bootp client port.
6456ca35587Sdholland 	 */
6466ca35587Sdholland 	sin = &dst;
6476ca35587Sdholland 	clear_sinaddr(sin);
6486ca35587Sdholland 	sin->sin_port = htons(IPPORT_BOOTPC);
6496ca35587Sdholland 	error = sobind(bootp_so, (struct sockaddr *)sin, td);
6506ca35587Sdholland 	if (error != 0) {
6516ca35587Sdholland 		printf("bind failed\n");
6526ca35587Sdholland 		goto out;
6536ca35587Sdholland 	}
6546ca35587Sdholland 
6556ca35587Sdholland 	/*
6566ca35587Sdholland 	 * Setup socket address for the server.
6576ca35587Sdholland 	 */
6586ca35587Sdholland 	sin = &dst;
6596ca35587Sdholland 	clear_sinaddr(sin);
6606ca35587Sdholland 	sin->sin_addr.s_addr = INADDR_BROADCAST;
6616ca35587Sdholland 	sin->sin_port = htons(IPPORT_BOOTPS);
6626ca35587Sdholland 
6636ca35587Sdholland 	/*
6646ca35587Sdholland 	 * Send it, repeatedly, until a reply is received,
6656ca35587Sdholland 	 * but delay each re-send by an increasing amount.
6666ca35587Sdholland 	 * If the delay hits the maximum, start complaining.
6676ca35587Sdholland 	 */
6686ca35587Sdholland 	timo = 0;
6696ca35587Sdholland 	rtimo = 0;
6706ca35587Sdholland 	for (;;) {
6716ca35587Sdholland 
6726ca35587Sdholland 		outstanding = 0;
6736ca35587Sdholland 		gotrootpath = 0;
6746ca35587Sdholland 
6756ca35587Sdholland 		STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
6766ca35587Sdholland 			if (bootpc_ifctx_isresolved(ifctx) != 0 &&
6776ca35587Sdholland 			    bootpc_tag(&gctx->tmptag, &ifctx->reply,
6786ca35587Sdholland 				       ifctx->replylen,
6796ca35587Sdholland 				       TAG_ROOT) != NULL)
6806ca35587Sdholland 				gotrootpath = 1;
6816ca35587Sdholland 		}
6826ca35587Sdholland 
6836ca35587Sdholland 		STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
6846ca35587Sdholland 			struct in_aliasreq *ifra = &ifctx->iareq;
6856ca35587Sdholland 			sin = (struct sockaddr_in *)&ifra->ifra_mask;
6866ca35587Sdholland 
6876ca35587Sdholland 			ifctx->outstanding = 0;
6886ca35587Sdholland 			if (bootpc_ifctx_isresolved(ifctx)  != 0 &&
6896ca35587Sdholland 			    gotrootpath != 0) {
6906ca35587Sdholland 				continue;
6916ca35587Sdholland 			}
6926ca35587Sdholland 			if (bootpc_ifctx_isfailed(ifctx) != 0)
6936ca35587Sdholland 				continue;
6946ca35587Sdholland 
6956ca35587Sdholland 			outstanding++;
6966ca35587Sdholland 			ifctx->outstanding = 1;
6976ca35587Sdholland 
6986ca35587Sdholland 			/* Proceed to next step in DHCP negotiation */
6996ca35587Sdholland 			if ((ifctx->state == IF_DHCP_OFFERED &&
7006ca35587Sdholland 			     ifctx->dhcpquerytype != DHCP_REQUEST) ||
7016ca35587Sdholland 			    (ifctx->state == IF_DHCP_UNRESOLVED &&
7026ca35587Sdholland 			     ifctx->dhcpquerytype != DHCP_DISCOVER) ||
7036ca35587Sdholland 			    (ifctx->state == IF_BOOTP_UNRESOLVED &&
7046ca35587Sdholland 			     ifctx->dhcpquerytype != DHCP_NOMSG)) {
7056ca35587Sdholland 				ifctx->sentmsg = 0;
7066ca35587Sdholland 				bootpc_compose_query(ifctx, td);
7076ca35587Sdholland 			}
7086ca35587Sdholland 
7096ca35587Sdholland 			/* Send BOOTP request (or re-send). */
7106ca35587Sdholland 
7116ca35587Sdholland 			if (ifctx->sentmsg == 0) {
7126ca35587Sdholland 				switch(ifctx->dhcpquerytype) {
7136ca35587Sdholland 				case DHCP_DISCOVER:
7146ca35587Sdholland 					s = "DHCP Discover";
7156ca35587Sdholland 					break;
7166ca35587Sdholland 				case DHCP_REQUEST:
7176ca35587Sdholland 					s = "DHCP Request";
7186ca35587Sdholland 					break;
7196ca35587Sdholland 				case DHCP_NOMSG:
7206ca35587Sdholland 				default:
7216ca35587Sdholland 					s = "BOOTP Query";
7226ca35587Sdholland 					break;
7236ca35587Sdholland 				}
7246ca35587Sdholland 				printf("Sending %s packet from "
725*0342d508Spgoyette 				       "interface %s (%*ld):\n",
7266ca35587Sdholland 				       s,
7276ca35587Sdholland 				       ifctx->ireq.ifr_name,
7286ca35587Sdholland 				       ifctx->sdl->sdl_alen,
729*0342d508Spgoyette 				       (unsigned char *) LLADDR(ifctx->sdl) );
7306ca35587Sdholland 				ifctx->sentmsg = 1;
7316ca35587Sdholland 			}
7326ca35587Sdholland 
733*0342d508Spgoyette 			aio.iov_base = (void *) &ifctx->call;
7346ca35587Sdholland 			aio.iov_len = sizeof(ifctx->call);
7356ca35587Sdholland 
7366ca35587Sdholland 			auio.uio_iov = &aio;
7376ca35587Sdholland 			auio.uio_iovcnt = 1;
7386ca35587Sdholland 			auio.uio_segflg = UIO_SYSSPACE;
7396ca35587Sdholland 			auio.uio_rw = UIO_WRITE;
7406ca35587Sdholland 			auio.uio_offset = 0;
7416ca35587Sdholland 			auio.uio_resid = sizeof(ifctx->call);
7426ca35587Sdholland 			auio.uio_td = td;
7436ca35587Sdholland 
7446ca35587Sdholland 			/* Set netmask to 0.0.0.0 */
7456ca35587Sdholland 			clear_sinaddr(sin);
746*0342d508Spgoyette 			error = ifioctl(bootp_so, SIOCAIFADDR, (void *)ifra,
7476ca35587Sdholland 			    td);
7486ca35587Sdholland 			if (error != 0)
7496ca35587Sdholland 				panic("%s: SIOCAIFADDR, error=%d", __func__,
7506ca35587Sdholland 				    error);
7516ca35587Sdholland 
7526ca35587Sdholland 			error = sosend(bootp_so, (struct sockaddr *) &dst,
7536ca35587Sdholland 				       &auio, NULL, NULL, 0, td);
7546ca35587Sdholland 			if (error != 0)
7556ca35587Sdholland 				printf("%s: sosend: %d state %08x\n", __func__,
7566ca35587Sdholland 				    error, (int )bootp_so->so_state);
7576ca35587Sdholland 
7586ca35587Sdholland 			/* Set netmask to 255.0.0.0 */
7596ca35587Sdholland 			sin->sin_addr.s_addr = htonl(IN_CLASSA_NET);
760*0342d508Spgoyette 			error = ifioctl(bootp_so, SIOCAIFADDR, (void *)ifra,
7616ca35587Sdholland 			    td);
7626ca35587Sdholland 			if (error != 0)
7636ca35587Sdholland 				panic("%s: SIOCAIFADDR, error=%d", __func__,
7646ca35587Sdholland 				    error);
7656ca35587Sdholland 		}
7666ca35587Sdholland 
7676ca35587Sdholland 		if (outstanding == 0 &&
7686ca35587Sdholland 		    (rtimo == 0 || time_second >= rtimo)) {
7696ca35587Sdholland 			error = 0;
7706ca35587Sdholland 			goto out;
7716ca35587Sdholland 		}
7726ca35587Sdholland 
7736ca35587Sdholland 		/* Determine new timeout. */
7746ca35587Sdholland 		if (timo < MAX_RESEND_DELAY)
7756ca35587Sdholland 			timo++;
7766ca35587Sdholland 		else {
7776ca35587Sdholland 			printf("DHCP/BOOTP timeout for server ");
7786ca35587Sdholland 			print_sin_addr(&dst);
7796ca35587Sdholland 			printf("\n");
7806ca35587Sdholland 		}
7816ca35587Sdholland 
7826ca35587Sdholland 		/*
7836ca35587Sdholland 		 * Wait for up to timo seconds for a reply.
7846ca35587Sdholland 		 * The socket receive timeout was set to 1 second.
7856ca35587Sdholland 		 */
7866ca35587Sdholland 		atimo = timo + time_second;
7876ca35587Sdholland 		while (time_second < atimo) {
788*0342d508Spgoyette 			aio.iov_base = (void *) &gctx->reply;
7896ca35587Sdholland 			aio.iov_len = sizeof(gctx->reply);
7906ca35587Sdholland 
7916ca35587Sdholland 			auio.uio_iov = &aio;
7926ca35587Sdholland 			auio.uio_iovcnt = 1;
7936ca35587Sdholland 			auio.uio_segflg = UIO_SYSSPACE;
7946ca35587Sdholland 			auio.uio_rw = UIO_READ;
7956ca35587Sdholland 			auio.uio_offset = 0;
7966ca35587Sdholland 			auio.uio_resid = sizeof(gctx->reply);
7976ca35587Sdholland 			auio.uio_td = td;
7986ca35587Sdholland 
7996ca35587Sdholland 			rcvflg = 0;
8006ca35587Sdholland 			error = soreceive(bootp_so, NULL, &auio,
8016ca35587Sdholland 					  NULL, NULL, &rcvflg);
8026ca35587Sdholland 			gctx->secs = time_second - gctx->starttime;
8036ca35587Sdholland 			STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
8046ca35587Sdholland 				if (bootpc_ifctx_isresolved(ifctx) != 0 ||
8056ca35587Sdholland 				    bootpc_ifctx_isfailed(ifctx) != 0)
8066ca35587Sdholland 					continue;
8076ca35587Sdholland 
8086ca35587Sdholland 				ifctx->call.secs = htons(gctx->secs);
8096ca35587Sdholland 			}
8106ca35587Sdholland 			if (error == EWOULDBLOCK)
8116ca35587Sdholland 				continue;
8126ca35587Sdholland 			if (error != 0)
8136ca35587Sdholland 				goto out;
8146ca35587Sdholland 			len = sizeof(gctx->reply) - auio.uio_resid;
8156ca35587Sdholland 
8166ca35587Sdholland 			/* Do we have the required number of bytes ? */
8176ca35587Sdholland 			if (len < BOOTP_MIN_LEN)
8186ca35587Sdholland 				continue;
8196ca35587Sdholland 			gctx->replylen = len;
8206ca35587Sdholland 
8216ca35587Sdholland 			/* Is it a reply? */
8226ca35587Sdholland 			if (gctx->reply.op != BOOTP_REPLY)
8236ca35587Sdholland 				continue;
8246ca35587Sdholland 
8256ca35587Sdholland 			/* Is this an answer to our query */
8266ca35587Sdholland 			STAILQ_FOREACH(ifctx, &gctx->interfaces, next) {
8276ca35587Sdholland 				if (gctx->reply.xid != ifctx->call.xid)
8286ca35587Sdholland 					continue;
8296ca35587Sdholland 
8306ca35587Sdholland 				/* Same HW address size ? */
8316ca35587Sdholland 				if (gctx->reply.hlen != ifctx->call.hlen)
8326ca35587Sdholland 					continue;
8336ca35587Sdholland 
8346ca35587Sdholland 				/* Correct HW address ? */
8356ca35587Sdholland 				if (bcmp(gctx->reply.chaddr,
8366ca35587Sdholland 					 ifctx->call.chaddr,
8376ca35587Sdholland 					 ifctx->call.hlen) != 0)
8386ca35587Sdholland 					continue;
8396ca35587Sdholland 
8406ca35587Sdholland 				break;
8416ca35587Sdholland 			}
8426ca35587Sdholland 
8436ca35587Sdholland 			if (ifctx != NULL) {
8446ca35587Sdholland 				s =  bootpc_tag(&gctx->tmptag,
8456ca35587Sdholland 						&gctx->reply,
8466ca35587Sdholland 						gctx->replylen,
8476ca35587Sdholland 						TAG_DHCP_MSGTYPE);
8486ca35587Sdholland 				if (s != NULL) {
8496ca35587Sdholland 					switch (*s) {
8506ca35587Sdholland 					case DHCP_OFFER:
8516ca35587Sdholland 						s = "DHCP Offer";
8526ca35587Sdholland 						break;
8536ca35587Sdholland 					case DHCP_ACK:
8546ca35587Sdholland 						s = "DHCP Ack";
8556ca35587Sdholland 						break;
8566ca35587Sdholland 					default:
8576ca35587Sdholland 						s = "DHCP (unexpected)";
8586ca35587Sdholland 						break;
8596ca35587Sdholland 					}
8606ca35587Sdholland 				} else
8616ca35587Sdholland 					s = "BOOTP Reply";
8626ca35587Sdholland 
8636ca35587Sdholland 				printf("Received %s packet"
8646ca35587Sdholland 				       " on %s from ",
8656ca35587Sdholland 				       s,
8666ca35587Sdholland 				       ifctx->ireq.ifr_name);
8676ca35587Sdholland 				print_in_addr(gctx->reply.siaddr);
8686ca35587Sdholland 				if (gctx->reply.giaddr.s_addr !=
8696ca35587Sdholland 				    htonl(INADDR_ANY)) {
8706ca35587Sdholland 					printf(" via ");
8716ca35587Sdholland 					print_in_addr(gctx->reply.giaddr);
8726ca35587Sdholland 				}
8736ca35587Sdholland 				if (bootpc_received(gctx, ifctx) != 0) {
8746ca35587Sdholland 					printf(" (accepted)");
8756ca35587Sdholland 					if (ifctx->outstanding) {
8766ca35587Sdholland 						ifctx->outstanding = 0;
8776ca35587Sdholland 						outstanding--;
8786ca35587Sdholland 					}
8796ca35587Sdholland 					/* Network settle delay */
8806ca35587Sdholland 					if (outstanding == 0)
8816ca35587Sdholland 						atimo = time_second +
8826ca35587Sdholland 							BOOTP_SETTLE_DELAY;
8836ca35587Sdholland 				} else
8846ca35587Sdholland 					printf(" (ignored)");
8856ca35587Sdholland 				if (ifctx->gotrootpath ||
8866ca35587Sdholland 				    gctx->any_root_overrides) {
8876ca35587Sdholland 					gotrootpath = 1;
8886ca35587Sdholland 					rtimo = time_second +
8896ca35587Sdholland 						BOOTP_SETTLE_DELAY;
8906ca35587Sdholland 					if (ifctx->gotrootpath)
8916ca35587Sdholland 						printf(" (got root path)");
8926ca35587Sdholland 				}
8936ca35587Sdholland 				printf("\n");
8946ca35587Sdholland 			}
8956ca35587Sdholland 		} /* while secs */
8966ca35587Sdholland #ifdef BOOTP_TIMEOUT
8976ca35587Sdholland 		if (gctx->secs > BOOTP_TIMEOUT && BOOTP_TIMEOUT > 0)
8986ca35587Sdholland 			break;
8996ca35587Sdholland #endif
9006ca35587Sdholland 		/* Force a retry if halfway in DHCP negotiation */
9016ca35587Sdholland 		retry = 0;
9026ca35587Sdholland 		STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
9036ca35587Sdholland 			if (ifctx->state == IF_DHCP_OFFERED) {
9046ca35587Sdholland 				if (ifctx->dhcpquerytype == DHCP_DISCOVER)
9056ca35587Sdholland 					retry = 1;
9066ca35587Sdholland 				else
9076ca35587Sdholland 					ifctx->state = IF_DHCP_UNRESOLVED;
9086ca35587Sdholland 			}
9096ca35587Sdholland 
9106ca35587Sdholland 		if (retry != 0)
9116ca35587Sdholland 			continue;
9126ca35587Sdholland 
9136ca35587Sdholland 		if (gotrootpath != 0) {
9146ca35587Sdholland 			gctx->gotrootpath = gotrootpath;
9156ca35587Sdholland 			if (rtimo != 0 && time_second >= rtimo)
9166ca35587Sdholland 				break;
9176ca35587Sdholland 		}
9186ca35587Sdholland 	} /* forever send/receive */
9196ca35587Sdholland 
9206ca35587Sdholland 	/*
9216ca35587Sdholland 	 * XXX: These are errors of varying seriousness being silently
9226ca35587Sdholland 	 * ignored
9236ca35587Sdholland 	 */
9246ca35587Sdholland 
9256ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
9266ca35587Sdholland 		if (bootpc_ifctx_isresolved(ifctx) == 0) {
9276ca35587Sdholland 			printf("%s timeout for interface %s\n",
9286ca35587Sdholland 			       ifctx->dhcpquerytype != DHCP_NOMSG ?
9296ca35587Sdholland 			       "DHCP" : "BOOTP",
9306ca35587Sdholland 			       ifctx->ireq.ifr_name);
9316ca35587Sdholland 		}
9326ca35587Sdholland 
9336ca35587Sdholland 	if (gctx->gotrootpath != 0) {
9346ca35587Sdholland #if 0
9356ca35587Sdholland 		printf("Got a root path, ignoring remaining timeout\n");
9366ca35587Sdholland #endif
9376ca35587Sdholland 		error = 0;
9386ca35587Sdholland 		goto out;
9396ca35587Sdholland 	}
9406ca35587Sdholland #ifndef BOOTP_NFSROOT
9416ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
9426ca35587Sdholland 		if (bootpc_ifctx_isresolved(ifctx) != 0) {
9436ca35587Sdholland 			error = 0;
9446ca35587Sdholland 			goto out;
9456ca35587Sdholland 		}
9466ca35587Sdholland #endif
9476ca35587Sdholland 	error = ETIMEDOUT;
9486ca35587Sdholland 
9496ca35587Sdholland out:
9506ca35587Sdholland 	return (error);
9516ca35587Sdholland }
9526ca35587Sdholland 
9536ca35587Sdholland static void
bootpc_fakeup_interface(struct bootpc_ifcontext * ifctx,struct lwp * td)954*0342d508Spgoyette bootpc_fakeup_interface(struct bootpc_ifcontext *ifctx, struct lwp *td)
9556ca35587Sdholland {
9566ca35587Sdholland 	struct ifreq *ifr;
9576ca35587Sdholland 	struct in_aliasreq *ifra;
9586ca35587Sdholland 	struct sockaddr_in *sin;
9596ca35587Sdholland 	int error;
9606ca35587Sdholland 
9616ca35587Sdholland 	ifr = &ifctx->ireq;
9626ca35587Sdholland 	ifra = &ifctx->iareq;
9636ca35587Sdholland 
9646ca35587Sdholland 	/*
9656ca35587Sdholland 	 * Bring up the interface.
9666ca35587Sdholland 	 *
9676ca35587Sdholland 	 * Get the old interface flags and or IFF_UP into them; if
9686ca35587Sdholland 	 * IFF_UP set blindly, interface selection can be clobbered.
9696ca35587Sdholland 	 */
970*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCGIFFLAGS, (void *)ifr, td);
9716ca35587Sdholland 	if (error != 0)
9726ca35587Sdholland 		panic("%s: SIOCGIFFLAGS, error=%d", __func__, error);
9736ca35587Sdholland 	ifr->ifr_flags |= IFF_UP;
974*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCSIFFLAGS, (void *)ifr, td);
9756ca35587Sdholland 	if (error != 0)
9766ca35587Sdholland 		panic("%s: SIOCSIFFLAGS, error=%d", __func__, error);
9776ca35587Sdholland 
9786ca35587Sdholland 	/*
9796ca35587Sdholland 	 * Do enough of ifconfig(8) so that the chosen interface
9806ca35587Sdholland 	 * can talk to the servers. Set address to 0.0.0.0/8 and
9816ca35587Sdholland 	 * broadcast address to local broadcast.
9826ca35587Sdholland 	 */
9836ca35587Sdholland 	sin = (struct sockaddr_in *)&ifra->ifra_addr;
9846ca35587Sdholland 	clear_sinaddr(sin);
9856ca35587Sdholland 	sin = (struct sockaddr_in *)&ifra->ifra_mask;
9866ca35587Sdholland 	clear_sinaddr(sin);
9876ca35587Sdholland 	sin->sin_addr.s_addr = htonl(IN_CLASSA_NET);
9886ca35587Sdholland 	sin = (struct sockaddr_in *)&ifra->ifra_broadaddr;
9896ca35587Sdholland 	clear_sinaddr(sin);
9906ca35587Sdholland 	sin->sin_addr.s_addr = htonl(INADDR_BROADCAST);
991*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCAIFADDR, (void *)ifra, td);
9926ca35587Sdholland 	if (error != 0)
9936ca35587Sdholland 		panic("%s: SIOCAIFADDR, error=%d", __func__, error);
9946ca35587Sdholland }
9956ca35587Sdholland 
9966ca35587Sdholland static void
bootpc_shutdown_interface(struct bootpc_ifcontext * ifctx,struct lwp * td)997*0342d508Spgoyette bootpc_shutdown_interface(struct bootpc_ifcontext *ifctx, struct lwp *td)
9986ca35587Sdholland {
9996ca35587Sdholland 	struct ifreq *ifr;
10006ca35587Sdholland 	struct sockaddr_in *sin;
10016ca35587Sdholland 	int error;
10026ca35587Sdholland 
10036ca35587Sdholland 	ifr = &ifctx->ireq;
10046ca35587Sdholland 
10056ca35587Sdholland 	printf("Shutdown interface %s\n", ifctx->ireq.ifr_name);
1006*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCGIFFLAGS, (void *)ifr, td);
10076ca35587Sdholland 	if (error != 0)
10086ca35587Sdholland 		panic("%s: SIOCGIFFLAGS, error=%d", __func__, error);
10096ca35587Sdholland 	ifr->ifr_flags &= ~IFF_UP;
1010*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCSIFFLAGS, (void *)ifr, td);
10116ca35587Sdholland 	if (error != 0)
10126ca35587Sdholland 		panic("%s: SIOCSIFFLAGS, error=%d", __func__, error);
10136ca35587Sdholland 
10146ca35587Sdholland 	sin = (struct sockaddr_in *) &ifr->ifr_addr;
10156ca35587Sdholland 	clear_sinaddr(sin);
1016*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCDIFADDR, (void *) ifr, td);
10176ca35587Sdholland 	if (error != 0)
10186ca35587Sdholland 		panic("%s: SIOCDIFADDR, error=%d", __func__, error);
10196ca35587Sdholland }
10206ca35587Sdholland 
1021e81f0ea2Spgoyette static void
bootpc_adjust_interface(struct bootpc_ifcontext * ifctx,struct bootpc_globalcontext * gctx,struct lwp * td)10226ca35587Sdholland bootpc_adjust_interface(struct bootpc_ifcontext *ifctx,
1023*0342d508Spgoyette     struct bootpc_globalcontext *gctx, struct lwp *td)
10246ca35587Sdholland {
10256ca35587Sdholland 	int error;
10266ca35587Sdholland 	struct sockaddr_in *sin;
10276ca35587Sdholland 	struct ifreq *ifr;
10286ca35587Sdholland 	struct in_aliasreq *ifra;
10296ca35587Sdholland 	struct sockaddr_in *myaddr;
10306ca35587Sdholland 	struct sockaddr_in *netmask;
10316ca35587Sdholland 
10326ca35587Sdholland 	ifr = &ifctx->ireq;
10336ca35587Sdholland 	ifra = &ifctx->iareq;
10346ca35587Sdholland 	myaddr = &ifctx->myaddr;
10356ca35587Sdholland 	netmask = &ifctx->netmask;
10366ca35587Sdholland 
10376ca35587Sdholland 	if (bootpc_ifctx_isresolved(ifctx) == 0) {
10386ca35587Sdholland 		/* Shutdown interfaces where BOOTP failed */
10396ca35587Sdholland 		bootpc_shutdown_interface(ifctx, td);
1040e81f0ea2Spgoyette 		return;
10416ca35587Sdholland 	}
10426ca35587Sdholland 
1043e81f0ea2Spgoyette 	printf("Adjusted interface %s", ifctx->ireq.ifr_name);
1044e81f0ea2Spgoyette 
1045e81f0ea2Spgoyette 	/* Do BOOTP interface options */
1046e81f0ea2Spgoyette 	if (ifctx->mtu != 0) {
1047e81f0ea2Spgoyette 		printf(" (MTU=%d%s)", ifctx->mtu,
1048e81f0ea2Spgoyette 		    (ifctx->mtu > 1514) ? "/JUMBO" : "");
1049e81f0ea2Spgoyette 		ifr->ifr_mtu = ifctx->mtu;
1050*0342d508Spgoyette 		error = ifioctl(bootp_so, SIOCSIFMTU, (void *) ifr, td);
1051e81f0ea2Spgoyette 		if (error != 0)
1052e81f0ea2Spgoyette 			panic("%s: SIOCSIFMTU, error=%d", __func__, error);
1053e81f0ea2Spgoyette 	}
1054e81f0ea2Spgoyette 	printf("\n");
1055e81f0ea2Spgoyette 
10566ca35587Sdholland 	/*
10576ca35587Sdholland 	 * Do enough of ifconfig(8) so that the chosen interface
10586ca35587Sdholland 	 * can talk to the servers.  (just set the address)
10596ca35587Sdholland 	 */
10606ca35587Sdholland 	sin = (struct sockaddr_in *) &ifr->ifr_addr;
10616ca35587Sdholland 	clear_sinaddr(sin);
1062*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCDIFADDR, (void *) ifr, td);
10636ca35587Sdholland 	if (error != 0)
10646ca35587Sdholland 		panic("%s: SIOCDIFADDR, error=%d", __func__, error);
10656ca35587Sdholland 
10666ca35587Sdholland 	bcopy(myaddr, &ifra->ifra_addr, sizeof(*myaddr));
10676ca35587Sdholland 	bcopy(netmask, &ifra->ifra_mask, sizeof(*netmask));
10686ca35587Sdholland 	clear_sinaddr(&ifra->ifra_broadaddr);
10696ca35587Sdholland 	ifra->ifra_broadaddr.sin_addr.s_addr = myaddr->sin_addr.s_addr |
10706ca35587Sdholland 	    ~netmask->sin_addr.s_addr;
10716ca35587Sdholland 
1072*0342d508Spgoyette 	error = ifioctl(bootp_so, SIOCAIFADDR, (void *)ifra, td);
10736ca35587Sdholland 	if (error != 0)
10746ca35587Sdholland 		panic("%s: SIOCAIFADDR, error=%d", __func__, error);
1075e81f0ea2Spgoyette }
10766ca35587Sdholland 
1077e81f0ea2Spgoyette static void
bootpc_add_default_route(struct bootpc_ifcontext * ifctx)1078e81f0ea2Spgoyette bootpc_add_default_route(struct bootpc_ifcontext *ifctx)
1079e81f0ea2Spgoyette {
1080e81f0ea2Spgoyette 	int error;
1081e81f0ea2Spgoyette 	struct sockaddr_in defdst;
1082e81f0ea2Spgoyette 	struct sockaddr_in defmask;
10836ca35587Sdholland 
1084e81f0ea2Spgoyette 	if (ifctx->gw.sin_addr.s_addr == htonl(INADDR_ANY))
1085e81f0ea2Spgoyette 		return;
1086e81f0ea2Spgoyette 
10876ca35587Sdholland 	clear_sinaddr(&defdst);
10886ca35587Sdholland 	clear_sinaddr(&defmask);
1089e81f0ea2Spgoyette 
1090e81f0ea2Spgoyette 	error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&defdst,
1091e81f0ea2Spgoyette 	    (struct sockaddr *) &ifctx->gw, (struct sockaddr *)&defmask,
10926ca35587Sdholland 	    (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB);
10936ca35587Sdholland 	if (error != 0) {
10946ca35587Sdholland 		printf("%s: RTM_ADD, error=%d\n", __func__, error);
10956ca35587Sdholland 	}
10966ca35587Sdholland }
10976ca35587Sdholland 
1098e81f0ea2Spgoyette static void
bootpc_remove_default_route(struct bootpc_ifcontext * ifctx)1099e81f0ea2Spgoyette bootpc_remove_default_route(struct bootpc_ifcontext *ifctx)
1100e81f0ea2Spgoyette {
1101e81f0ea2Spgoyette 	int error;
1102e81f0ea2Spgoyette 	struct sockaddr_in defdst;
1103e81f0ea2Spgoyette 	struct sockaddr_in defmask;
1104e81f0ea2Spgoyette 
1105e81f0ea2Spgoyette 	if (ifctx->gw.sin_addr.s_addr == htonl(INADDR_ANY))
1106e81f0ea2Spgoyette 		return;
1107e81f0ea2Spgoyette 
1108e81f0ea2Spgoyette 	clear_sinaddr(&defdst);
1109e81f0ea2Spgoyette 	clear_sinaddr(&defmask);
1110e81f0ea2Spgoyette 
1111e81f0ea2Spgoyette 	error = rtrequest_fib(RTM_DELETE, (struct sockaddr *)&defdst,
1112e81f0ea2Spgoyette 	    (struct sockaddr *) &ifctx->gw, (struct sockaddr *)&defmask,
1113e81f0ea2Spgoyette 	    (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL, RT_DEFAULT_FIB);
1114e81f0ea2Spgoyette 	if (error != 0) {
1115e81f0ea2Spgoyette 		printf("%s: RTM_DELETE, error=%d\n", __func__, error);
1116e81f0ea2Spgoyette 	}
11176ca35587Sdholland }
11186ca35587Sdholland 
11196ca35587Sdholland static int
setmyfs(struct sockaddr_in * addr,char * path,char * p,const struct in_addr * siaddr)1120*0342d508Spgoyette setmyfs(struct sockaddr_in *addr, char *path, char *p,
11216ca35587Sdholland     const struct in_addr *siaddr)
11226ca35587Sdholland {
11236ca35587Sdholland 
11246ca35587Sdholland 	if (getip(&p, &addr->sin_addr) == 0) {
11256ca35587Sdholland 		if (siaddr != NULL && *p == '/')
11266ca35587Sdholland 			bcopy(siaddr, &addr->sin_addr, sizeof(struct in_addr));
11276ca35587Sdholland 		else
11286ca35587Sdholland 			return 0;
11296ca35587Sdholland 	} else {
11306ca35587Sdholland 		if (*p != ':')
11316ca35587Sdholland 			return 0;
11326ca35587Sdholland 		p++;
11336ca35587Sdholland 	}
11346ca35587Sdholland 
11356ca35587Sdholland 	addr->sin_len = sizeof(struct sockaddr_in);
11366ca35587Sdholland 	addr->sin_family = AF_INET;
11376ca35587Sdholland 
11386ca35587Sdholland 	strlcpy(path, p, MNAMELEN);
11396ca35587Sdholland 	return 1;
11406ca35587Sdholland }
11416ca35587Sdholland 
11426ca35587Sdholland static int
getip(char ** ptr,struct in_addr * addr)11436ca35587Sdholland getip(char **ptr, struct in_addr *addr)
11446ca35587Sdholland {
11456ca35587Sdholland 	char *p;
11466ca35587Sdholland 	unsigned int ip;
11476ca35587Sdholland 	int val;
11486ca35587Sdholland 
11496ca35587Sdholland 	p = *ptr;
11506ca35587Sdholland 	ip = 0;
11516ca35587Sdholland 	if (((val = getdec(&p)) < 0) || (val > 255))
11526ca35587Sdholland 		return 0;
11536ca35587Sdholland 	ip = val << 24;
11546ca35587Sdholland 	if (*p != '.')
11556ca35587Sdholland 		return 0;
11566ca35587Sdholland 	p++;
11576ca35587Sdholland 	if (((val = getdec(&p)) < 0) || (val > 255))
11586ca35587Sdholland 		return 0;
11596ca35587Sdholland 	ip |= (val << 16);
11606ca35587Sdholland 	if (*p != '.')
11616ca35587Sdholland 		return 0;
11626ca35587Sdholland 	p++;
11636ca35587Sdholland 	if (((val = getdec(&p)) < 0) || (val > 255))
11646ca35587Sdholland 		return 0;
11656ca35587Sdholland 	ip |= (val << 8);
11666ca35587Sdholland 	if (*p != '.')
11676ca35587Sdholland 		return 0;
11686ca35587Sdholland 	p++;
11696ca35587Sdholland 	if (((val = getdec(&p)) < 0) || (val > 255))
11706ca35587Sdholland 		return 0;
11716ca35587Sdholland 	ip |= val;
11726ca35587Sdholland 
11736ca35587Sdholland 	addr->s_addr = htonl(ip);
11746ca35587Sdholland 	*ptr = p;
11756ca35587Sdholland 	return 1;
11766ca35587Sdholland }
11776ca35587Sdholland 
11786ca35587Sdholland static int
getdec(char ** ptr)11796ca35587Sdholland getdec(char **ptr)
11806ca35587Sdholland {
11816ca35587Sdholland 	char *p;
11826ca35587Sdholland 	int ret;
11836ca35587Sdholland 
11846ca35587Sdholland 	p = *ptr;
11856ca35587Sdholland 	ret = 0;
11866ca35587Sdholland 	if ((*p < '0') || (*p > '9'))
11876ca35587Sdholland 		return -1;
11886ca35587Sdholland 	while ((*p >= '0') && (*p <= '9')) {
11896ca35587Sdholland 		ret = ret * 10 + (*p - '0');
11906ca35587Sdholland 		p++;
11916ca35587Sdholland 	}
11926ca35587Sdholland 	*ptr = p;
11936ca35587Sdholland 	return ret;
11946ca35587Sdholland }
11956ca35587Sdholland 
11966ca35587Sdholland static void
mountopts(struct nfs_args * args,char * p)11976ca35587Sdholland mountopts(struct nfs_args *args, char *p)
11986ca35587Sdholland {
11996ca35587Sdholland 	args->version = NFS_ARGSVERSION;
12006ca35587Sdholland 	args->rsize = BOOTP_BLOCKSIZE;
12016ca35587Sdholland 	args->wsize = BOOTP_BLOCKSIZE;
12026ca35587Sdholland 	args->flags = NFSMNT_RSIZE | NFSMNT_WSIZE | NFSMNT_RESVPORT;
12036ca35587Sdholland 	args->sotype = SOCK_DGRAM;
12046ca35587Sdholland 	if (p != NULL)
12056ca35587Sdholland 		nfs_parse_options(p, args);
12066ca35587Sdholland }
12076ca35587Sdholland 
12086ca35587Sdholland static int
xdr_opaque_decode(struct mbuf ** mptr,u_char * buf,int len)12096ca35587Sdholland xdr_opaque_decode(struct mbuf **mptr, u_char *buf, int len)
12106ca35587Sdholland {
12116ca35587Sdholland 	struct mbuf *m;
12126ca35587Sdholland 	int alignedlen;
12136ca35587Sdholland 
12146ca35587Sdholland 	m = *mptr;
12156ca35587Sdholland 	alignedlen = ( len + 3 ) & ~3;
12166ca35587Sdholland 
12176ca35587Sdholland 	if (m->m_len < alignedlen) {
12186ca35587Sdholland 		m = m_pullup(m, alignedlen);
12196ca35587Sdholland 		if (m == NULL) {
12206ca35587Sdholland 			*mptr = NULL;
12216ca35587Sdholland 			return EBADRPC;
12226ca35587Sdholland 		}
12236ca35587Sdholland 	}
12246ca35587Sdholland 	bcopy(mtod(m, u_char *), buf, len);
12256ca35587Sdholland 	m_adj(m, alignedlen);
12266ca35587Sdholland 	*mptr = m;
12276ca35587Sdholland 	return 0;
12286ca35587Sdholland }
12296ca35587Sdholland 
12306ca35587Sdholland static int
xdr_int_decode(struct mbuf ** mptr,int * iptr)12316ca35587Sdholland xdr_int_decode(struct mbuf **mptr, int *iptr)
12326ca35587Sdholland {
12336ca35587Sdholland 	u_int32_t i;
12346ca35587Sdholland 
12356ca35587Sdholland 	if (xdr_opaque_decode(mptr, (u_char *) &i, sizeof(u_int32_t)) != 0)
12366ca35587Sdholland 		return EBADRPC;
12376ca35587Sdholland 	*iptr = fxdr_unsigned(u_int32_t, i);
12386ca35587Sdholland 	return 0;
12396ca35587Sdholland }
12406ca35587Sdholland 
12416ca35587Sdholland static void
print_sin_addr(struct sockaddr_in * sin)12426ca35587Sdholland print_sin_addr(struct sockaddr_in *sin)
12436ca35587Sdholland {
12446ca35587Sdholland 
12456ca35587Sdholland 	print_in_addr(sin->sin_addr);
12466ca35587Sdholland }
12476ca35587Sdholland 
12486ca35587Sdholland static void
print_in_addr(struct in_addr addr)12496ca35587Sdholland print_in_addr(struct in_addr addr)
12506ca35587Sdholland {
12516ca35587Sdholland 	unsigned int ip;
12526ca35587Sdholland 
12536ca35587Sdholland 	ip = ntohl(addr.s_addr);
12546ca35587Sdholland 	printf("%d.%d.%d.%d",
12556ca35587Sdholland 	       ip >> 24, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255);
12566ca35587Sdholland }
12576ca35587Sdholland 
12586ca35587Sdholland static void
bootpc_compose_query(struct bootpc_ifcontext * ifctx,struct lwp * td)1259*0342d508Spgoyette bootpc_compose_query(struct bootpc_ifcontext *ifctx, struct lwp *td)
12606ca35587Sdholland {
12616ca35587Sdholland 	unsigned char *vendp;
12626ca35587Sdholland 	unsigned char vendor_client[64];
12636ca35587Sdholland 	uint32_t leasetime;
12646ca35587Sdholland 	uint8_t vendor_client_len;
12656ca35587Sdholland 
12666ca35587Sdholland 	ifctx->gotrootpath = 0;
12676ca35587Sdholland 
1268*0342d508Spgoyette 	bzero((void *) &ifctx->call, sizeof(ifctx->call));
12696ca35587Sdholland 
12706ca35587Sdholland 	/* bootpc part */
12716ca35587Sdholland 	ifctx->call.op = BOOTP_REQUEST; 	/* BOOTREQUEST */
12726ca35587Sdholland 	ifctx->call.htype = 1;			/* 10mb ethernet */
12736ca35587Sdholland 	ifctx->call.hlen = ifctx->sdl->sdl_alen;/* Hardware address length */
12746ca35587Sdholland 	ifctx->call.hops = 0;
12756ca35587Sdholland 	if (bootpc_ifctx_isunresolved(ifctx) != 0)
12766ca35587Sdholland 		ifctx->xid++;
12776ca35587Sdholland 	ifctx->call.xid = txdr_unsigned(ifctx->xid);
12786ca35587Sdholland 	bcopy(LLADDR(ifctx->sdl), &ifctx->call.chaddr, ifctx->sdl->sdl_alen);
12796ca35587Sdholland 
12806ca35587Sdholland 	vendp = ifctx->call.vend;
12816ca35587Sdholland 	*vendp++ = 99;		/* RFC1048 cookie */
12826ca35587Sdholland 	*vendp++ = 130;
12836ca35587Sdholland 	*vendp++ = 83;
12846ca35587Sdholland 	*vendp++ = 99;
12856ca35587Sdholland 	*vendp++ = TAG_MAXMSGSIZE;
12866ca35587Sdholland 	*vendp++ = 2;
12876ca35587Sdholland 	*vendp++ = (sizeof(struct bootp_packet) >> 8) & 255;
12886ca35587Sdholland 	*vendp++ = sizeof(struct bootp_packet) & 255;
12896ca35587Sdholland 
12906ca35587Sdholland 	snprintf(vendor_client, sizeof(vendor_client), "%s:%s:%s",
12916ca35587Sdholland 		ostype, MACHINE, osrelease);
12926ca35587Sdholland 	vendor_client_len = strlen(vendor_client);
12936ca35587Sdholland 	*vendp++ = TAG_VENDOR_INDENTIFIER;
12946ca35587Sdholland 	*vendp++ = vendor_client_len;
12956ca35587Sdholland 	memcpy(vendp, vendor_client, vendor_client_len);
12966ca35587Sdholland 	vendp += vendor_client_len;
12976ca35587Sdholland 	ifctx->dhcpquerytype = DHCP_NOMSG;
12986ca35587Sdholland 	switch (ifctx->state) {
12996ca35587Sdholland 	case IF_DHCP_UNRESOLVED:
13006ca35587Sdholland 		*vendp++ = TAG_DHCP_MSGTYPE;
13016ca35587Sdholland 		*vendp++ = 1;
13026ca35587Sdholland 		*vendp++ = DHCP_DISCOVER;
13036ca35587Sdholland 		ifctx->dhcpquerytype = DHCP_DISCOVER;
13046ca35587Sdholland 		ifctx->gotdhcpserver = 0;
13056ca35587Sdholland 		break;
13066ca35587Sdholland 	case IF_DHCP_OFFERED:
13076ca35587Sdholland 		*vendp++ = TAG_DHCP_MSGTYPE;
13086ca35587Sdholland 		*vendp++ = 1;
13096ca35587Sdholland 		*vendp++ = DHCP_REQUEST;
13106ca35587Sdholland 		ifctx->dhcpquerytype = DHCP_REQUEST;
13116ca35587Sdholland 		*vendp++ = TAG_DHCP_REQ_ADDR;
13126ca35587Sdholland 		*vendp++ = 4;
13136ca35587Sdholland 		memcpy(vendp, &ifctx->reply.yiaddr, 4);
13146ca35587Sdholland 		vendp += 4;
13156ca35587Sdholland 		if (ifctx->gotdhcpserver != 0) {
13166ca35587Sdholland 			*vendp++ = TAG_DHCP_SERVERID;
13176ca35587Sdholland 			*vendp++ = 4;
13186ca35587Sdholland 			memcpy(vendp, &ifctx->dhcpserver, 4);
13196ca35587Sdholland 			vendp += 4;
13206ca35587Sdholland 		}
13216ca35587Sdholland 		*vendp++ = TAG_DHCP_LEASETIME;
13226ca35587Sdholland 		*vendp++ = 4;
13236ca35587Sdholland 		leasetime = htonl(300);
13246ca35587Sdholland 		memcpy(vendp, &leasetime, 4);
13256ca35587Sdholland 		vendp += 4;
13266ca35587Sdholland 		break;
13276ca35587Sdholland 	default:
13286ca35587Sdholland 		break;
13296ca35587Sdholland 	}
13306ca35587Sdholland 	*vendp = TAG_END;
13316ca35587Sdholland 
13326ca35587Sdholland 	ifctx->call.secs = 0;
13336ca35587Sdholland 	ifctx->call.flags = htons(0x8000); /* We need a broadcast answer */
13346ca35587Sdholland }
13356ca35587Sdholland 
13366ca35587Sdholland static int
bootpc_hascookie(struct bootp_packet * bp)13376ca35587Sdholland bootpc_hascookie(struct bootp_packet *bp)
13386ca35587Sdholland {
13396ca35587Sdholland 
13406ca35587Sdholland 	return (bp->vend[0] == 99 && bp->vend[1] == 130 &&
13416ca35587Sdholland 		bp->vend[2] == 83 && bp->vend[3] == 99);
13426ca35587Sdholland }
13436ca35587Sdholland 
13446ca35587Sdholland static void
bootpc_tag_helper(struct bootpc_tagcontext * tctx,unsigned char * start,int len,int tag)13456ca35587Sdholland bootpc_tag_helper(struct bootpc_tagcontext *tctx,
13466ca35587Sdholland     unsigned char *start, int len, int tag)
13476ca35587Sdholland {
13486ca35587Sdholland 	unsigned char *j;
13496ca35587Sdholland 	unsigned char *ej;
13506ca35587Sdholland 	unsigned char code;
13516ca35587Sdholland 
13526ca35587Sdholland 	if (tctx->badtag != 0 || tctx->badopt != 0)
13536ca35587Sdholland 		return;
13546ca35587Sdholland 
13556ca35587Sdholland 	j = start;
13566ca35587Sdholland 	ej = j + len;
13576ca35587Sdholland 
13586ca35587Sdholland 	while (j < ej) {
13596ca35587Sdholland 		code = *j++;
13606ca35587Sdholland 		if (code == TAG_PAD)
13616ca35587Sdholland 			continue;
13626ca35587Sdholland 		if (code == TAG_END)
13636ca35587Sdholland 			return;
13646ca35587Sdholland 		if (j >= ej || j + *j + 1 > ej) {
13656ca35587Sdholland 			tctx->badopt = 1;
13666ca35587Sdholland 			return;
13676ca35587Sdholland 		}
13686ca35587Sdholland 		len = *j++;
13696ca35587Sdholland 		if (code == tag) {
13706ca35587Sdholland 			if (tctx->taglen + len > TAG_MAXLEN) {
13716ca35587Sdholland 				tctx->badtag = 1;
13726ca35587Sdholland 				return;
13736ca35587Sdholland 			}
13746ca35587Sdholland 			tctx->foundopt = 1;
13756ca35587Sdholland 			if (len > 0)
13766ca35587Sdholland 				memcpy(tctx->buf + tctx->taglen,
13776ca35587Sdholland 				       j, len);
13786ca35587Sdholland 			tctx->taglen += len;
13796ca35587Sdholland 		}
13806ca35587Sdholland 		if (code == TAG_OVERLOAD)
13816ca35587Sdholland 			tctx->overload = *j;
13826ca35587Sdholland 
13836ca35587Sdholland 		j += len;
13846ca35587Sdholland 	}
13856ca35587Sdholland }
13866ca35587Sdholland 
13876ca35587Sdholland static unsigned char *
bootpc_tag(struct bootpc_tagcontext * tctx,struct bootp_packet * bp,int len,int tag)13886ca35587Sdholland bootpc_tag(struct bootpc_tagcontext *tctx,
13896ca35587Sdholland     struct bootp_packet *bp, int len, int tag)
13906ca35587Sdholland {
13916ca35587Sdholland 	tctx->overload = 0;
13926ca35587Sdholland 	tctx->badopt = 0;
13936ca35587Sdholland 	tctx->badtag = 0;
13946ca35587Sdholland 	tctx->foundopt = 0;
13956ca35587Sdholland 	tctx->taglen = 0;
13966ca35587Sdholland 
13976ca35587Sdholland 	if (bootpc_hascookie(bp) == 0)
13986ca35587Sdholland 		return NULL;
13996ca35587Sdholland 
14006ca35587Sdholland 	bootpc_tag_helper(tctx, &bp->vend[4],
14016ca35587Sdholland 			  (unsigned char *) bp + len - &bp->vend[4], tag);
14026ca35587Sdholland 
14036ca35587Sdholland 	if ((tctx->overload & OVERLOAD_FILE) != 0)
14046ca35587Sdholland 		bootpc_tag_helper(tctx,
14056ca35587Sdholland 				  (unsigned char *) bp->file,
14066ca35587Sdholland 				  sizeof(bp->file),
14076ca35587Sdholland 				  tag);
14086ca35587Sdholland 	if ((tctx->overload & OVERLOAD_SNAME) != 0)
14096ca35587Sdholland 		bootpc_tag_helper(tctx,
14106ca35587Sdholland 				  (unsigned char *) bp->sname,
14116ca35587Sdholland 				  sizeof(bp->sname),
14126ca35587Sdholland 				  tag);
14136ca35587Sdholland 
14146ca35587Sdholland 	if (tctx->badopt != 0 || tctx->badtag != 0 || tctx->foundopt == 0)
14156ca35587Sdholland 		return NULL;
14166ca35587Sdholland 	tctx->buf[tctx->taglen] = '\0';
14176ca35587Sdholland 	return tctx->buf;
14186ca35587Sdholland }
14196ca35587Sdholland 
14206ca35587Sdholland static void
bootpc_decode_reply(struct nfsv3_diskless * nd,struct bootpc_ifcontext * ifctx,struct bootpc_globalcontext * gctx)14216ca35587Sdholland bootpc_decode_reply(struct nfsv3_diskless *nd, struct bootpc_ifcontext *ifctx,
14226ca35587Sdholland     struct bootpc_globalcontext *gctx)
14236ca35587Sdholland {
14246ca35587Sdholland 	char *p, *s;
14256ca35587Sdholland 
14266ca35587Sdholland 	ifctx->gotgw = 0;
14276ca35587Sdholland 	ifctx->gotnetmask = 0;
14286ca35587Sdholland 
14296ca35587Sdholland 	clear_sinaddr(&ifctx->myaddr);
14306ca35587Sdholland 	clear_sinaddr(&ifctx->netmask);
14316ca35587Sdholland 	clear_sinaddr(&ifctx->gw);
14326ca35587Sdholland 
14336ca35587Sdholland 	ifctx->myaddr.sin_addr = ifctx->reply.yiaddr;
14346ca35587Sdholland 
14356ca35587Sdholland 	printf("%s at ", ifctx->ireq.ifr_name);
14366ca35587Sdholland 	print_sin_addr(&ifctx->myaddr);
14376ca35587Sdholland 	printf(" server ");
14386ca35587Sdholland 	print_in_addr(ifctx->reply.siaddr);
14396ca35587Sdholland 
14406ca35587Sdholland 	ifctx->gw.sin_addr = ifctx->reply.giaddr;
14416ca35587Sdholland 	if (ifctx->reply.giaddr.s_addr != htonl(INADDR_ANY)) {
14426ca35587Sdholland 		printf(" via gateway ");
14436ca35587Sdholland 		print_in_addr(ifctx->reply.giaddr);
14446ca35587Sdholland 	}
14456ca35587Sdholland 
14466ca35587Sdholland 	/* This call used for the side effect (overload flag) */
14476ca35587Sdholland 	(void) bootpc_tag(&gctx->tmptag,
14486ca35587Sdholland 			  &ifctx->reply, ifctx->replylen, TAG_END);
14496ca35587Sdholland 
14506ca35587Sdholland 	if ((gctx->tmptag.overload & OVERLOAD_SNAME) == 0)
14516ca35587Sdholland 		if (ifctx->reply.sname[0] != '\0')
14526ca35587Sdholland 			printf(" server name %s", ifctx->reply.sname);
14536ca35587Sdholland 	if ((gctx->tmptag.overload & OVERLOAD_FILE) == 0)
14546ca35587Sdholland 		if (ifctx->reply.file[0] != '\0')
14556ca35587Sdholland 			printf(" boot file %s", ifctx->reply.file);
14566ca35587Sdholland 
14576ca35587Sdholland 	printf("\n");
14586ca35587Sdholland 
14596ca35587Sdholland 	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
14606ca35587Sdholland 		       TAG_SUBNETMASK);
14616ca35587Sdholland 	if (p != NULL) {
14626ca35587Sdholland 		if (gctx->tag.taglen != 4)
14636ca35587Sdholland 			panic("bootpc: subnet mask len is %d",
14646ca35587Sdholland 			      gctx->tag.taglen);
14656ca35587Sdholland 		bcopy(p, &ifctx->netmask.sin_addr, 4);
14666ca35587Sdholland 		ifctx->gotnetmask = 1;
14676ca35587Sdholland 		printf("subnet mask ");
14686ca35587Sdholland 		print_sin_addr(&ifctx->netmask);
14696ca35587Sdholland 		printf(" ");
14706ca35587Sdholland 	}
14716ca35587Sdholland 
14726ca35587Sdholland 	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
14736ca35587Sdholland 		       TAG_ROUTERS);
14746ca35587Sdholland 	if (p != NULL) {
14756ca35587Sdholland 		/* Routers */
14766ca35587Sdholland 		if (gctx->tag.taglen % 4)
14776ca35587Sdholland 			panic("bootpc: Router Len is %d", gctx->tag.taglen);
14786ca35587Sdholland 		if (gctx->tag.taglen > 0) {
14796ca35587Sdholland 			bcopy(p, &ifctx->gw.sin_addr, 4);
14806ca35587Sdholland 			printf("router ");
14816ca35587Sdholland 			print_sin_addr(&ifctx->gw);
14826ca35587Sdholland 			printf(" ");
14836ca35587Sdholland 			ifctx->gotgw = 1;
14846ca35587Sdholland 			gctx->gotgw = 1;
14856ca35587Sdholland 		}
14866ca35587Sdholland 	}
14876ca35587Sdholland 
14886ca35587Sdholland 	/*
14896ca35587Sdholland 	 * Choose a root filesystem.  If a value is forced in the environment
14906ca35587Sdholland 	 * and it contains "nfs:", use it unconditionally.  Otherwise, if the
14916ca35587Sdholland 	 * kernel is compiled with the ROOTDEVNAME option, then use it if:
14926ca35587Sdholland 	 *  - The server doesn't provide a pathname.
14936ca35587Sdholland 	 *  - The boothowto flags include RB_DFLTROOT (user said to override
14946ca35587Sdholland 	 *    the server value).
14956ca35587Sdholland 	 */
14966ca35587Sdholland 	p = NULL;
1497e81f0ea2Spgoyette 	if ((s = kern_getenv("vfs.root.mountfrom")) != NULL) {
14986ca35587Sdholland 		if ((p = strstr(s, "nfs:")) != NULL)
14996ca35587Sdholland 			p = strdup(p + 4, M_TEMP);
15006ca35587Sdholland 		freeenv(s);
15016ca35587Sdholland 	}
15026ca35587Sdholland 	if (p == NULL) {
15036ca35587Sdholland 		p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
15046ca35587Sdholland 		       TAG_ROOT);
1505e81f0ea2Spgoyette 		if (p != NULL)
1506e81f0ea2Spgoyette 			ifctx->gotrootpath = 1;
15076ca35587Sdholland 	}
15086ca35587Sdholland #ifdef ROOTDEVNAME
15096ca35587Sdholland 	if ((p == NULL || (boothowto & RB_DFLTROOT) != 0) &&
15106ca35587Sdholland 	    (p = strstr(ROOTDEVNAME, "nfs:")) != NULL) {
15116ca35587Sdholland 		p += 4;
15126ca35587Sdholland 	}
15136ca35587Sdholland #endif
15146ca35587Sdholland 	if (p != NULL) {
15156ca35587Sdholland 		if (gctx->setrootfs != NULL) {
15166ca35587Sdholland 			printf("rootfs %s (ignored) ", p);
1517*0342d508Spgoyette 		} else 	if (setmyfs(&nd->root_saddr,
15186ca35587Sdholland 				  nd->root_hostnam, p, &ifctx->reply.siaddr)) {
15196ca35587Sdholland 			if (*p == '/') {
15206ca35587Sdholland 				printf("root_server ");
15216ca35587Sdholland 				print_sin_addr(&nd->root_saddr);
15226ca35587Sdholland 				printf(" ");
15236ca35587Sdholland 			}
15246ca35587Sdholland 			printf("rootfs %s ", p);
15256ca35587Sdholland 			gctx->gotrootpath = 1;
15266ca35587Sdholland 			gctx->setrootfs = ifctx;
15276ca35587Sdholland 
15286ca35587Sdholland 			p = bootpc_tag(&gctx->tag, &ifctx->reply,
15296ca35587Sdholland 				       ifctx->replylen,
15306ca35587Sdholland 				       TAG_ROOTOPTS);
15316ca35587Sdholland 			if (p != NULL) {
15326ca35587Sdholland 				mountopts(&nd->root_args, p);
15336ca35587Sdholland 				printf("rootopts %s ", p);
15346ca35587Sdholland 			}
15356ca35587Sdholland 		} else
15366ca35587Sdholland 			panic("Failed to set rootfs to %s", p);
15376ca35587Sdholland 	}
15386ca35587Sdholland 
15396ca35587Sdholland 	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
15406ca35587Sdholland 		       TAG_HOSTNAME);
15416ca35587Sdholland 	if (p != NULL) {
15426ca35587Sdholland 		if (gctx->tag.taglen >= MAXHOSTNAMELEN)
15436ca35587Sdholland 			panic("bootpc: hostname >= %d bytes",
15446ca35587Sdholland 			      MAXHOSTNAMELEN);
15456ca35587Sdholland 		if (gctx->sethostname != NULL) {
15466ca35587Sdholland 			printf("hostname %s (ignored) ", p);
15476ca35587Sdholland 		} else {
15486ca35587Sdholland 			strcpy(nd->my_hostnam, p);
1549*0342d508Spgoyette 			mutex_enter(&prison0.pr_mtx);
15506ca35587Sdholland 			strcpy(prison0.pr_hostname, p);
1551*0342d508Spgoyette 			mutex_exit(&prison0.pr_mtx);
15526ca35587Sdholland 			printf("hostname %s ", p);
15536ca35587Sdholland 			gctx->sethostname = ifctx;
15546ca35587Sdholland 		}
15556ca35587Sdholland 	}
15566ca35587Sdholland 	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
15576ca35587Sdholland 			TAG_COOKIE);
15586ca35587Sdholland 	if (p != NULL) {        /* store in a sysctl variable */
15596ca35587Sdholland 		int i, l = sizeof(bootp_cookie) - 1;
15606ca35587Sdholland 		for (i = 0; i < l && p[i] != '\0'; i++)
15616ca35587Sdholland 			bootp_cookie[i] = p[i];
15626ca35587Sdholland 		p[i] = '\0';
15636ca35587Sdholland 	}
15646ca35587Sdholland 
1565e81f0ea2Spgoyette 	p = bootpc_tag(&gctx->tag, &ifctx->reply, ifctx->replylen,
1566e81f0ea2Spgoyette 		       TAG_INTF_MTU);
1567e81f0ea2Spgoyette 	if (p != NULL) {
1568e81f0ea2Spgoyette 		ifctx->mtu = be16dec(p);
1569e81f0ea2Spgoyette 	}
15706ca35587Sdholland 
15716ca35587Sdholland 	printf("\n");
15726ca35587Sdholland 
15736ca35587Sdholland 	if (ifctx->gotnetmask == 0) {
15746ca35587Sdholland 		if (IN_CLASSA(ntohl(ifctx->myaddr.sin_addr.s_addr)))
15756ca35587Sdholland 			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSA_NET);
15766ca35587Sdholland 		else if (IN_CLASSB(ntohl(ifctx->myaddr.sin_addr.s_addr)))
15776ca35587Sdholland 			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSB_NET);
15786ca35587Sdholland 		else
15796ca35587Sdholland 			ifctx->netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET);
15806ca35587Sdholland 	}
15816ca35587Sdholland }
15826ca35587Sdholland 
15836ca35587Sdholland void
bootpc_init(void)15846ca35587Sdholland bootpc_init(void)
15856ca35587Sdholland {
15866ca35587Sdholland 	struct bootpc_ifcontext *ifctx;		/* Interface BOOTP contexts */
15876ca35587Sdholland 	struct bootpc_globalcontext *gctx; 	/* Global BOOTP context */
15886ca35587Sdholland 	struct ifnet *ifp;
15896ca35587Sdholland 	struct sockaddr_dl *sdl;
15906ca35587Sdholland 	struct ifaddr *ifa;
15916ca35587Sdholland 	int error;
15926ca35587Sdholland #ifndef BOOTP_WIRED_TO
15936ca35587Sdholland 	int ifcnt;
15946ca35587Sdholland #endif
15956ca35587Sdholland 	struct nfsv3_diskless *nd;
1596*0342d508Spgoyette 	struct lwp *td;
15976ca35587Sdholland 	int timeout;
15986ca35587Sdholland 	int delay;
15996ca35587Sdholland 
16006ca35587Sdholland 	timeout = BOOTP_IFACE_WAIT_TIMEOUT * hz;
16016ca35587Sdholland 	delay = hz / 10;
16026ca35587Sdholland 
16036ca35587Sdholland 	nd = &nfsv3_diskless;
1604*0342d508Spgoyette 	td = curlwp;
16056ca35587Sdholland 
16066ca35587Sdholland 	/*
16076ca35587Sdholland 	 * If already filled in, don't touch it here
16086ca35587Sdholland 	 */
16096ca35587Sdholland 	if (nfs_diskless_valid != 0)
16106ca35587Sdholland 		return;
16116ca35587Sdholland 
16126ca35587Sdholland 	gctx = malloc(sizeof(*gctx), M_TEMP, M_WAITOK | M_ZERO);
16136ca35587Sdholland 	STAILQ_INIT(&gctx->interfaces);
16146ca35587Sdholland 	gctx->xid = ~0xFFFF;
16156ca35587Sdholland 	gctx->starttime = time_second;
16166ca35587Sdholland 
16176ca35587Sdholland 	/*
16186ca35587Sdholland 	 * If ROOTDEVNAME is defined or vfs.root.mountfrom is set then we have
16196ca35587Sdholland 	 * root-path overrides that can potentially let us boot even if we don't
16206ca35587Sdholland 	 * get a root path from the server, so we can treat that as a non-error.
16216ca35587Sdholland 	 */
16226ca35587Sdholland #ifdef ROOTDEVNAME
16236ca35587Sdholland 	gctx->any_root_overrides = 1;
16246ca35587Sdholland #else
16256ca35587Sdholland 	gctx->any_root_overrides = testenv("vfs.root.mountfrom");
16266ca35587Sdholland #endif
16276ca35587Sdholland 
16286ca35587Sdholland 	/*
16296ca35587Sdholland 	 * Find a network interface.
16306ca35587Sdholland 	 */
16316ca35587Sdholland 	CURVNET_SET(TD_TO_VNET(td));
16326ca35587Sdholland #ifdef BOOTP_WIRED_TO
16336ca35587Sdholland 	printf("%s: wired to interface '%s'\n", __func__,
16346ca35587Sdholland 	       __XSTRING(BOOTP_WIRED_TO));
16356ca35587Sdholland 	allocifctx(gctx);
16366ca35587Sdholland #else
16376ca35587Sdholland 	/*
16386ca35587Sdholland 	 * Preallocate interface context storage, if another interface
16396ca35587Sdholland 	 * attaches and wins the race, it won't be eligible for bootp.
16406ca35587Sdholland 	 */
16416ca35587Sdholland 	ifcnt = 0;
16426ca35587Sdholland 	IFNET_RLOCK();
1643*0342d508Spgoyette 	TAILQ_FOREACH(ifp, &V_ifnet, if_list) {
16446ca35587Sdholland 		if ((ifp->if_flags &
16456ca35587Sdholland 		     (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_BROADCAST)) !=
16466ca35587Sdholland 		    IFF_BROADCAST)
16476ca35587Sdholland 			continue;
1648*0342d508Spgoyette 		switch (ifp->if_type) {
16496ca35587Sdholland 			case IFT_ETHER:
16506ca35587Sdholland 			case IFT_FDDI:
16516ca35587Sdholland 			case IFT_ISO88025:
16526ca35587Sdholland 				break;
16536ca35587Sdholland 			default:
16546ca35587Sdholland 				continue;
16556ca35587Sdholland 		}
16566ca35587Sdholland 		ifcnt++;
16576ca35587Sdholland 	}
16586ca35587Sdholland 	IFNET_RUNLOCK();
16596ca35587Sdholland 	if (ifcnt == 0)
16606ca35587Sdholland 		panic("%s: no eligible interfaces", __func__);
16616ca35587Sdholland 	for (; ifcnt > 0; ifcnt--)
16626ca35587Sdholland 		allocifctx(gctx);
16636ca35587Sdholland #endif
16646ca35587Sdholland 
16656ca35587Sdholland retry:
16666ca35587Sdholland 	ifctx = STAILQ_FIRST(&gctx->interfaces);
16676ca35587Sdholland 	IFNET_RLOCK();
1668*0342d508Spgoyette 	TAILQ_FOREACH(ifp, &V_ifnet, if_list) {
16696ca35587Sdholland 		if (ifctx == NULL)
16706ca35587Sdholland 			break;
16716ca35587Sdholland #ifdef BOOTP_WIRED_TO
16726ca35587Sdholland 		if (strcmp(ifp->if_xname, __XSTRING(BOOTP_WIRED_TO)) != 0)
16736ca35587Sdholland 			continue;
16746ca35587Sdholland #else
16756ca35587Sdholland 		if ((ifp->if_flags &
16766ca35587Sdholland 		     (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_BROADCAST)) !=
16776ca35587Sdholland 		    IFF_BROADCAST)
16786ca35587Sdholland 			continue;
1679*0342d508Spgoyette 		switch (ifp->if_type) {
16806ca35587Sdholland 			case IFT_ETHER:
16816ca35587Sdholland 			case IFT_FDDI:
16826ca35587Sdholland 			case IFT_ISO88025:
16836ca35587Sdholland 				break;
16846ca35587Sdholland 			default:
16856ca35587Sdholland 				continue;
16866ca35587Sdholland 		}
16876ca35587Sdholland #endif
16886ca35587Sdholland 		strlcpy(ifctx->ireq.ifr_name, ifp->if_xname,
16896ca35587Sdholland 		    sizeof(ifctx->ireq.ifr_name));
16906ca35587Sdholland 		ifctx->ifp = ifp;
16916ca35587Sdholland 
16926ca35587Sdholland 		/* Get HW address */
16936ca35587Sdholland 		sdl = NULL;
1694*0342d508Spgoyette 		TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
16956ca35587Sdholland 			if (ifa->ifa_addr->sa_family == AF_LINK) {
16966ca35587Sdholland 				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
16976ca35587Sdholland 				if (sdl->sdl_type == IFT_ETHER)
16986ca35587Sdholland 					break;
16996ca35587Sdholland 			}
17006ca35587Sdholland 		if (sdl == NULL)
17016ca35587Sdholland 			panic("bootpc: Unable to find HW address for %s",
17026ca35587Sdholland 			    ifctx->ireq.ifr_name);
17036ca35587Sdholland 		ifctx->sdl = sdl;
17046ca35587Sdholland 
17056ca35587Sdholland 		ifctx = STAILQ_NEXT(ifctx, next);
17066ca35587Sdholland 	}
17076ca35587Sdholland 	IFNET_RUNLOCK();
17086ca35587Sdholland 	CURVNET_RESTORE();
17096ca35587Sdholland 
17106ca35587Sdholland 	if (STAILQ_EMPTY(&gctx->interfaces) ||
17116ca35587Sdholland 	    STAILQ_FIRST(&gctx->interfaces)->ifp == NULL) {
17126ca35587Sdholland 		if (timeout > 0) {
17136ca35587Sdholland 			pause("bootpc", delay);
17146ca35587Sdholland 			timeout -= delay;
17156ca35587Sdholland 			goto retry;
17166ca35587Sdholland 		}
17176ca35587Sdholland #ifdef BOOTP_WIRED_TO
17186ca35587Sdholland 		panic("%s: Could not find interface specified "
17196ca35587Sdholland 		      "by BOOTP_WIRED_TO: "
17206ca35587Sdholland 		      __XSTRING(BOOTP_WIRED_TO), __func__);
17216ca35587Sdholland #else
17226ca35587Sdholland 		panic("%s: no suitable interface", __func__);
17236ca35587Sdholland #endif
17246ca35587Sdholland 	}
17256ca35587Sdholland 
1726*0342d508Spgoyette 	error = socreate(AF_INET, &bootp_so, SOCK_DGRAM, 0, td, NULL);
17276ca35587Sdholland 	if (error != 0)
17286ca35587Sdholland 		panic("%s: socreate, error=%d", __func__, error);
17296ca35587Sdholland 
17306ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17316ca35587Sdholland 		bootpc_fakeup_interface(ifctx, td);
17326ca35587Sdholland 
17336ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17346ca35587Sdholland 		bootpc_compose_query(ifctx, td);
17356ca35587Sdholland 
17366ca35587Sdholland 	error = bootpc_call(gctx, td);
17376ca35587Sdholland 	if (error != 0) {
17386ca35587Sdholland 		printf("BOOTP call failed\n");
17396ca35587Sdholland 	}
17406ca35587Sdholland 
17416ca35587Sdholland 	mountopts(&nd->root_args, NULL);
17426ca35587Sdholland 
17436ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17446ca35587Sdholland 		if (bootpc_ifctx_isresolved(ifctx) != 0)
17456ca35587Sdholland 			bootpc_decode_reply(nd, ifctx, gctx);
17466ca35587Sdholland 
17476ca35587Sdholland #ifdef BOOTP_NFSROOT
17486ca35587Sdholland 	if (gctx->gotrootpath == 0 && gctx->any_root_overrides == 0)
17496ca35587Sdholland 		panic("bootpc: No root path offered");
17506ca35587Sdholland #endif
17516ca35587Sdholland 
17526ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17536ca35587Sdholland 		bootpc_adjust_interface(ifctx, gctx, td);
17546ca35587Sdholland 
17556ca35587Sdholland 	soclose(bootp_so);
17566ca35587Sdholland 
17576ca35587Sdholland 	STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17586ca35587Sdholland 		if (ifctx->gotrootpath != 0)
17596ca35587Sdholland 			break;
17606ca35587Sdholland 	if (ifctx == NULL) {
17616ca35587Sdholland 		STAILQ_FOREACH(ifctx, &gctx->interfaces, next)
17626ca35587Sdholland 			if (bootpc_ifctx_isresolved(ifctx) != 0)
17636ca35587Sdholland 				break;
17646ca35587Sdholland 	}
17656ca35587Sdholland 	if (ifctx == NULL)
17666ca35587Sdholland 		goto out;
17676ca35587Sdholland 
17686ca35587Sdholland 	if (gctx->gotrootpath != 0) {
17696ca35587Sdholland 
1770e81f0ea2Spgoyette 		kern_setenv("boot.netif.name", ifctx->ifp->if_xname);
17716ca35587Sdholland 
1772e81f0ea2Spgoyette 		bootpc_add_default_route(ifctx);
17736ca35587Sdholland 		error = md_mount(&nd->root_saddr, nd->root_hostnam,
17746ca35587Sdholland 				 nd->root_fh, &nd->root_fhsize,
17756ca35587Sdholland 				 &nd->root_args, td);
1776e81f0ea2Spgoyette 		bootpc_remove_default_route(ifctx);
17776ca35587Sdholland 		if (error != 0) {
17786ca35587Sdholland 			if (gctx->any_root_overrides == 0)
17796ca35587Sdholland 				panic("nfs_boot: mount root, error=%d", error);
17806ca35587Sdholland 			else
17816ca35587Sdholland 				goto out;
17826ca35587Sdholland 		}
17836ca35587Sdholland 		rootdevnames[0] = "nfs:";
17846ca35587Sdholland 		nfs_diskless_valid = 3;
17856ca35587Sdholland 	}
17866ca35587Sdholland 
17876ca35587Sdholland 	strcpy(nd->myif.ifra_name, ifctx->ireq.ifr_name);
17886ca35587Sdholland 	bcopy(&ifctx->myaddr, &nd->myif.ifra_addr, sizeof(ifctx->myaddr));
17896ca35587Sdholland 	bcopy(&ifctx->myaddr, &nd->myif.ifra_broadaddr, sizeof(ifctx->myaddr));
17906ca35587Sdholland 	((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr =
17916ca35587Sdholland 		ifctx->myaddr.sin_addr.s_addr |
17926ca35587Sdholland 		~ ifctx->netmask.sin_addr.s_addr;
17936ca35587Sdholland 	bcopy(&ifctx->netmask, &nd->myif.ifra_mask, sizeof(ifctx->netmask));
1794e81f0ea2Spgoyette 	bcopy(&ifctx->gw, &nd->mygateway, sizeof(ifctx->gw));
17956ca35587Sdholland 
17966ca35587Sdholland out:
17976ca35587Sdholland 	while((ifctx = STAILQ_FIRST(&gctx->interfaces)) != NULL) {
17986ca35587Sdholland 		STAILQ_REMOVE_HEAD(&gctx->interfaces, next);
17996ca35587Sdholland 		free(ifctx, M_TEMP);
18006ca35587Sdholland 	}
18016ca35587Sdholland 	free(gctx, M_TEMP);
18026ca35587Sdholland }
18036ca35587Sdholland 
18046ca35587Sdholland /*
18056ca35587Sdholland  * RPC: mountd/mount
18066ca35587Sdholland  * Given a server pathname, get an NFS file handle.
18076ca35587Sdholland  * Also, sets sin->sin_port to the NFS service port.
18086ca35587Sdholland  */
18096ca35587Sdholland static int
md_mount(struct sockaddr_in * mdsin,char * path,u_char * fhp,int * fhsizep,struct nfs_args * args,struct lwp * td)18106ca35587Sdholland md_mount(struct sockaddr_in *mdsin, char *path, u_char *fhp, int *fhsizep,
1811*0342d508Spgoyette     struct nfs_args *args, struct lwp *td)
18126ca35587Sdholland {
18136ca35587Sdholland 	struct mbuf *m;
18146ca35587Sdholland 	int error;
18156ca35587Sdholland 	int authunixok;
18166ca35587Sdholland 	int authcount;
18176ca35587Sdholland 	int authver;
18186ca35587Sdholland 
18196ca35587Sdholland #define	RPCPROG_MNT	100005
18206ca35587Sdholland #define	RPCMNT_VER1	1
18216ca35587Sdholland #define RPCMNT_VER3	3
18226ca35587Sdholland #define	RPCMNT_MOUNT	1
18236ca35587Sdholland #define	AUTH_SYS	1		/* unix style (uid, gids) */
18246ca35587Sdholland #define AUTH_UNIX	AUTH_SYS
18256ca35587Sdholland 
18266ca35587Sdholland 	/* XXX honor v2/v3 flags in args->flags? */
18276ca35587Sdholland #ifdef BOOTP_NFSV3
18286ca35587Sdholland 	/* First try NFS v3 */
18296ca35587Sdholland 	/* Get port number for MOUNTD. */
18306ca35587Sdholland 	error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3,
18316ca35587Sdholland 			     &mdsin->sin_port, td);
18326ca35587Sdholland 	if (error == 0) {
18336ca35587Sdholland 		m = xdr_string_encode(path, strlen(path));
18346ca35587Sdholland 
18356ca35587Sdholland 		/* Do RPC to mountd. */
18366ca35587Sdholland 		error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3,
18376ca35587Sdholland 				  RPCMNT_MOUNT, &m, NULL, td);
18386ca35587Sdholland 	}
18396ca35587Sdholland 	if (error == 0) {
18406ca35587Sdholland 		args->flags |= NFSMNT_NFSV3;
18416ca35587Sdholland 	} else {
18426ca35587Sdholland #endif
18436ca35587Sdholland 		/* Fallback to NFS v2 */
18446ca35587Sdholland 
18456ca35587Sdholland 		/* Get port number for MOUNTD. */
18466ca35587Sdholland 		error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
18476ca35587Sdholland 				     &mdsin->sin_port, td);
18486ca35587Sdholland 		if (error != 0)
18496ca35587Sdholland 			return error;
18506ca35587Sdholland 
18516ca35587Sdholland 		m = xdr_string_encode(path, strlen(path));
18526ca35587Sdholland 
18536ca35587Sdholland 		/* Do RPC to mountd. */
18546ca35587Sdholland 		error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
18556ca35587Sdholland 				  RPCMNT_MOUNT, &m, NULL, td);
18566ca35587Sdholland 		if (error != 0)
18576ca35587Sdholland 			return error;	/* message already freed */
18586ca35587Sdholland 
18596ca35587Sdholland #ifdef BOOTP_NFSV3
18606ca35587Sdholland 	}
18616ca35587Sdholland #endif
18626ca35587Sdholland 
18636ca35587Sdholland 	if (xdr_int_decode(&m, &error) != 0 || error != 0)
18646ca35587Sdholland 		goto bad;
18656ca35587Sdholland 
18666ca35587Sdholland 	if ((args->flags & NFSMNT_NFSV3) != 0) {
18676ca35587Sdholland 		if (xdr_int_decode(&m, fhsizep) != 0 ||
18686ca35587Sdholland 		    *fhsizep > NFSX_V3FHMAX ||
18696ca35587Sdholland 		    *fhsizep <= 0)
18706ca35587Sdholland 			goto bad;
18716ca35587Sdholland 	} else
18726ca35587Sdholland 		*fhsizep = NFSX_V2FH;
18736ca35587Sdholland 
18746ca35587Sdholland 	if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0)
18756ca35587Sdholland 		goto bad;
18766ca35587Sdholland 
18776ca35587Sdholland 	if (args->flags & NFSMNT_NFSV3) {
18786ca35587Sdholland 		if (xdr_int_decode(&m, &authcount) != 0)
18796ca35587Sdholland 			goto bad;
18806ca35587Sdholland 		authunixok = 0;
18816ca35587Sdholland 		if (authcount < 0 || authcount > 100)
18826ca35587Sdholland 			goto bad;
18836ca35587Sdholland 		while (authcount > 0) {
18846ca35587Sdholland 			if (xdr_int_decode(&m, &authver) != 0)
18856ca35587Sdholland 				goto bad;
18866ca35587Sdholland 			if (authver == AUTH_UNIX)
18876ca35587Sdholland 				authunixok = 1;
18886ca35587Sdholland 			authcount--;
18896ca35587Sdholland 		}
18906ca35587Sdholland 		if (authunixok == 0)
18916ca35587Sdholland 			goto bad;
18926ca35587Sdholland 	}
18936ca35587Sdholland 
18946ca35587Sdholland 	/* Set port number for NFS use. */
18956ca35587Sdholland 	error = krpc_portmap(mdsin, NFS_PROG,
18966ca35587Sdholland 			     (args->flags &
18976ca35587Sdholland 			      NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
18986ca35587Sdholland 			     &mdsin->sin_port, td);
18996ca35587Sdholland 
19006ca35587Sdholland 	goto out;
19016ca35587Sdholland 
19026ca35587Sdholland bad:
19036ca35587Sdholland 	error = EBADRPC;
19046ca35587Sdholland 
19056ca35587Sdholland out:
19066ca35587Sdholland 	m_freem(m);
19076ca35587Sdholland 	return error;
19086ca35587Sdholland }
19096ca35587Sdholland 
1910*0342d508Spgoyette #if 0	/* Need to call bootpc_init from module initialization routine */
19116ca35587Sdholland SYSINIT(bootp_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, bootpc_init, NULL);
1912*0342d508Spgoyette #endif
1913