1 /* $NetBSD: nfs_bootdhcp.c,v 1.9 1998/09/13 13:49:29 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass and Gordon W. Ross. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Support for NFS diskless booting with BOOTP (RFC951, RFC1048) 41 * 42 * History: 43 * 44 * Tor Egge developed the initial version of this code based on 45 * the Sun RPC/bootparam sources nfs_boot.c and krpc_subr.c and 46 * submitted that work to NetBSD as bugreport "kern/2351" on 47 * 29 Apr 1996. 48 * 49 * Gordon Ross reorganized Tor's version into this form and 50 * integrated it into the NetBSD sources during Aug 1997. 51 */ 52 53 #include "opt_nfs_boot.h" 54 55 #include <sys/param.h> 56 #include <sys/systm.h> 57 #include <sys/kernel.h> 58 #include <sys/conf.h> 59 #include <sys/device.h> 60 #include <sys/ioctl.h> 61 #include <sys/proc.h> 62 #include <sys/mount.h> 63 #include <sys/mbuf.h> 64 #include <sys/reboot.h> 65 #include <sys/socket.h> 66 #include <sys/socketvar.h> 67 68 #include <net/if.h> 69 #include <net/if_types.h> 70 #include <net/if_arp.h> /* ARPHRD_ETHER, etc. */ 71 #include <net/if_dl.h> 72 #include <net/if_ether.h> 73 #include <net/route.h> 74 75 #include <netinet/in.h> 76 #include <netinet/if_inarp.h> 77 78 #include <nfs/rpcv2.h> 79 80 #include <nfs/nfsproto.h> 81 #include <nfs/nfs.h> 82 #include <nfs/nfsmount.h> 83 #include <nfs/nfsdiskless.h> 84 85 /* 86 * There are two implementations of NFS diskless boot. 87 * This implementation uses BOOTP (RFC951, RFC1048), and 88 * the other uses Sun RPC/bootparams (nfs_bootparam.c). 89 * 90 * This method gets everything it needs with one BOOTP 91 * request and reply. Note that this actually uses only 92 * the old BOOTP functionality subset of DHCP. It is not 93 * clear that DHCP provides any advantage over BOOTP for 94 * diskless boot. DHCP allows the server to assign an IP 95 * address without any a-priori knowledge of the client, 96 * but we require that the server has a-priori knowledge 97 * of the client so it can export our (unique) NFS root. 98 * Given that the server needs a-priori knowledge about 99 * the client anyway, it might as well assign a fixed IP 100 * address for the client and support BOOTP. 101 * 102 * On the other hand, disk-FULL clients may use DHCP, but 103 * in that case the DHCP client should be user-mode code, 104 * and has no bearing on the code below. -gwr 105 */ 106 107 /* Begin stuff from bootp.h */ 108 /* Definitions from RFC951 */ 109 #define BP_CHADDR_LEN 16 110 #define BP_SNAME_LEN 64 111 #define BP_FILE_LEN 128 112 #define BP_VEND_LEN 64 113 struct bootp { 114 u_int8_t bp_op; /* packet opcode type */ 115 u_int8_t bp_htype; /* hardware addr type */ 116 u_int8_t bp_hlen; /* hardware addr length */ 117 u_int8_t bp_hops; /* gateway hops */ 118 u_int32_t bp_xid; /* transaction ID */ 119 u_int16_t bp_secs; /* seconds since boot began */ 120 u_int16_t bp_flags; /* RFC1532 broadcast, etc. */ 121 struct in_addr bp_ciaddr; /* client IP address */ 122 struct in_addr bp_yiaddr; /* 'your' IP address */ 123 struct in_addr bp_siaddr; /* server IP address */ 124 struct in_addr bp_giaddr; /* gateway IP address */ 125 u_int8_t bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ 126 char bp_sname[BP_SNAME_LEN]; /* server host name */ 127 char bp_file[BP_FILE_LEN]; /* boot file name */ 128 u_int8_t bp_vend[BP_VEND_LEN]; /* RFC1048 options */ 129 /* 130 * Note that BOOTP packets are allowed to be longer 131 * (see RFC 1532 sect. 2.1) and common practice is to 132 * allow the option data in bp_vend to extend into the 133 * additional space provided in longer packets. 134 */ 135 }; 136 137 #define IPPORT_BOOTPS 67 138 #define IPPORT_BOOTPC 68 139 140 #define BOOTREQUEST 1 141 #define BOOTREPLY 2 142 143 /* 144 * Is this available from the sockaddr_dl somehow? 145 * Perhaps (struct arphdr)->ar_hrd = ARPHRD_ETHER? 146 * The interface has ->if_type but not the ARP fmt. 147 */ 148 #define HTYPE_ETHERNET 1 149 150 /* 151 * Vendor magic cookie (v_magic) for RFC1048 152 */ 153 static const u_int8_t vm_rfc1048[4] = { 99, 130, 83, 99 }; 154 155 /* 156 * Tag values used to specify what information is being supplied in 157 * the vendor (options) data area of the packet. 158 */ 159 /* RFC 1048 */ 160 #define TAG_END ((unsigned char) 255) 161 #define TAG_PAD ((unsigned char) 0) 162 #define TAG_SUBNET_MASK ((unsigned char) 1) 163 #define TAG_TIME_OFFSET ((unsigned char) 2) 164 #define TAG_GATEWAY ((unsigned char) 3) 165 #define TAG_TIME_SERVER ((unsigned char) 4) 166 #define TAG_NAME_SERVER ((unsigned char) 5) 167 #define TAG_DOMAIN_SERVER ((unsigned char) 6) 168 #define TAG_LOG_SERVER ((unsigned char) 7) 169 #define TAG_COOKIE_SERVER ((unsigned char) 8) 170 #define TAG_LPR_SERVER ((unsigned char) 9) 171 #define TAG_IMPRESS_SERVER ((unsigned char) 10) 172 #define TAG_RLP_SERVER ((unsigned char) 11) 173 #define TAG_HOST_NAME ((unsigned char) 12) 174 #define TAG_BOOT_SIZE ((unsigned char) 13) 175 /* RFC 1395 */ 176 #define TAG_DUMP_FILE ((unsigned char) 14) 177 #define TAG_DOMAIN_NAME ((unsigned char) 15) 178 #define TAG_SWAP_SERVER ((unsigned char) 16) 179 #define TAG_ROOT_PATH ((unsigned char) 17) 180 /* End of stuff from bootp.h */ 181 182 #ifdef NFS_BOOT_DHCP 183 #define TAG_REQ_ADDR ((unsigned char) 50) 184 #define TAG_LEASETIME ((unsigned char) 51) 185 #define TAG_OVERLOAD ((unsigned char) 52) 186 #define TAG_DHCP_MSGTYPE ((unsigned char) 53) 187 #define TAG_SERVERID ((unsigned char) 54) 188 #define TAG_PARAM_REQ ((unsigned char) 55) 189 #define TAG_MSG ((unsigned char) 56) 190 #define TAG_MAXSIZE ((unsigned char) 57) 191 #define TAG_T1 ((unsigned char) 58) 192 #define TAG_T2 ((unsigned char) 59) 193 #define TAG_CLASSID ((unsigned char) 60) 194 #define TAG_CLIENTID ((unsigned char) 61) 195 #endif 196 197 #ifdef NFS_BOOT_DHCP 198 #define DHCPDISCOVER 1 199 #define DHCPOFFER 2 200 #define DHCPREQUEST 3 201 #define DHCPDECLINE 4 202 #define DHCPACK 5 203 #define DHCPNAK 6 204 #define DHCPRELEASE 7 205 #endif 206 207 #ifdef NFS_BOOT_DHCP 208 #define BOOTP_SIZE_MAX (sizeof(struct bootp)+312-64) 209 #else 210 /* 211 * The "extended" size is somewhat arbitrary, but is 212 * constrained by the maximum message size specified 213 * by RFC1533 (567 total). This value increases the 214 * space for options from 64 bytes to 256 bytes. 215 */ 216 #define BOOTP_SIZE_MAX (sizeof(struct bootp)+256-64) 217 #endif 218 #define BOOTP_SIZE_MIN (sizeof(struct bootp)) 219 220 /* Convenience macro */ 221 #define INTOHL(ina) ((u_int32_t)ntohl((ina).s_addr)) 222 223 static int bootpc_call __P((struct socket *, struct ifnet *, 224 struct nfs_diskless *, struct proc *)); 225 static void bootp_extract __P((struct bootp *, int, struct nfs_diskless *)); 226 227 /* #define DEBUG XXX */ 228 229 #ifdef DEBUG 230 #define DPRINT(s) printf("nfs_boot: %s\n", s) 231 #else 232 #define DPRINT(s) (void)0 233 #endif 234 235 236 /* 237 * Get our boot parameters using BOOTP. 238 */ 239 int 240 nfs_bootdhcp(ifp, nd, procp) 241 struct ifnet *ifp; 242 struct nfs_diskless *nd; 243 struct proc *procp; 244 { 245 struct ifaliasreq iareq; 246 struct socket *so; 247 struct sockaddr_in *sin; 248 int error; 249 250 /* 251 * Get a socket to use for various things in here. 252 * After this, use "goto out" to cleanup and return. 253 */ 254 error = socreate(AF_INET, &so, SOCK_DGRAM, 0); 255 if (error) { 256 printf("nfs_boot: socreate, error=%d\n", error); 257 return (error); 258 } 259 260 /* 261 * Do enough of ifconfig(8) so that the chosen interface 262 * can talk to the servers. Use address zero for now. 263 */ 264 memset(&iareq, 0, sizeof(iareq)); 265 memcpy(iareq.ifra_name, ifp->if_xname, IFNAMSIZ); 266 /* Set the I/F address */ 267 sin = (struct sockaddr_in *)&iareq.ifra_addr; 268 sin->sin_len = sizeof(*sin); 269 sin->sin_family = AF_INET; 270 sin->sin_addr.s_addr = INADDR_ANY; 271 /* Leave subnetmask unspecified (len=0) */ 272 /* Set the broadcast addr. */ 273 sin = (struct sockaddr_in *)&iareq.ifra_broadaddr; 274 sin->sin_len = sizeof(*sin); 275 sin->sin_family = AF_INET; 276 sin->sin_addr.s_addr = INADDR_BROADCAST; 277 error = ifioctl(so, SIOCAIFADDR, (caddr_t)&iareq, procp); 278 if (error) { 279 printf("nfs_boot: set ifaddr zero, error=%d\n", error); 280 goto out; 281 } 282 283 /* This function call does the real send/recv work. */ 284 error = bootpc_call(so, ifp, nd, procp); 285 /* Get rid of the temporary (zero) IP address. */ 286 /* 287 * XXX SIOCDIFADDR takes a "struct ifreq", which is 288 * an exact subset of "struct ifaliasreq". 289 */ 290 (void) ifioctl(so, SIOCDIFADDR, (caddr_t)&iareq, procp); 291 /* NOW we can test the error from bootpc_call. */ 292 if (error) 293 goto out; 294 295 /* 296 * Do ifconfig with our real IP address and mask. 297 */ 298 /* I/F address */ 299 sin = (struct sockaddr_in *)&iareq.ifra_addr; 300 sin->sin_addr = nd->nd_myip; 301 /* subnetmask */ 302 if (nd->nd_mask.s_addr) { 303 sin = (struct sockaddr_in *)&iareq.ifra_mask; 304 sin->sin_len = sizeof(*sin); 305 sin->sin_family = AF_INET; 306 sin->sin_addr = nd->nd_mask; 307 } 308 /* Let ifioctl() default the broadcast address. */ 309 sin = (struct sockaddr_in *)&iareq.ifra_broadaddr; 310 sin->sin_len = 0; 311 sin->sin_family = 0; 312 sin->sin_addr.s_addr = 0; 313 error = ifioctl(so, SIOCAIFADDR, (caddr_t)&iareq, procp); 314 if (error) { 315 printf("nfs_boot: set ifaddr real, error=%d\n", error); 316 goto out; 317 } 318 319 out: 320 soclose(so); 321 return (error); 322 } 323 324 struct bootpcontext { 325 int xid; 326 u_char *haddr; 327 u_char halen; 328 struct bootp *replybuf; 329 int replylen; 330 #ifdef NFS_BOOT_DHCP 331 char expected_dhcpmsgtype, dhcp_ok; 332 struct in_addr dhcp_serverip; 333 #endif 334 }; 335 336 static int bootpset __P((struct mbuf*, void*, int)); 337 static int bootpcheck __P((struct mbuf*, void*)); 338 339 static int 340 bootpset(m, context, waited) 341 struct mbuf *m; 342 void *context; 343 int waited; 344 { 345 struct bootp *bootp; 346 347 /* we know it's contigous (in 1 mbuf cluster) */ 348 bootp = mtod(m, struct bootp*); 349 350 bootp->bp_secs = htons(waited); 351 352 return (0); 353 } 354 355 static int 356 bootpcheck(m, context) 357 struct mbuf *m; 358 void *context; 359 { 360 struct bootp *bootp; 361 struct bootpcontext *bpc = context; 362 u_int tag, len; 363 u_char *p, *limit; 364 365 /* 366 * Is this a valid reply? 367 */ 368 if (m->m_pkthdr.len < BOOTP_SIZE_MIN) { 369 DPRINT("short packet"); 370 return (-1); 371 } 372 if (m->m_pkthdr.len > BOOTP_SIZE_MAX) { 373 DPRINT("long packet"); 374 return (-1); 375 } 376 377 /* 378 * don't make first checks more expensive than necessary 379 */ 380 #define ofs(what, elem) ((int)&(((what *)0)->elem)) 381 if (m->m_len < ofs(struct bootp, bp_secs)) { 382 m = m_pullup(m, ofs(struct bootp, bp_secs)); 383 if (m == NULL) 384 return (-1); 385 } 386 #undef ofs 387 bootp = mtod(m, struct bootp*); 388 389 if (bootp->bp_op != BOOTREPLY) { 390 DPRINT("not reply"); 391 return (-1); 392 } 393 if (bootp->bp_hlen != bpc->halen) { 394 DPRINT("bad hwa_len"); 395 return (-1); 396 } 397 if (memcmp(bootp->bp_chaddr, bpc->haddr, bpc->halen)) { 398 DPRINT("wrong hwaddr"); 399 return (-1); 400 } 401 if (bootp->bp_xid != bpc->xid) { 402 DPRINT("wrong xid"); 403 return (-1); 404 } 405 406 /* 407 * OK, it's worth to look deeper. 408 * We copy the mbuf into a flat buffer here because 409 * m_pullup() is a bit limited for this purpose 410 * (doesn't allocate a cluster if necessary). 411 */ 412 bpc->replylen = m->m_pkthdr.len; 413 m_copydata(m, 0, bpc->replylen, (caddr_t)bpc->replybuf); 414 bootp = bpc->replybuf; 415 416 /* 417 * Check if the IP address we get looks correct. 418 * (DHCP servers can send junk to unknown clients.) 419 * XXX more checks might be needed 420 */ 421 if (bootp->bp_yiaddr.s_addr == INADDR_ANY || 422 bootp->bp_yiaddr.s_addr == INADDR_BROADCAST) { 423 printf("nfs_boot: wrong IP addr 0x%x", 424 INTOHL(bootp->bp_yiaddr)); 425 goto warn; 426 } 427 428 /* 429 * Check the vendor data. 430 */ 431 if (memcmp(bootp->bp_vend, vm_rfc1048, 4)) { 432 printf("nfs_boot: reply missing options"); 433 goto warn; 434 } 435 p = &bootp->bp_vend[4]; 436 limit = ((char*)bootp) + bpc->replylen; 437 while (p < limit) { 438 tag = *p++; 439 if (tag == TAG_END) 440 break; 441 if (tag == TAG_PAD) 442 continue; 443 len = *p++; 444 if ((p + len) > limit) { 445 printf("nfs_boot: option %d too long", tag); 446 goto warn; 447 } 448 switch (tag) { 449 #ifdef NFS_BOOT_DHCP 450 case TAG_DHCP_MSGTYPE: 451 if (*p != bpc->expected_dhcpmsgtype) 452 return (-1); 453 bpc->dhcp_ok = 1; 454 break; 455 case TAG_SERVERID: 456 memcpy(&bpc->dhcp_serverip.s_addr, p, 457 sizeof(bpc->dhcp_serverip.s_addr)); 458 break; 459 #endif 460 default: 461 break; 462 } 463 p += len; 464 } 465 return (0); 466 467 warn: 468 printf(" (bad reply from 0x%x)\n", INTOHL(bootp->bp_siaddr)); 469 return (-1); 470 } 471 472 static int 473 bootpc_call(so, ifp, nd, procp) 474 struct socket *so; 475 struct ifnet *ifp; 476 struct nfs_diskless *nd; 477 struct proc *procp; 478 { 479 static u_int32_t xid = ~0xFF; 480 struct bootp *bootp; /* request */ 481 struct mbuf *m, *nam; 482 struct sockaddr_in *sin; 483 int error; 484 u_char *haddr; 485 u_char hafmt, halen; 486 struct bootpcontext bpc; 487 488 /* 489 * Initialize to NULL anything that will hold an allocation, 490 * and free each at the end if not null. 491 */ 492 bpc.replybuf = NULL; 493 m = nam = NULL; 494 495 /* Record our H/W (Ethernet) address. */ 496 { struct sockaddr_dl *sdl = ifp->if_sadl; 497 switch (sdl->sdl_type) { 498 case IFT_ETHER: 499 case IFT_FDDI: 500 hafmt = HTYPE_ETHERNET; 501 break; 502 default: 503 printf("bootp: unsupported interface type %d\n", 504 sdl->sdl_type); 505 error = EINVAL; 506 goto out; 507 } 508 halen = sdl->sdl_alen; 509 haddr = (unsigned char *)LLADDR(sdl); 510 } 511 512 /* 513 * Skip the route table when sending on this socket. 514 * If this is not done, ip_output finds the loopback 515 * interface (why?) and then fails because broadcast 516 * is not supported on that interface... 517 */ 518 { int32_t *opt; 519 m = m_get(M_WAIT, MT_SOOPTS); 520 opt = mtod(m, int32_t *); 521 m->m_len = sizeof(*opt); 522 *opt = 1; 523 error = sosetopt(so, SOL_SOCKET, SO_DONTROUTE, m); 524 m = NULL; /* was consumed */ 525 } 526 if (error) { 527 DPRINT("SO_DONTROUTE"); 528 goto out; 529 } 530 531 /* Enable broadcast. */ 532 if ((error = nfs_boot_enbroadcast(so))) { 533 DPRINT("SO_BROADCAST"); 534 goto out; 535 } 536 537 /* Set the receive timeout for the socket. */ 538 if ((error = nfs_boot_setrecvtimo(so))) { 539 DPRINT("SO_RCVTIMEO"); 540 goto out; 541 } 542 543 /* 544 * Bind the local endpoint to a bootp client port. 545 */ 546 if ((error = nfs_boot_sobind_ipport(so, IPPORT_BOOTPC))) { 547 DPRINT("bind failed\n"); 548 goto out; 549 } 550 551 /* 552 * Setup socket address for the server. 553 */ 554 nam = m_get(M_WAIT, MT_SONAME); 555 sin = mtod(nam, struct sockaddr_in *); 556 sin->sin_len = nam->m_len = sizeof(*sin); 557 sin->sin_family = AF_INET; 558 sin->sin_addr.s_addr = INADDR_BROADCAST; 559 sin->sin_port = htons(IPPORT_BOOTPS); 560 561 /* 562 * Allocate buffer used for request 563 */ 564 m = m_gethdr(M_WAIT, MT_DATA); 565 MCLGET(m, M_WAIT); 566 bootp = mtod(m, struct bootp*); 567 m->m_pkthdr.len = m->m_len = BOOTP_SIZE_MAX; 568 m->m_pkthdr.rcvif = NULL; 569 570 /* 571 * Build the BOOTP reqest message. 572 * Note: xid is host order! (opaque to server) 573 */ 574 memset((caddr_t)bootp, 0, BOOTP_SIZE_MAX); 575 bootp->bp_op = BOOTREQUEST; 576 bootp->bp_htype = hafmt; 577 bootp->bp_hlen = halen; /* Hardware address length */ 578 bootp->bp_xid = ++xid; 579 memcpy(bootp->bp_chaddr, haddr, halen); 580 /* Fill-in the vendor data. */ 581 memcpy(bootp->bp_vend, vm_rfc1048, 4); 582 #ifdef NFS_BOOT_DHCP 583 bootp->bp_vend[4] = TAG_DHCP_MSGTYPE; 584 bootp->bp_vend[5] = 1; 585 bootp->bp_vend[6] = DHCPDISCOVER; 586 bootp->bp_vend[7] = TAG_END; 587 #else 588 bootp->bp_vend[4] = TAG_END; 589 #endif 590 591 bpc.xid = xid; 592 bpc.haddr = haddr; 593 bpc.halen = halen; 594 bpc.replybuf = malloc(BOOTP_SIZE_MAX, M_DEVBUF, M_WAITOK); 595 if (bpc.replybuf == NULL) 596 panic("nfs_boot: malloc reply buf"); 597 #ifdef NFS_BOOT_DHCP 598 bpc.expected_dhcpmsgtype = DHCPOFFER; 599 bpc.dhcp_ok = 0; 600 #endif 601 602 error = nfs_boot_sendrecv(so, nam, bootpset, m, 603 bootpcheck, 0, 0, &bpc); 604 if (error) 605 goto out; 606 607 #ifdef NFS_BOOT_DHCP 608 if (bpc.dhcp_ok) { 609 u_int32_t leasetime; 610 bootp->bp_vend[6] = DHCPREQUEST; 611 bootp->bp_vend[7] = TAG_REQ_ADDR; 612 bootp->bp_vend[8] = 4; 613 memcpy(&bootp->bp_vend[9], &bpc.replybuf->bp_yiaddr, 4); 614 bootp->bp_vend[13] = TAG_SERVERID; 615 bootp->bp_vend[14] = 4; 616 memcpy(&bootp->bp_vend[15], &bpc.dhcp_serverip.s_addr, 4); 617 bootp->bp_vend[19] = TAG_LEASETIME; 618 bootp->bp_vend[20] = 4; 619 leasetime = htonl(300); 620 memcpy(&bootp->bp_vend[21], &leasetime, 4); 621 bootp->bp_vend[25] = TAG_END; 622 623 bpc.expected_dhcpmsgtype = DHCPACK; 624 625 error = nfs_boot_sendrecv(so, nam, bootpset, m, 626 bootpcheck, 0, 0, &bpc); 627 if (error) 628 goto out; 629 } 630 #endif 631 632 /* 633 * bootpcheck() has copied the receive mbuf into 634 * the buffer at bpc.replybuf. 635 */ 636 #ifdef NFS_BOOT_DHCP 637 printf("nfs_boot: %s server: 0x%x\n", 638 (bpc.dhcp_ok ? "DHCP" : "BOOTP"), 639 #else 640 printf("nfs_boot: BOOTP server: 0x%x\n", 641 #endif 642 INTOHL(bpc.replybuf->bp_siaddr)); 643 644 bootp_extract(bpc.replybuf, bpc.replylen, nd); 645 646 out: 647 if (bpc.replybuf) 648 free(bpc.replybuf, M_DEVBUF); 649 if (m) 650 m_freem(m); 651 if (nam) 652 m_freem(nam); 653 return (error); 654 } 655 656 static void 657 bootp_extract(bootp, replylen, nd) 658 struct bootp *bootp; 659 int replylen; 660 struct nfs_diskless *nd; 661 { 662 struct sockaddr_in *sin; 663 struct in_addr netmask; 664 struct in_addr gateway; 665 struct in_addr rootserver; 666 char *myname; /* my hostname */ 667 char *mydomain; /* my domainname */ 668 char *rootpath; 669 int mynamelen; 670 int mydomainlen; 671 int rootpathlen; 672 u_int tag, len; 673 u_char *p, *limit; 674 675 /* Default these to "unspecified". */ 676 netmask.s_addr = 0; 677 gateway.s_addr = 0; 678 mydomain = myname = rootpath = NULL; 679 mydomainlen = mynamelen = rootpathlen = 0; 680 /* default root server to bootp next-server */ 681 rootserver = bootp->bp_siaddr; 682 683 p = &bootp->bp_vend[4]; 684 limit = ((char*)bootp) + replylen; 685 while (p < limit) { 686 tag = *p++; 687 if (tag == TAG_END) 688 break; 689 if (tag == TAG_PAD) 690 continue; 691 len = *p++; 692 #if 0 /* already done in bootpcheck() */ 693 if ((p + len) > limit) { 694 printf("nfs_boot: option %d too long\n", tag); 695 break; 696 } 697 #endif 698 switch (tag) { 699 case TAG_SUBNET_MASK: 700 memcpy(&netmask, p, 4); 701 break; 702 case TAG_GATEWAY: 703 /* Routers */ 704 memcpy(&gateway, p, 4); 705 break; 706 case TAG_HOST_NAME: 707 if (len >= sizeof(hostname)) { 708 printf("nfs_boot: host name >=%d bytes", 709 sizeof(hostname)); 710 break; 711 } 712 myname = p; 713 mynamelen = len; 714 break; 715 case TAG_DOMAIN_NAME: 716 if (len >= sizeof(domainname)) { 717 printf("nfs_boot: domain name >=%d bytes", 718 sizeof(domainname)); 719 break; 720 } 721 mydomain = p; 722 mydomainlen = len; 723 break; 724 case TAG_ROOT_PATH: 725 /* Leave some room for the server name. */ 726 if (len >= (MNAMELEN-10)) { 727 printf("nfs_boot: rootpath >=%d bytes", 728 (MNAMELEN-10)); 729 break; 730 } 731 rootpath = p; 732 rootpathlen = len; 733 break; 734 case TAG_SWAP_SERVER: 735 /* override NFS server address */ 736 memcpy(&rootserver, p, 4); 737 break; 738 default: 739 break; 740 } 741 p += len; 742 } 743 744 /* 745 * Store and print network config info. 746 */ 747 if (myname) { 748 myname[mynamelen] = '\0'; 749 strncpy(hostname, myname, sizeof(hostname)); 750 hostnamelen = mynamelen; 751 printf("nfs_boot: my_name=%s\n", hostname); 752 } 753 if (mydomain) { 754 mydomain[mydomainlen] = '\0'; 755 strncpy(domainname, mydomain, sizeof(domainname)); 756 domainnamelen = mydomainlen; 757 printf("nfs_boot: my_domain=%s\n", domainname); 758 } 759 nd->nd_myip = bootp->bp_yiaddr; 760 if (nd->nd_myip.s_addr) 761 printf("nfs_boot: my_addr=0x%x\n", INTOHL(nd->nd_myip)); 762 nd->nd_mask = netmask; 763 if (nd->nd_mask.s_addr) 764 printf("nfs_boot: my_mask=0x%x\n", INTOHL(nd->nd_mask)); 765 nd->nd_gwip = gateway; 766 if (nd->nd_gwip.s_addr) 767 printf("nfs_boot: gateway=0x%x\n", INTOHL(nd->nd_gwip)); 768 769 /* 770 * Store the information about our NFS root mount. 771 * The caller will print it, so be silent here. 772 */ 773 { 774 struct nfs_dlmount *ndm = &nd->nd_root; 775 776 /* Server IP address. */ 777 sin = (struct sockaddr_in *) &ndm->ndm_saddr; 778 memset((caddr_t)sin, 0, sizeof(*sin)); 779 sin->sin_len = sizeof(*sin); 780 sin->sin_family = AF_INET; 781 sin->sin_addr = rootserver; 782 /* Server name. */ 783 if (!memcmp(&rootserver, &bootp->bp_siaddr, 784 sizeof(struct in_addr))) { 785 /* standard root server, we have the name */ 786 strncpy(ndm->ndm_host, bootp->bp_sname, BP_SNAME_LEN-1); 787 } else { 788 /* Show the server IP address numerically. */ 789 sprintf(ndm->ndm_host, "0x%8x", 790 INTOHL(rootserver)); 791 } 792 len = strlen(ndm->ndm_host); 793 if (rootpath && 794 len + 1 + rootpathlen + 1 <= sizeof(ndm->ndm_host)) { 795 ndm->ndm_host[len++] = ':'; 796 strncpy(ndm->ndm_host + len, 797 rootpath, rootpathlen); 798 ndm->ndm_host[len + rootpathlen] = '\0'; 799 } /* else: upper layer will handle error */ 800 } 801 } 802