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