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