xref: /freebsd/sys/nfs/bootp_subr.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
1 /*	$Id: bootp_subr.c,v 1.4 1997/06/12 14:08:20 tegge Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Gordon Ross, Adam Glass
5  * Copyright (c) 1992 Regents of the University of California.
6  * All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Lawrence Berkeley Laboratory and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  * based on:
41  *      nfs/krpc_subr.c
42  *	$NetBSD: krpc_subr.c,v 1.10 1995/08/08 20:43:43 gwr Exp $
43  */
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/conf.h>
49 #include <sys/sockio.h>
50 #include <sys/proc.h>
51 #include <sys/mount.h>
52 #include <sys/mbuf.h>
53 #include <sys/reboot.h>
54 #include <sys/socket.h>
55 #include <sys/socketvar.h>
56 
57 #include <net/if.h>
58 #include <net/route.h>
59 
60 #include <netinet/in.h>
61 #include <net/if_types.h>
62 #include <net/if_dl.h>
63 #include <netinet/if_ether.h>
64 
65 #include <nfs/rpcv2.h>
66 #include <nfs/nfsproto.h>
67 #include <nfs/nfs.h>
68 #include <nfs/nfsdiskless.h>
69 #include <nfs/krpc.h>
70 #include <nfs/xdr_subs.h>
71 
72 
73 #define MIN_REPLY_HDR 16	/* xid, dir, astat, errno */
74 
75 /*
76  * What is the longest we will wait before re-sending a request?
77  * Note this is also the frequency of "RPC timeout" messages.
78  * The re-send loop count sup linearly to this maximum, so the
79  * first complaint will happen after (1+2+3+4+5)=15 seconds.
80  */
81 #define	MAX_RESEND_DELAY 5	/* seconds */
82 
83 /* Definitions from RFC951 */
84 struct bootp_packet {
85   u_int8_t op;
86   u_int8_t htype;
87   u_int8_t hlen;
88   u_int8_t hops;
89   u_int32_t xid;
90   u_int16_t secs;
91   u_int16_t flags;
92   struct in_addr ciaddr;
93   struct in_addr yiaddr;
94   struct in_addr siaddr;
95   struct in_addr giaddr;
96   unsigned char chaddr[16];
97   char sname[64];
98   char file[128];
99   unsigned char vend[256];
100 };
101 
102 #define IPPORT_BOOTPC 68
103 #define IPPORT_BOOTPS 67
104 
105 extern int nfs_diskless_valid;
106 extern struct nfsv3_diskless nfsv3_diskless;
107 
108 /* mountd RPC */
109 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
110 	u_char *fhp, int *fhsizep, struct nfs_args *args,struct proc *procp));
111 static int md_lookup_swap __P((struct sockaddr_in *mdsin,char *path,
112 			       u_char *fhp, int *fhsizep,
113 			       struct nfs_args *args,
114 			       struct proc *procp));
115 static int setfs __P((struct sockaddr_in *addr, char *path, char *p));
116 static int getdec __P((char **ptr));
117 static char *substr __P((char *a,char *b));
118 static void mountopts __P((struct nfs_args *args, char *p));
119 static int xdr_opaque_decode __P((struct mbuf **ptr,u_char *buf,
120 				  int len));
121 static int xdr_int_decode __P((struct mbuf **ptr,int *iptr));
122 static void printip __P((char *prefix,struct in_addr addr));
123 
124 #ifdef BOOTP_DEBUG
125 void bootpboot_p_sa(struct sockaddr *sa,struct sockaddr *ma);
126 void bootpboot_p_ma(struct sockaddr *ma);
127 void bootpboot_p_rtentry(struct rtentry *rt);
128 void bootpboot_p_tree(struct radix_node *rn);
129 void bootpboot_p_rtlist(void);
130 void bootpboot_p_iflist(void);
131 #endif
132 
133 int  bootpc_call(struct bootp_packet *call,
134 		 struct bootp_packet *reply,
135 		 struct proc *procp);
136 
137 int bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so,
138 			struct proc *procp);
139 
140 int
141 bootpc_adjust_interface(struct ifreq *ireq,struct socket *so,
142 			struct sockaddr_in *myaddr,
143 			struct sockaddr_in *netmask,
144 			struct sockaddr_in *gw,
145 			struct proc *procp);
146 
147 void bootpc_init(void);
148 
149 #ifdef BOOTP_DEBUG
150 void bootpboot_p_sa(sa,ma)
151      struct sockaddr *sa;
152      struct sockaddr *ma;
153 {
154   if (!sa) {
155     printf("(sockaddr *) <null>");
156     return;
157   }
158   switch (sa->sa_family) {
159   case AF_INET:
160     {
161       struct sockaddr_in *sin = (struct sockaddr_in *) sa;
162       printf("inet %x",ntohl(sin->sin_addr.s_addr));
163       if (ma) {
164 	struct sockaddr_in *sin = (struct sockaddr_in *) ma;
165 	printf(" mask %x",ntohl(sin->sin_addr.s_addr));
166       }
167     }
168   break;
169   case AF_LINK:
170     {
171       struct sockaddr_dl *sli = (struct sockaddr_dl *) sa;
172       int i;
173       printf("link %.*s ",sli->sdl_nlen,sli->sdl_data);
174       for (i=0;i<sli->sdl_alen;i++) {
175 	if (i>0)
176 	  printf(":");
177 	printf("%x",(unsigned char) sli->sdl_data[i+sli->sdl_nlen]);
178       }
179     }
180   break;
181   default:
182     printf("af%d",sa->sa_family);
183   }
184 }
185 
186 void bootpboot_p_ma(ma)
187      struct sockaddr *ma;
188 {
189   if (!ma) {
190     printf("<null>");
191     return;
192   }
193   printf("%x",*(int*)ma);
194 }
195 
196 void bootpboot_p_rtentry(rt)
197      struct rtentry *rt;
198 {
199   bootpboot_p_sa(rt_key(rt),rt_mask(rt));
200   printf(" ");
201   bootpboot_p_ma(rt->rt_genmask);
202   printf(" ");
203   bootpboot_p_sa(rt->rt_gateway,NULL);
204   printf(" ");
205   printf("flags %x",(unsigned short) rt->rt_flags);
206   printf(" %d",rt->rt_rmx.rmx_expire);
207   printf(" %s%d\n",rt->rt_ifp->if_name,rt->rt_ifp->if_unit);
208 }
209 void  bootpboot_p_tree(rn)
210      struct radix_node *rn;
211 {
212   while (rn) {
213     if (rn->rn_b < 0) {
214       if (rn->rn_flags & RNF_ROOT) {
215       } else {
216 	bootpboot_p_rtentry((struct rtentry *) rn);
217       }
218       rn = rn->rn_dupedkey;
219     } else {
220       bootpboot_p_tree(rn->rn_l);
221       bootpboot_p_tree(rn->rn_r);
222       return;
223     }
224 
225   }
226 }
227 
228 void bootpboot_p_rtlist(void)
229 {
230   printf("Routing table:\n");
231   bootpboot_p_tree(rt_tables[AF_INET]->rnh_treetop);
232 }
233 
234 void bootpboot_p_iflist(void)
235 {
236   struct ifnet *ifp;
237   struct ifaddr *ifa;
238   printf("Interface list:\n");
239   for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link))
240     {
241       for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa;
242 	   ifa=TAILQ_NEXT(ifa,ifa_link))
243 	if (ifa->ifa_addr->sa_family == AF_INET ) {
244 	  printf("%s%d flags %x, addr %x, bcast %x, net %x\n",
245 		 ifp->if_name,ifp->if_unit,
246 		 (unsigned short) ifp->if_flags,
247 		 ntohl(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr),
248 		 ntohl(((struct sockaddr_in *) ifa->ifa_dstaddr)->sin_addr.s_addr),
249 		 ntohl(((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr)
250 		 );
251 	}
252     }
253 }
254 #endif
255 
256 int
257 bootpc_call(call,reply,procp)
258      struct bootp_packet *call;
259      struct bootp_packet *reply;	/* output */
260      struct proc *procp;
261 {
262 	struct socket *so;
263 	struct sockaddr_in *sin, sa;
264 	struct mbuf *m;
265 	struct uio auio;
266 	struct iovec aio;
267 	int error, rcvflg, timo, secs, len;
268 	u_int tport;
269 
270 	/*
271 	 * Create socket and set its recieve timeout.
272 	 */
273 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)))
274 		goto out;
275 
276 	m = m_get(M_WAIT, MT_SOOPTS);
277 	if (m == NULL) {
278 		error = ENOBUFS;
279 		goto out;
280 	} else {
281 		struct timeval *tv;
282 		tv = mtod(m, struct timeval *);
283 		m->m_len = sizeof(*tv);
284 		tv->tv_sec = 1;
285 		tv->tv_usec = 0;
286 		if ((error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m, procp)))
287 			goto out;
288 	}
289 
290 	/*
291 	 * Enable broadcast.
292 	 */
293 	{
294 		int *on;
295 		m = m_get(M_WAIT, MT_SOOPTS);
296 		if (m == NULL) {
297 			error = ENOBUFS;
298 			goto out;
299 		}
300 		on = mtod(m, int *);
301 		m->m_len = sizeof(*on);
302 		*on = 1;
303 		if ((error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m, procp)))
304 			goto out;
305 	}
306 
307 	/*
308 	 * Bind the local endpoint to a bootp client port.
309 	 */
310 	sin = &sa;
311 	bzero(sin, sizeof *sin);
312 	sin->sin_len = sizeof(*sin);
313 	sin->sin_family = AF_INET;
314 	sin->sin_addr.s_addr = INADDR_ANY;
315 	sin->sin_port = htons(IPPORT_BOOTPC);
316 	error = sobind(so, (struct sockaddr *)sin, procp);
317 	if (error) {
318 		printf("bind failed\n");
319 		goto out;
320 	}
321 
322 	/*
323 	 * Setup socket address for the server.
324 	 */
325 	sin = &sa;
326 	bzero(sin, sizeof *sin);
327 	sin->sin_len = sizeof(*sin);
328 	sin->sin_family = AF_INET;
329 	sin->sin_addr.s_addr = INADDR_BROADCAST;
330 	sin->sin_port = htons(IPPORT_BOOTPS);
331 
332 	/*
333 	 * Send it, repeatedly, until a reply is received,
334 	 * but delay each re-send by an increasing amount.
335 	 * If the delay hits the maximum, start complaining.
336 	 */
337 	timo = 0;
338 	for (;;) {
339 		/* Send BOOTP request (or re-send). */
340 
341 		aio.iov_base = (caddr_t) call;
342 		aio.iov_len = sizeof(*call);
343 
344 		auio.uio_iov = &aio;
345 		auio.uio_iovcnt = 1;
346 		auio.uio_segflg = UIO_SYSSPACE;
347 		auio.uio_rw = UIO_WRITE;
348 		auio.uio_offset = 0;
349 		auio.uio_resid = sizeof(*call);
350 		auio.uio_procp = procp;
351 
352 		error = sosend(so, (struct sockaddr *)sin, &auio, NULL,
353 			       NULL, 0, procp);
354 		if (error) {
355 			printf("bootpc_call: sosend: %d\n", error);
356 			goto out;
357 		}
358 
359 		/* Determine new timeout. */
360 		if (timo < MAX_RESEND_DELAY)
361 			timo++;
362 		else
363 			printf("BOOTP timeout for server 0x%x\n",
364 			       ntohl(sin->sin_addr.s_addr));
365 
366 		/*
367 		 * Wait for up to timo seconds for a reply.
368 		 * The socket receive timeout was set to 1 second.
369 		 */
370 		secs = timo;
371 		while (secs > 0) {
372 			aio.iov_base = (caddr_t) reply;
373 			aio.iov_len = sizeof(*reply);
374 
375 			auio.uio_iov = &aio;
376 			auio.uio_iovcnt = 1;
377 			auio.uio_segflg = UIO_SYSSPACE;
378 			auio.uio_rw = UIO_READ;
379 			auio.uio_offset = 0;
380 			auio.uio_resid = sizeof(*reply);
381 			auio.uio_procp = procp;
382 
383 			rcvflg = 0;
384 			error = soreceive(so, NULL, &auio, NULL, NULL, &rcvflg);
385 			if (error == EWOULDBLOCK) {
386 				secs--;
387 				call->secs=htons(ntohs(call->secs)+1);
388 				continue;
389 			}
390 			if (error)
391 				goto out;
392 			len = sizeof(*reply) - auio.uio_resid;
393 
394 			/* Does the reply contain at least a header? */
395 			if (len < MIN_REPLY_HDR)
396 				continue;
397 
398 			/* Is it the right reply? */
399 			if (reply->op != 2)
400 			  continue;
401 
402 			if (reply->xid != call->xid)
403 				continue;
404 
405 			if (reply->hlen != call->hlen)
406 			  continue;
407 
408 			if (bcmp(reply->chaddr,call->chaddr,call->hlen))
409 			  continue;
410 
411 			goto gotreply;	/* break two levels */
412 
413 		} /* while secs */
414 	} /* forever send/receive */
415 
416 	error = ETIMEDOUT;
417 	goto out;
418 
419  gotreply:
420  out:
421 	soclose(so);
422 	return error;
423 }
424 
425 int
426 bootpc_fakeup_interface(struct ifreq *ireq,struct socket *so,
427 			struct proc *procp)
428 {
429   struct sockaddr_in *sin;
430   int error;
431   struct sockaddr_in dst;
432   struct sockaddr_in gw;
433   struct sockaddr_in mask;
434 
435   /*
436    * Bring up the interface.
437    *
438    * Get the old interface flags and or IFF_UP into them; if
439    * IFF_UP set blindly, interface selection can be clobbered.
440    */
441   error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp);
442   if (error)
443     panic("bootpc_fakeup_interface: GIFFLAGS, error=%d", error);
444   ireq->ifr_flags |= IFF_UP;
445   error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)ireq, procp);
446   if (error)
447     panic("bootpc_fakeup_interface: SIFFLAGS, error=%d", error);
448 
449   /*
450    * Do enough of ifconfig(8) so that the chosen interface
451    * can talk to the servers.  (just set the address)
452    */
453 
454   /* addr is 0.0.0.0 */
455 
456   sin = (struct sockaddr_in *)&ireq->ifr_addr;
457   bzero((caddr_t)sin, sizeof(*sin));
458   sin->sin_len = sizeof(*sin);
459   sin->sin_family = AF_INET;
460   sin->sin_addr.s_addr = INADDR_ANY;
461   error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp);
462   if (error)
463     panic("bootpc_fakeup_interface: set if addr, error=%d", error);
464 
465   /* netmask is 0.0.0.0 */
466 
467   sin = (struct sockaddr_in *)&ireq->ifr_addr;
468   bzero((caddr_t)sin, sizeof(*sin));
469   sin->sin_len = sizeof(*sin);
470   sin->sin_family = AF_INET;
471   sin->sin_addr.s_addr = INADDR_ANY;
472   error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp);
473   if (error)
474     panic("bootpc_fakeup_interface: set if net addr, error=%d", error);
475 
476   /* Broadcast is 255.255.255.255 */
477 
478   sin = (struct sockaddr_in *)&ireq->ifr_addr;
479   bzero((caddr_t)sin, sizeof(*sin));
480   sin->sin_len = sizeof(*sin);
481   sin->sin_family = AF_INET;
482   sin->sin_addr.s_addr = INADDR_BROADCAST;
483   error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp);
484   if (error)
485     panic("bootpc_fakeup_interface: set if broadcast addr, error=%d", error);
486 
487   /* Add default route to 0.0.0.0 so we can send data */
488 
489   bzero((caddr_t) &dst, sizeof(dst));
490   dst.sin_len=sizeof(dst);
491   dst.sin_family=AF_INET;
492   dst.sin_addr.s_addr = htonl(0);
493 
494   bzero((caddr_t) &gw, sizeof(gw));
495   gw.sin_len=sizeof(gw);
496   gw.sin_family=AF_INET;
497   gw.sin_addr.s_addr = htonl(0x0);
498 
499   bzero((caddr_t) &mask, sizeof(mask));
500   mask.sin_len=sizeof(mask);
501   mask.sin_family=AF_INET;
502   mask.sin_addr.s_addr = htonl(0);
503 
504   error = rtrequest(RTM_ADD,
505 		    (struct sockaddr *) &dst,
506 		    (struct sockaddr *) &gw,
507 		    (struct sockaddr *) &mask,
508 		    RTF_UP | RTF_STATIC
509 		    , NULL);
510   if (error)
511     printf("bootpc_fakeup_interface: add default route, error=%d\n", error);
512   return error;
513 }
514 
515 int
516 bootpc_adjust_interface(struct ifreq *ireq,struct socket *so,
517 			struct sockaddr_in *myaddr,
518 			struct sockaddr_in *netmask,
519 			struct sockaddr_in *gw,
520 			struct proc *procp)
521 {
522   int error;
523   struct sockaddr_in oldgw;
524   struct sockaddr_in olddst;
525   struct sockaddr_in oldmask;
526   struct sockaddr_in *sin;
527 
528   /* Remove old default route to 0.0.0.0 */
529 
530   bzero((caddr_t) &olddst, sizeof(olddst));
531   olddst.sin_len=sizeof(olddst);
532   olddst.sin_family=AF_INET;
533   olddst.sin_addr.s_addr = INADDR_ANY;
534 
535   bzero((caddr_t) &oldgw, sizeof(oldgw));
536   oldgw.sin_len=sizeof(oldgw);
537   oldgw.sin_family=AF_INET;
538   oldgw.sin_addr.s_addr = INADDR_ANY;
539 
540   bzero((caddr_t) &oldmask, sizeof(oldmask));
541   oldmask.sin_len=sizeof(oldmask);
542   oldmask.sin_family=AF_INET;
543   oldmask.sin_addr.s_addr = INADDR_ANY;
544 
545   error = rtrequest(RTM_DELETE,
546 		    (struct sockaddr *) &olddst,
547 		    (struct sockaddr *) &oldgw,
548 		    (struct sockaddr *) &oldmask,
549 		    (RTF_UP | RTF_STATIC), NULL);
550   if (error) {
551     printf("nfs_boot: del default route, error=%d\n", error);
552     return error;
553   }
554 
555   /*
556    * Do enough of ifconfig(8) so that the chosen interface
557    * can talk to the servers.  (just set the address)
558    */
559   bcopy(netmask,&ireq->ifr_addr,sizeof(*netmask));
560   error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)ireq, procp);
561   if (error)
562     panic("nfs_boot: set if netmask, error=%d", error);
563 
564   /* Broadcast is with host part of IP address all 1's */
565 
566   sin = (struct sockaddr_in *)&ireq->ifr_addr;
567   bzero((caddr_t)sin, sizeof(*sin));
568   sin->sin_len = sizeof(*sin);
569   sin->sin_family = AF_INET;
570   sin->sin_addr.s_addr = myaddr->sin_addr.s_addr | ~ netmask->sin_addr.s_addr;
571   error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)ireq, procp);
572   if (error)
573     panic("bootpc_call: set if broadcast addr, error=%d", error);
574 
575   bcopy(myaddr,&ireq->ifr_addr,sizeof(*myaddr));
576   error = ifioctl(so, SIOCSIFADDR, (caddr_t)ireq, procp);
577   if (error)
578     panic("nfs_boot: set if addr, error=%d", error);
579 
580   /* Add new default route */
581 
582   error = rtrequest(RTM_ADD,
583 		    (struct sockaddr *) &olddst,
584 		    (struct sockaddr *) gw,
585 		    (struct sockaddr *) &oldmask,
586 		    (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
587   if (error) {
588     printf("nfs_boot: add net route, error=%d\n", error);
589     return error;
590   }
591 
592   return 0;
593 }
594 
595 static int setfs(addr, path, p)
596 	struct sockaddr_in *addr;
597 	char *path;
598 	char *p;
599 {
600 	unsigned ip = 0;
601 	int val;
602 
603 	if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
604 	ip = val << 24;
605 	if (*p != '.') return(0);
606 	p++;
607 	if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
608 	ip |= (val << 16);
609 	if (*p != '.') return(0);
610 	p++;
611 	if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
612 	ip |= (val << 8);
613 	if (*p != '.') return(0);
614 	p++;
615 	if (((val = getdec(&p)) < 0) || (val > 255)) return(0);
616 	ip |= val;
617 	if (*p != ':') return(0);
618 	p++;
619 
620 	addr->sin_addr.s_addr = htonl(ip);
621 	addr->sin_len = sizeof(struct sockaddr_in);
622 	addr->sin_family = AF_INET;
623 
624 	strncpy(path,p,MNAMELEN-1);
625 	return(1);
626 }
627 
628 static int getdec(ptr)
629 	char **ptr;
630 {
631 	char *p = *ptr;
632 	int ret=0;
633 	if ((*p < '0') || (*p > '9')) return(-1);
634 	while ((*p >= '0') && (*p <= '9')) {
635 		ret = ret*10 + (*p - '0');
636 		p++;
637 	}
638 	*ptr = p;
639 	return(ret);
640 }
641 
642 static char *substr(a,b)
643 	char *a,*b;
644 {
645 	char *loc1;
646 	char *loc2;
647 
648         while (*a != '\0') {
649                 loc1 = a;
650                 loc2 = b;
651                 while (*loc1 == *loc2++) {
652                         if (*loc1 == '\0') return (0);
653                         loc1++;
654                         if (*loc2 == '\0') return (loc1);
655                 }
656         a++;
657         }
658         return (0);
659 }
660 
661 static void mountopts(args,p)
662 	struct nfs_args *args;
663 	char *p;
664 {
665 	char *tmp;
666 
667 	args->flags = NFSMNT_RSIZE | NFSMNT_WSIZE | NFSMNT_RESVPORT;
668 	args->sotype = SOCK_DGRAM;
669 	if ((tmp = (char *)substr(p,"rsize=")))
670 		args->rsize=getdec(&tmp);
671 	if ((tmp = (char *)substr(p,"wsize=")))
672 		args->wsize=getdec(&tmp);
673 	if ((tmp = (char *)substr(p,"intr")))
674 		args->flags |= NFSMNT_INT;
675 	if ((tmp = (char *)substr(p,"soft")))
676 		args->flags |= NFSMNT_SOFT;
677 	if ((tmp = (char *)substr(p,"noconn")))
678 		args->flags |= NFSMNT_NOCONN;
679 	if ((tmp = (char *)substr(p, "tcp")))
680 	    args->sotype = SOCK_STREAM;
681 }
682 
683 static int xdr_opaque_decode(mptr,buf,len)
684      struct mbuf **mptr;
685      u_char *buf;
686      int len;
687 {
688   struct mbuf *m;
689   int alignedlen;
690 
691   m = *mptr;
692   alignedlen = ( len + 3 ) & ~3;
693 
694   if (m->m_len < alignedlen) {
695     m = m_pullup(m,alignedlen);
696     if (m == NULL) {
697       *mptr = NULL;
698       return EBADRPC;
699     }
700   }
701   bcopy(mtod(m,u_char *),buf,len);
702   m_adj(m,alignedlen);
703   *mptr = m;
704   return 0;
705 }
706 
707 static int xdr_int_decode(mptr,iptr)
708      struct mbuf **mptr;
709      int *iptr;
710 {
711   u_int32_t i;
712   if (xdr_opaque_decode(mptr,(u_char *) &i,sizeof(u_int32_t)))
713     return EBADRPC;
714   *iptr = fxdr_unsigned(u_int32_t,i);
715   return 0;
716 }
717 
718 static void printip(char *prefix,struct in_addr addr)
719 {
720   unsigned int ip;
721 
722   ip = ntohl(addr.s_addr);
723 
724   printf("%s is %d.%d.%d.%d\n",prefix,
725 	 ip >> 24, (ip >> 16) & 255 ,(ip >> 8) & 255 ,ip & 255 );
726 }
727 
728 void
729 bootpc_init(void)
730 {
731   struct bootp_packet call;
732   struct bootp_packet reply;
733   static u_int32_t xid = ~0xFF;
734 
735   struct ifreq ireq;
736   struct ifnet *ifp;
737   struct socket *so;
738   int error;
739   int code,ncode,len;
740   int i,j;
741   char *p;
742   unsigned int ip;
743 
744   struct sockaddr_in myaddr;
745   struct sockaddr_in netmask;
746   struct sockaddr_in gw;
747   int gotgw=0;
748   int gotnetmask=0;
749   int gotrootpath=0;
750   int gotswappath=0;
751   char lookup_path[24];
752 
753 #define EALEN 6
754   unsigned char ea[EALEN];
755   struct ifaddr *ifa;
756   struct sockaddr_dl *sdl = NULL;
757   char *delim;
758 
759   struct nfsv3_diskless *nd = &nfsv3_diskless;
760   struct proc *procp = curproc;
761 
762   /*
763    * If already filled in, don't touch it here
764    */
765   if (nfs_diskless_valid)
766     return;
767 
768   /*
769    * Bump time if 0.
770    */
771   if (!time.tv_sec)
772     time.tv_sec++;
773 
774   /*
775    * Find a network interface.
776    */
777   for (ifp = TAILQ_FIRST(&ifnet); ifp != 0; ifp = TAILQ_NEXT(ifp,if_link))
778     if ((ifp->if_flags &
779       (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
780 	break;
781   if (ifp == NULL)
782     panic("bootpc_init: no suitable interface");
783   bzero(&ireq,sizeof(ireq));
784   sprintf(ireq.ifr_name, "%s%d", ifp->if_name,ifp->if_unit);
785   strcpy(nd->myif.ifra_name,ireq.ifr_name);
786   printf("bootpc_init: using network interface '%s'\n",
787 	 ireq.ifr_name);
788 
789   if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0,procp)) != 0)
790     panic("nfs_boot: socreate, error=%d", error);
791 
792   bootpc_fakeup_interface(&ireq,so,procp);
793 
794   printf("Bootpc testing starting\n");
795 
796   /* Get HW address */
797 
798   for (ifa = TAILQ_FIRST(&ifp->if_addrhead) ;ifa;
799        ifa=TAILQ_NEXT(ifa,ifa_link))
800     if (ifa->ifa_addr->sa_family == AF_LINK &&
801 	(sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) &&
802 	sdl->sdl_type == IFT_ETHER)
803       break;
804 
805   if (!sdl)
806     panic("bootpc: Unable to find HW address");
807   if (sdl->sdl_alen != EALEN )
808     panic("bootpc: HW address len is %d, expected value is %d",
809 	  sdl->sdl_alen,EALEN);
810 
811   printf("bootpc hw address is ");
812   delim="";
813   for (j=0;j<sdl->sdl_alen;j++) {
814     printf("%s%x",delim,((unsigned char *)LLADDR(sdl))[j]);
815     delim=":";
816   }
817   printf("\n");
818 
819 #if 0
820   bootpboot_p_iflist();
821   bootpboot_p_rtlist();
822 #endif
823 
824   bzero((caddr_t) &call, sizeof(call));
825 
826   /* bootpc part */
827   call.op = 1; 			/* BOOTREQUEST */
828   call.htype= 1;		/* 10mb ethernet */
829   call.hlen=sdl->sdl_alen;	/* Hardware address length */
830   call.hops=0;
831   xid++;
832   call.xid = txdr_unsigned(xid);
833   bcopy(LLADDR(sdl),&call.chaddr,sdl->sdl_alen);
834 
835   call.vend[0]=99;
836   call.vend[1]=130;
837   call.vend[2]=83;
838   call.vend[3]=99;
839   call.vend[4]=255;
840 
841   call.secs = 0;
842   call.flags = htons(0x8000); /* We need an broadcast answer */
843 
844   error = bootpc_call(&call,&reply,procp);
845 
846   if (error) {
847 #ifdef BOOTP_NFSROOT
848     panic("BOOTP call failed");
849 #endif
850     return;
851   }
852 
853   bzero(&myaddr,sizeof(myaddr));
854   bzero(&netmask,sizeof(netmask));
855   bzero(&gw,sizeof(gw));
856 
857   myaddr.sin_len = sizeof(myaddr);
858   myaddr.sin_family = AF_INET;
859 
860   netmask.sin_len = sizeof(netmask);
861   netmask.sin_family = AF_INET;
862 
863   gw.sin_len = sizeof(gw);
864   gw.sin_family= AF_INET;
865 
866   nd->root_args.version = NFS_ARGSVERSION;
867   nd->root_args.rsize = 8192;
868   nd->root_args.wsize = 8192;
869   nd->root_args.sotype = SOCK_DGRAM;
870   nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT);
871 
872   nd->swap_saddr.sin_len = sizeof(gw);
873   nd->swap_saddr.sin_family = AF_INET;
874 
875   nd->swap_args.version = NFS_ARGSVERSION;
876   nd->swap_args.rsize = 8192;
877   nd->swap_args.wsize = 8192;
878   nd->swap_args.sotype = SOCK_DGRAM;
879   nd->swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT);
880 
881   myaddr.sin_addr = reply.yiaddr;
882 
883   ip = ntohl(myaddr.sin_addr.s_addr);
884   sprintf(lookup_path,"swap.%d.%d.%d.%d",
885 	  ip >> 24, (ip >> 16) & 255 ,(ip >> 8) & 255 ,ip & 255 );
886 
887   printip("My ip address",myaddr.sin_addr);
888 
889   printip("Server ip address",reply.siaddr);
890 
891   gw.sin_addr = reply.giaddr;
892   printip("Gateway ip address",reply.giaddr);
893 
894   if (reply.sname[0])
895     printf("Server name is %s\n",reply.sname);
896   if (reply.file[0])
897     printf("boot file is %s\n",reply.file);
898   if (reply.vend[0]==99 && reply.vend[1]==130 &&
899       reply.vend[2]==83 && reply.vend[3]==99) {
900     j=4;
901     ncode = reply.vend[j];
902     while (j<sizeof(reply.vend)) {
903       code = reply.vend[j] = ncode;
904       if (code==255)
905 	break;
906       if (code==0) {
907 	j++;
908 	continue;
909       }
910       len = reply.vend[j+1];
911       j+=2;
912       if (len+j>=sizeof(reply.vend)) {
913 	printf("Truncated field");
914 	break;
915       }
916       ncode = reply.vend[j+len];
917       reply.vend[j+len]='\0';
918       p = &reply.vend[j];
919       switch (code) {
920       case 1:
921 	if (len!=4)
922 	  panic("bootpc: subnet mask len is %d",len);
923 	bcopy(&reply.vend[j],&netmask.sin_addr,4);
924 	gotnetmask=1;
925 	printip("Subnet mask",netmask.sin_addr);
926 	break;
927       case 6:	/* Domain Name servers. Unused */
928       case 16:	/* Swap server IP address. unused */
929       case 2:
930 	/* Time offset */
931 	break;
932       case 3:
933 	/* Routers */
934 	if (len % 4)
935 	  panic("bootpc: Router Len is %d",len);
936 	if (len > 0) {
937 	  bcopy(&reply.vend[j],&gw.sin_addr,4);
938 	  printip("Router",gw.sin_addr);
939 	  gotgw=1;
940 	}
941 	break;
942       case 17:
943 	if (setfs(&nd->root_saddr, nd->root_hostnam, p)) {
944 	  printf("rootfs is %s\n",p);
945 	  gotrootpath=1;
946 	} else
947 	  panic("Failed to set rootfs to %s",p);
948 	break;
949       case 12:
950 	if (len>=MAXHOSTNAMELEN)
951 	  panic("bootpc: hostname  >=%d bytes",MAXHOSTNAMELEN);
952 	strncpy(nd->my_hostnam,&reply.vend[j],len);
953 	nd->my_hostnam[len]=0;
954 	strncpy(hostname,&reply.vend[j],len);
955 	hostname[len]=0;
956 	printf("Hostname is %s\n",hostname);
957 	break;
958       case 128:
959 	if (setfs(&nd->swap_saddr, nd->swap_hostnam, p)) {
960 	  gotswappath=1;
961 	  printf("swapfs is %s\n",p);
962 	} else
963 	  panic("Failed to set swapfs to %s",p);
964 	break;
965       case 129:
966 	{
967 	  int swaplen;
968 	  if (len!=4)
969 	    panic("bootpc: Expected 4 bytes for swaplen, not %d bytes",len);
970 	  bcopy(&reply.vend[j],&swaplen,4);
971 	  nd->swap_nblks = ntohl(swaplen);
972 	  printf("bootpc: Swap size is %d KB\n",nd->swap_nblks);
973 	}
974 	break;
975       case 130:	/* root mount options */
976 	mountopts(&nd->root_args,p);
977 	break;
978       case 131:	/* swap mount options */
979 	mountopts(&nd->swap_args,p);
980 	break;
981       default:
982 	printf("Ignoring field type %d\n",code);
983       }
984       j+=len;
985     }
986   }
987 
988   if (!gotswappath)
989     nd->swap_nblks = 0;
990 #ifdef BOOTP_NFSROOT
991   if (!gotrootpath)
992     panic("bootpc: No root path offered");
993 #endif
994 
995   if (!gotnetmask) {
996     if (IN_CLASSA(ntohl(myaddr.sin_addr.s_addr)))
997       netmask.sin_addr.s_addr = htonl(IN_CLASSA_NET);
998     else if (IN_CLASSB(ntohl(myaddr.sin_addr.s_addr)))
999       netmask.sin_addr.s_addr = htonl(IN_CLASSB_NET);
1000     else
1001       netmask.sin_addr.s_addr = htonl(IN_CLASSC_NET);
1002   }
1003   if (!gotgw) {
1004     /* Use proxyarp */
1005     gw.sin_addr.s_addr = myaddr.sin_addr.s_addr;
1006   }
1007 
1008 #if 0
1009   bootpboot_p_iflist();
1010   bootpboot_p_rtlist();
1011 #endif
1012   error = bootpc_adjust_interface(&ireq,so,
1013 				  &myaddr,&netmask,&gw,procp);
1014 
1015   soclose(so);
1016 
1017 #if 0
1018   bootpboot_p_iflist();
1019   bootpboot_p_rtlist();
1020 #endif
1021 
1022   if (gotrootpath) {
1023 
1024     error = md_mount(&nd->root_saddr, nd->root_hostnam,
1025 		     nd->root_fh, &nd->root_fhsize,
1026 		     &nd->root_args,procp);
1027     if (error)
1028       panic("nfs_boot: mountd root, error=%d", error);
1029 
1030     if (gotswappath) {
1031 
1032       error = md_mount(&nd->swap_saddr,
1033 		       nd->swap_hostnam,
1034 		       nd->swap_fh, &nd->swap_fhsize,&nd->swap_args,procp);
1035       if (error)
1036 	panic("nfs_boot: mountd swap, error=%d", error);
1037 
1038       error = md_lookup_swap(&nd->swap_saddr,lookup_path,nd->swap_fh,
1039 			     &nd->swap_fhsize, &nd->swap_args,procp);
1040       if (error)
1041 	panic("nfs_boot: lookup swap, error=%d", error);
1042     }
1043     nfs_diskless_valid = 3;
1044   }
1045 
1046 
1047   bcopy(&myaddr,&nd->myif.ifra_addr,sizeof(myaddr));
1048   bcopy(&myaddr,&nd->myif.ifra_broadaddr,sizeof(myaddr));
1049   ((struct sockaddr_in *) &nd->myif.ifra_broadaddr)->sin_addr.s_addr =
1050     myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr;
1051   bcopy(&netmask,&nd->myif.ifra_mask,sizeof(netmask));
1052 
1053 #if 0
1054   bootpboot_p_iflist();
1055   bootpboot_p_rtlist();
1056 #endif
1057   return;
1058 }
1059 
1060 /*
1061  * RPC: mountd/mount
1062  * Given a server pathname, get an NFS file handle.
1063  * Also, sets sin->sin_port to the NFS service port.
1064  */
1065 static int
1066 md_mount(mdsin, path, fhp, fhsizep, args, procp)
1067 	struct sockaddr_in *mdsin;		/* mountd server address */
1068 	char *path;
1069 	u_char *fhp;
1070 	int *fhsizep;
1071 	struct nfs_args *args;
1072 	struct proc *procp;
1073 {
1074 	struct mbuf *m;
1075 	int error;
1076 	int authunixok;
1077 	int authcount;
1078 	int authver;
1079 
1080 #ifdef BOOTP_NFSV3
1081 	/* First try NFS v3 */
1082 	/* Get port number for MOUNTD. */
1083 	error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3,
1084 						 &mdsin->sin_port, procp);
1085 	if (!error) {
1086 	  m = xdr_string_encode(path, strlen(path));
1087 
1088 	  /* Do RPC to mountd. */
1089 	  error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3,
1090 			    RPCMNT_MOUNT, &m, NULL, curproc);
1091 	}
1092 	if (!error) {
1093 	  args->flags |= NFSMNT_NFSV3;
1094 	} else {
1095 #endif
1096 	  /* Fallback to NFS v2 */
1097 
1098 	  /* Get port number for MOUNTD. */
1099 	  error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
1100 			       &mdsin->sin_port, procp);
1101 	  if (error) return error;
1102 
1103 	  m = xdr_string_encode(path, strlen(path));
1104 
1105 	  /* Do RPC to mountd. */
1106 	  error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
1107 			    RPCMNT_MOUNT, &m, NULL, curproc);
1108 	  if (error)
1109 	    return error;	/* message already freed */
1110 
1111 #ifdef BOOTP_NFSV3
1112 	}
1113 #endif
1114 
1115 	if (xdr_int_decode(&m,&error) || error)
1116 	  goto bad;
1117 
1118 	if (args->flags & NFSMNT_NFSV3) {
1119 	  if (xdr_int_decode(&m,fhsizep) ||
1120 	      *fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 )
1121 	    goto bad;
1122 	} else
1123 	  *fhsizep = NFSX_V2FH;
1124 
1125 	if (xdr_opaque_decode(&m,fhp,*fhsizep))
1126 	  goto bad;
1127 
1128 	if (args->flags & NFSMNT_NFSV3) {
1129 	  if (xdr_int_decode(&m,&authcount))
1130 	    goto bad;
1131 	  authunixok = 0;
1132 	  if (authcount<0 || authcount>100)
1133 	    goto bad;
1134 	  while (authcount>0) {
1135 	    if (xdr_int_decode(&m,&authver))
1136 	      goto bad;
1137 	    if (authver == RPCAUTH_UNIX)
1138 	      authunixok = 1;
1139 	    authcount--;
1140 	  }
1141 	  if (!authunixok)
1142 	    goto bad;
1143 	}
1144 
1145 	/* Set port number for NFS use. */
1146 	error = krpc_portmap(mdsin, NFS_PROG,
1147 			     (args->flags & NFSMNT_NFSV3)?NFS_VER3:NFS_VER2,
1148 			     &mdsin->sin_port, procp);
1149 
1150 	goto out;
1151 
1152 bad:
1153 	error = EBADRPC;
1154 
1155 out:
1156 	m_freem(m);
1157 	return error;
1158 }
1159 
1160 
1161 static int md_lookup_swap(mdsin, path, fhp, fhsizep, args, procp)
1162 	struct sockaddr_in *mdsin;		/* mountd server address */
1163 	char *path;
1164 	u_char *fhp;
1165 	int *fhsizep;
1166 	struct nfs_args *args;
1167 	struct proc *procp;
1168 {
1169 	struct mbuf *m;
1170 	int error;
1171 	int size = -1;
1172 	int attribs_present;
1173 	int status;
1174 	union {
1175 	  u_int32_t v2[17];
1176 	  u_int32_t v3[21];
1177 	} fattribs;
1178 
1179 	m = m_get(M_WAIT,MT_DATA);
1180 	if (!m)
1181 	  	return ENOBUFS;
1182 
1183 	if (args->flags & NFSMNT_NFSV3) {
1184 	  *mtod(m,u_int32_t *) = txdr_unsigned(*fhsizep);
1185 	  bcopy(fhp,mtod(m,u_char *)+sizeof(u_int32_t),*fhsizep);
1186 	  m->m_len = *fhsizep + sizeof(u_int32_t);
1187 	} else {
1188 	  bcopy(fhp,mtod(m,u_char *),NFSX_V2FH);
1189 	  m->m_len = NFSX_V2FH;
1190 	}
1191 
1192 	m->m_next = xdr_string_encode(path, strlen(path));
1193 	if (!m->m_next) {
1194 	  error = ENOBUFS;
1195 	  goto out;
1196 	}
1197 
1198 	/* Do RPC to nfsd. */
1199 	if (args->flags & NFSMNT_NFSV3)
1200 	  error = krpc_call(mdsin, NFS_PROG, NFS_VER3,
1201 			    NFSPROC_LOOKUP, &m, NULL, procp);
1202 	else
1203 	  error = krpc_call(mdsin, NFS_PROG, NFS_VER2,
1204 			    NFSV2PROC_LOOKUP, &m, NULL, procp);
1205 	if (error)
1206 	  return error;	/* message already freed */
1207 
1208 	if (xdr_int_decode(&m,&status))
1209 	  goto bad;
1210 	if (status) {
1211 	  error = ENOENT;
1212 	  goto out;
1213 	}
1214 
1215 	if (args->flags & NFSMNT_NFSV3) {
1216 	  if (xdr_int_decode(&m,fhsizep) ||
1217 	      *fhsizep > NFSX_V3FHMAX || *fhsizep <= 0 )
1218 	    goto bad;
1219 	} else
1220 	  *fhsizep = NFSX_V2FH;
1221 
1222 	if (xdr_opaque_decode(&m, fhp, *fhsizep))
1223 	  goto bad;
1224 
1225 	if (args->flags & NFSMNT_NFSV3) {
1226 	  if (xdr_int_decode(&m,&attribs_present))
1227 	    goto bad;
1228 	  if (attribs_present) {
1229 	    if (xdr_opaque_decode(&m,(u_char *) &fattribs.v3,
1230 				  sizeof(u_int32_t)*21))
1231 	      goto bad;
1232 	    size = fxdr_unsigned(u_int32_t, fattribs.v3[6]);
1233 	  }
1234 	} else {
1235   	  if (xdr_opaque_decode(&m,(u_char *) &fattribs.v2,
1236 				sizeof(u_int32_t)*17))
1237 	    goto bad;
1238 	  size = fxdr_unsigned(u_int32_t, fattribs.v2[5]);
1239 	}
1240 
1241 	if (!nfsv3_diskless.swap_nblks && size!= -1) {
1242 	  nfsv3_diskless.swap_nblks = size/1024;
1243 	  printf("md_lookup_swap: Swap size is %d KB\n",
1244 		 nfsv3_diskless.swap_nblks);
1245 	}
1246 
1247 	goto out;
1248 
1249 bad:
1250 	error = EBADRPC;
1251 
1252 out:
1253 	m_freem(m);
1254 	return error;
1255 }
1256