xref: /netbsd-src/sys/nfs/nfs_boot.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
1 /*
2  * Copyright (c) 1994 Adam Glass, Gordon Ross
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Adam Glass.
16  * 4. The name of the Author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL Adam Glass BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *	$Id: nfs_boot.c,v 1.2 1994/05/05 05:39:42 cgd Exp $
32  */
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/conf.h>
38 #include <sys/ioctl.h>
39 #include <sys/proc.h>
40 #include <sys/mount.h>
41 #include <sys/mbuf.h>
42 #include <sys/socket.h>
43 #include <sys/reboot.h>
44 
45 #include <net/if.h>
46 #include <netinet/in.h>
47 
48 #include <nfs/rpcv2.h>
49 #include <nfs/nfsv2.h>
50 #include <nfs/nfs.h>
51 #include <nfs/nfsdiskless.h>
52 
53 /*
54  * Support for NFS diskless booting, specifically getting information
55  * about where to boot from, what pathnames, etc.
56  *
57  * We currently support the RPC bootparam protocol.
58  *
59  * We'd like to support BOOTP, but someone needs to write small kernel-ized
60  * BOOTP client
61  *
62  */
63 
64 /* from rfc951 to avoid bringing in cmu header file */
65 
66 #define UDP_BOOTPSERVER 67
67 #define UDP_BOOTPCLIENT 68
68 
69 #define BOOTP_REQUEST 1
70 #define BOOTP_REPLY   2
71 
72 /* rfc1048 tag bytes, (from rfc1497), only the semi-useful bits */
73 #define TAG_PAD                0	/* [1] no data */
74 #define TAG_SUBNET_MASK        1        /* [4] subnet mask bytes */
75 #define TAG_GATEWAY_ADDR       3        /* [addr] gateway address */
76 #define TAG_DNS_ADDR           6        /* [addr] dns name server */
77 #define TAG_HOSTNAME          12        /* [n] hostname */
78 #define TAG_BOOT_SIZE         13        /* [2] boot file size?*/
79 #define TAG_DOMAIN_NAME       15        /* [n] domain name */
80 #define TAG_SWAP_ADDR         16        /* [addr] swap server */
81 #define TAG_ROOT_PATH         17        /* [n] root path */
82 #define TAG_END              255
83 
84 #define BOOTP_ROOT 1
85 #define BOOTP_SWAP 2
86 #define BOOTP_COMPLETED (BOOTP_ROOT|BOOTP_SWAP)
87 
88 struct bootp_msg {
89 	u_char bpm_op;		/* packet op code / message type */
90 	u_char bpm_htype;	/* hardware address type */
91 	u_char bpm_hlen;	/* hardware address length */
92 	u_char bpm_hops;	/* bootp hops XXX ugly*/
93 	u_long bpm_xid;		/* transaction ID */
94 	u_short bpm_secs;	/* seconds elapsed since boot */
95 	u_short bpm_unused;
96 	struct in_addr bpm_ciaddr; /* client IP address */
97 	struct in_addr bpm_yiaddr; /* 'your' (client) IP address */
98 	struct in_addr bpm_siaddr; /* server IP address */
99 	struct in_addr bpm_giaddr; /* gateway IP address */
100 	u_char bpm_chaddr[16];	/* client hardware address */
101 	u_char bpm_sname[64];	/* optional server host name */
102 	u_char bpm_file[128];	/* boot file name */
103 	u_char bpm_vendor[64];	/* vendor-specific data */
104 };
105 
106 static u_char vend_rfc1048[4] = {
107 	99, 130, 83, 99,
108 };
109 
110 /*
111  * Get a file handle given a path name.
112  */
113 static int
114 nfs_boot_getfh(sa, path, fhp)
115 	struct sockaddr *sa;		/* server address */
116 	char *path;
117 	u_char *fhp;
118 {
119 	/* The RPC structures */
120 	struct sdata {
121 		u_long  len;
122 		u_char  path[4];	/* longer, of course */
123 	} *sdata;
124 	struct rdata {
125 		u_long	errno;
126 		u_char	fh[NFS_FHSIZE];
127 	} *rdata;
128 	struct sockaddr_in *sin;
129 	struct mbuf *m;
130 	int error, mlen, slen;
131 
132 	/*
133 	 * Validate address family.
134 	 * Sorry, this is INET specific...
135 	 */
136 	if (sa->sa_family != AF_INET)
137 		return EAFNOSUPPORT;
138 
139 	slen = strlen(path);
140 	if (slen > (MLEN-4))
141 		slen = (MLEN-4);
142 	mlen = 4 + ((slen + 3) & ~3); /* XXX ??? */
143 
144 	m = m_get(M_WAIT, MT_DATA);
145 	if (m == NULL)
146 		return ENOBUFS;
147 	m->m_len = mlen;
148 	sdata = mtod(m, struct sdata *);
149 	sdata->len = htonl(slen);
150 	bcopy(path, sdata->path, slen);
151 
152 	/* Do RPC to mountd. */
153 	error = krpc_call(sa, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
154 			  &m, sizeof(*rdata));
155 	if (error)
156 		return error;
157 
158 	rdata = mtod(m, struct rdata *);
159 	error = ntohl(rdata->errno);
160 	if (!error)
161 		bcopy(rdata->fh, fhp, NFS_FHSIZE);
162 	m_freem(m);
163 	return error;
164 }
165 
166 /*
167  * Receive a bootp reply
168  */
169 static int bootp_receive(so, msg)
170 	struct socket *so;
171 	struct bootp_msg *msg;
172 {
173 	int error, rcvflag = 0;
174 	struct mbuf *m;
175 	struct uio auio;
176 
177 	auio.uio_resid = (1<<16);
178 
179 	error = soreceive(so, NULL, &auio, &m, NULL, &rcvflag);
180 	if (error)
181 		return error;
182 	if (m->m_len < sizeof(*msg)) {
183 		m = m_pullup(m, sizeof(*msg));
184 		if (m == NULL)
185 			return ENOBUFS;
186 	}
187 	if (((1<<16) - auio.uio_resid) != sizeof(*msg))
188 		return EMSGSIZE;
189 	m_copydata(m, 0, sizeof(*msg), (caddr_t) msg);
190 	return 0;
191 }
192 
193 /*
194  * Send a bootp request
195  */
196 static int bootp_send(so, nam, start, msg)
197 	struct socket *so;
198 	struct mbuf *nam;
199 	time_t start;
200 	struct bootp_msg *msg;
201 {
202 	int error;
203 	struct mbuf *m;
204 	struct bootp_msg *bpm;
205 
206 	MGETHDR(m, M_WAIT, MT_DATA);
207 	m->m_len = MHLEN;
208 	if (m->m_len < sizeof(*msg)) {
209 		MCLGET(m, M_WAIT);
210 		m->m_len = min(sizeof(*msg), MCLBYTES);
211 	}
212 	m->m_pkthdr.len = sizeof(*msg);
213 	m->m_pkthdr.rcvif = NULL;
214 	bpm = mtod(m, struct bootp_msg *);
215 	bcopy((caddr_t) msg, (caddr_t) bpm, sizeof(*bpm));
216 	bpm->bpm_secs = time.tv_sec - start;
217 
218 	error = sosend(so, nam, NULL, m, NULL, 0);
219 	if (error)
220 		return error;
221 }
222 
223 /*
224  * Fill in as much of the nfs_diskless struct using the results
225  * of bootp requests.
226  */
227 int nfs_boot_bootp(diskless, rpath, spath)
228 	struct nfs_diskless *diskless;
229 	char *rpath, *spath;
230 {
231 	int error, bootp_timeout = 30, *opt_val, have, need;
232 	time_t start;
233 	struct socket *so;
234 	struct sockaddr *sa;
235 	struct sockaddr_in *sin;
236 	struct mbuf *m, *nam;
237 	struct timeval *tv;
238 	struct bootp_msg outgoing, incoming;
239 
240 	sa = &diskless->myif.ifra_broadaddr;
241 	if (sa->sa_family != AF_INET)
242 		return EAFNOSUPPORT;
243 
244 	/*
245 	 * Create socket and set its recieve timeout.
246 	 */
247 	if (error = socreate(AF_INET, &so, SOCK_DGRAM, 0))
248 		return error;
249 	nam = m_get(M_WAIT, MT_SONAME);
250 	sin = mtod(nam, struct sockaddr_in *);
251 	bzero((caddr_t) sin, sizeof(*sin));
252 	sin->sin_len = sizeof(struct sockaddr_in);
253 	sin->sin_family = AF_INET;
254 	sin->sin_addr.s_addr = htonl(INADDR_ANY);
255 	sin->sin_port = htons(UDP_BOOTPCLIENT);
256 	nam->m_len = sizeof(struct sockaddr_in);
257 	if (error = sobind(so, nam))
258 		goto out;
259 	m = m_get(M_WAIT, MT_SOOPTS);
260 	tv = mtod(m, struct timeval *);
261 	m->m_len = sizeof(*tv);
262 	tv->tv_sec = 5;
263 	tv->tv_usec = 0;
264 	if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m)))
265 		goto out;
266 	m = m_get(M_WAIT, MT_SOOPTS);
267 	opt_val = mtod(m, int *);
268 	m->m_len = sizeof(*opt_val);
269 	*opt_val = 1;
270 	if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m)))
271 		goto out;
272 
273 	/*
274 	 * Setup socket address for the server.
275 	 */
276 	nam = m_get(M_WAIT, MT_SONAME);
277 	sin = mtod(nam, struct sockaddr_in *);
278 	bcopy((caddr_t)sa, (caddr_t)sin, sizeof(struct sockaddr));
279 	sin->sin_port = htons(UDP_BOOTPSERVER);
280 	nam->m_len = sizeof(struct sockaddr);
281 
282 	bzero((caddr_t) &outgoing, sizeof(outgoing));
283 	outgoing.bpm_op = BOOTP_REQUEST;
284 	outgoing.bpm_htype = 1;
285 	outgoing.bpm_hlen = 6;
286 	outgoing.bpm_hops = 0;
287 	sin = (struct sockaddr_in *) &diskless->myif.ifra_addr;
288 	bcopy((caddr_t) &sin->sin_addr, (caddr_t) &outgoing.bpm_ciaddr, 4);
289 	bcopy((caddr_t) &sin->sin_addr, (caddr_t) &outgoing.bpm_yiaddr, 4);
290 	bcopy((caddr_t) vend_rfc1048, (caddr_t) outgoing.bpm_vendor, 4);
291 	outgoing.bpm_xid = time.tv_usec;
292 	outgoing.bpm_vendor[4] = TAG_END;
293 	start = time.tv_sec;
294 
295 	have = 0;
296 	while (bootp_timeout-- && (have != BOOTP_COMPLETED)) {
297 		if ((have & BOOTP_ROOT) == 0)
298 			strcpy(outgoing.bpm_file, "root");
299 		else if ((have & BOOTP_SWAP) == 0)
300 			strcpy(outgoing.bpm_file, "swap");
301 		if (error = bootp_send(so, nam, start, &outgoing)) {
302 			goto out;
303 		}
304 		error = bootp_receive(so, &incoming);
305 		if (error == EWOULDBLOCK)
306 			continue;
307 		if (error)
308 			goto out;
309 
310 		if (outgoing.bpm_xid != incoming.bpm_xid)
311 			continue;
312 		if ((have & BOOTP_ROOT) == 0) {
313 			sin = (struct sockaddr_in *) &diskless->root_saddr;
314 			sin->sin_family = AF_INET;
315 			sin->sin_len = sizeof(*sin);
316 			bcopy((caddr_t) &incoming.bpm_siaddr,
317 			      (caddr_t) &sin->sin_addr, sizeof(sin->sin_addr));
318 			strcpy(diskless->root_hostnam, incoming.bpm_sname);
319 			strcpy(rpath, incoming.bpm_file);
320 			have |= BOOTP_ROOT;
321 			outgoing.bpm_xid++;
322 		}
323 		else if ((have & BOOTP_SWAP) == 0) {
324 			sin = (struct sockaddr_in *) &diskless->swap_saddr;
325 			sin->sin_family = AF_INET;
326 			sin->sin_len = sizeof(*sin);
327 			bcopy((caddr_t) &incoming.bpm_siaddr,
328 			      (caddr_t) &sin->sin_addr, sizeof(sin->sin_addr));
329 			strcpy(diskless->swap_hostnam, incoming.bpm_sname);
330 			strcpy(spath, incoming.bpm_file);
331 			have |= BOOTP_SWAP;
332 		}
333 	}
334 
335 	if (have != BOOTP_COMPLETED)
336 		error = ETIMEDOUT;
337  out:
338 	if (nam)
339 		m_freem(nam);
340 	soclose(so);
341 
342 	return error;
343 }
344 
345 /*
346  * Called with a nfs_diskless struct in which a interface ip addr,
347  * broadcast addr, and netmask have been specified.,
348  *
349  * The responsibility of this routine is to fill out the rest of the
350  * struct using whatever mechanism.
351  *
352  */
353 int nfs_boot(diskless)
354 	struct nfs_diskless *diskless;
355 {
356 	int error;
357 	u_short port;
358 	struct sockaddr_in *sin;
359 	char root_path[MAXPATHLEN], swap_path[MAXPATHLEN];
360 
361 	error = nfs_boot_bootp(diskless, root_path, swap_path);
362 	if (error)
363 		return error;
364 
365 	sin = (struct sockaddr_in *) &diskless->root_saddr;
366 	error = nfs_boot_getfh(&diskless->root_saddr, root_path,
367 			       diskless->root_fh);
368 	if (error)
369 		return error;
370 	error = krpc_portmap(&diskless->root_saddr,
371 			     NFS_PROG, NFS_VER2, &sin->sin_port);
372 	if (error)
373 		return error;
374 
375 	sin = (struct sockaddr_in *) &diskless->swap_saddr;
376 	error = nfs_boot_getfh(&diskless->swap_saddr, swap_path,
377 			       diskless->swap_fh);
378 	if (error)
379 		return error;
380 	error = krpc_portmap(&diskless->swap_saddr,
381 			     NFS_PROG, NFS_VER2, &sin->sin_port);
382 	if (error)
383 		return error;
384 
385 	printf("root on %x:%s\n", diskless->root_hostnam, root_path);
386 	printf("swap on %x:%s\n", diskless->swap_hostnam, swap_path);
387 	diskless->root_args.addr = &diskless->root_saddr;
388 	diskless->swap_args.addr = &diskless->swap_saddr;
389 	diskless->root_args.sotype = diskless->swap_args.sotype = SOCK_DGRAM;
390 	diskless->root_args.proto = diskless->swap_args.proto = 0;
391 	diskless->root_args.flags = diskless->swap_args.flags = 0;
392 	diskless->root_args.wsize = diskless->swap_args.wsize = NFS_WSIZE;
393 	diskless->root_args.rsize = diskless->swap_args.rsize = NFS_RSIZE;
394 	diskless->root_args.timeo = diskless->swap_args.timeo = NFS_TIMEO;
395 	diskless->root_args.retrans = diskless->swap_args.retrans =
396 		NFS_RETRANS;
397 	diskless->root_args.hostname = diskless->root_hostnam;
398 	diskless->swap_args.hostname = diskless->swap_hostnam;
399 	return 0;
400 }
401