nfs_bootdhcp.c revision 1.43
138032Speter/* $NetBSD: nfs_bootdhcp.c,v 1.43 2008/10/24 17:17:12 cegger Exp $ */ 2261363Sgshapiro 364565Sgshapiro/*- 438032Speter * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc. 538032Speter * All rights reserved. 638032Speter * 738032Speter * This code is derived from software contributed to The NetBSD Foundation 838032Speter * by Adam Glass and Gordon W. Ross. 938032Speter * 1038032Speter * Redistribution and use in source and binary forms, with or without 11102533Sgshapiro * modification, are permitted provided that the following conditions 12102533Sgshapiro * are met: 1338032Speter * 1. Redistributions of source code must retain the above copyright 1438032Speter * notice, this list of conditions and the following disclaimer. 1590795Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1690795Sgshapiro * notice, this list of conditions and the following disclaimer in the 1790795Sgshapiro * documentation and/or other materials provided with the distribution. 18261363Sgshapiro * 1964565Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2064565Sgshapiro * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2190795Sgshapiro * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2238032Speter * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23266692Sgshapiro * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2438032Speter * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2538032Speter * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2638032Speter * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2738032Speter * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2838032Speter * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2938032Speter * POSSIBILITY OF SUCH DAMAGE. 3064565Sgshapiro */ 3138032Speter 3238032Speter/* 3338032Speter * Support for NFS diskless booting with BOOTP (RFC951, RFC1048) 3438032Speter * 3538032Speter * History: 3638032Speter * 3738032Speter * Tor Egge developed the initial version of this code based on 3838032Speter * the Sun RPC/bootparam sources nfs_boot.c and krpc_subr.c and 3938032Speter * submitted that work to NetBSD as bugreport "kern/2351" on 4038032Speter * 29 Apr 1996. 4138032Speter * 4238032Speter * Gordon Ross reorganized Tor's version into this form and 4338032Speter * integrated it into the NetBSD sources during Aug 1997. 4438032Speter */ 4538032Speter 4638032Speter#include <sys/cdefs.h> 4738032Speter__KERNEL_RCSID(0, "$NetBSD: nfs_bootdhcp.c,v 1.43 2008/10/24 17:17:12 cegger Exp $"); 4838032Speter 4964565Sgshapiro#include "opt_nfs_boot.h" 5064565Sgshapiro#include "opt_tftproot.h" 5138032Speter 5238032Speter#include <sys/param.h> 5338032Speter#include <sys/systm.h> 5438032Speter#include <sys/kernel.h> 5538032Speter#include <sys/device.h> 5638032Speter#include <sys/ioctl.h> 5790795Sgshapiro#include <sys/proc.h> 5838032Speter#include <sys/mount.h> 5990795Sgshapiro#include <sys/mbuf.h> 6038032Speter#include <sys/reboot.h> 6138032Speter#include <sys/socket.h> 6238032Speter#include <sys/socketvar.h> 6364565Sgshapiro 6438032Speter#include <net/if.h> 6538032Speter#include <net/if_types.h> 6690795Sgshapiro#include <net/if_arp.h> /* ARPHRD_ETHER, etc. */ 6790795Sgshapiro#include <net/if_dl.h> 6890795Sgshapiro#include <net/if_ether.h> 6938032Speter#include <net/route.h> 7064565Sgshapiro 7164565Sgshapiro#include <netinet/in.h> 7264565Sgshapiro#include <netinet/if_inarp.h> 7364565Sgshapiro 7438032Speter#include <nfs/rpcv2.h> 7538032Speter 7664565Sgshapiro#include <nfs/nfsproto.h> 7738032Speter#include <nfs/nfs.h> 7838032Speter#include <nfs/nfsmount.h> 7938032Speter#include <nfs/nfsdiskless.h> 8038032Speter 8138032Speter/* 8238032Speter * There are two implementations of NFS diskless boot. 8338032Speter * This implementation uses BOOTP (RFC951, RFC1048), and 8438032Speter * the other uses Sun RPC/bootparams (nfs_bootparam.c). 8538032Speter * 8690795Sgshapiro * This method gets everything it needs with one BOOTP 8738032Speter * request and reply. Note that this actually uses only 8838032Speter * the old BOOTP functionality subset of DHCP. It is not 8938032Speter * clear that DHCP provides any advantage over BOOTP for 9038032Speter * diskless boot. DHCP allows the server to assign an IP 9138032Speter * address without any a-priori knowledge of the client, 9238032Speter * but we require that the server has a-priori knowledge 9338032Speter * of the client so it can export our (unique) NFS root. 9438032Speter * Given that the server needs a-priori knowledge about 9538032Speter * the client anyway, it might as well assign a fixed IP 9638032Speter * address for the client and support BOOTP. 9764565Sgshapiro * 9842580Speter * On the other hand, disk-FULL clients may use DHCP, but 9938032Speter * in that case the DHCP client should be user-mode code, 10090795Sgshapiro * and has no bearing on the code below. -gwr 10142580Speter */ 10238032Speter 10366497Sgshapiro/* Begin stuff from bootp.h */ 10464565Sgshapiro/* Definitions from RFC951 */ 10564565Sgshapiro#define BP_CHADDR_LEN 16 10664565Sgshapiro#define BP_SNAME_LEN 64 10738032Speter#define BP_FILE_LEN 128 10838032Speter#define BP_VEND_LEN 64 10938032Speterstruct bootp { 11038076Speter u_int8_t bp_op; /* packet opcode type */ 11164565Sgshapiro u_int8_t bp_htype; /* hardware addr type */ 11264565Sgshapiro u_int8_t bp_hlen; /* hardware addr length */ 11364565Sgshapiro u_int8_t bp_hops; /* gateway hops */ 11464565Sgshapiro u_int32_t bp_xid; /* transaction ID */ 11538032Speter u_int16_t bp_secs; /* seconds since boot began */ 11638032Speter u_int16_t bp_flags; /* RFC1532 broadcast, etc. */ 11764565Sgshapiro struct in_addr bp_ciaddr; /* client IP address */ 11864565Sgshapiro struct in_addr bp_yiaddr; /* 'your' IP address */ 11938032Speter struct in_addr bp_siaddr; /* server IP address */ 12038032Speter struct in_addr bp_giaddr; /* gateway IP address */ 12164565Sgshapiro u_int8_t bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ 12264565Sgshapiro char bp_sname[BP_SNAME_LEN]; /* server host name */ 12364565Sgshapiro char bp_file[BP_FILE_LEN]; /* boot file name */ 12438032Speter u_int8_t bp_vend[BP_VEND_LEN]; /* RFC1048 options */ 12538032Speter /* 12664565Sgshapiro * Note that BOOTP packets are allowed to be longer 12764565Sgshapiro * (see RFC 1532 sect. 2.1) and common practice is to 12838032Speter * allow the option data in bp_vend to extend into the 12938032Speter * additional space provided in longer packets. 13038032Speter */ 13138032Speter}; 13238032Speter 13338032Speter#define IPPORT_BOOTPS 67 13438032Speter#define IPPORT_BOOTPC 68 13564565Sgshapiro 13664565Sgshapiro#define BOOTREQUEST 1 13738032Speter#define BOOTREPLY 2 13890795Sgshapiro 139249729Sgshapiro/* 14090795Sgshapiro * Is this available from the sockaddr_dl somehow? 14138032Speter * Perhaps (struct arphdr)->ar_hrd = ARPHRD_ETHER? 14238032Speter * The interface has ->if_type but not the ARP fmt. 14338032Speter */ 14438032Speter#define HTYPE_ETHERNET 1 14538032Speter#define HTYPE_IEEE802 6 14638032Speter 14738032Speter/* 14838032Speter * Vendor magic cookie (v_magic) for RFC1048 14938032Speter */ 15038032Speterstatic const u_int8_t vm_rfc1048[4] = { 99, 130, 83, 99 }; 15138032Speter 15238032Speter/* 15364565Sgshapiro * Tag values used to specify what information is being supplied in 15464565Sgshapiro * the vendor (options) data area of the packet. 15538032Speter */ 15638032Speter/* RFC 1048 */ 15738032Speter#define TAG_END ((unsigned char) 255) 15838032Speter#define TAG_PAD ((unsigned char) 0) 15964565Sgshapiro#define TAG_SUBNET_MASK ((unsigned char) 1) 16038032Speter#define TAG_TIME_OFFSET ((unsigned char) 2) 16138032Speter#define TAG_GATEWAY ((unsigned char) 3) 16238032Speter#define TAG_TIME_SERVER ((unsigned char) 4) 16364565Sgshapiro#define TAG_NAME_SERVER ((unsigned char) 5) 16464565Sgshapiro#define TAG_DOMAIN_SERVER ((unsigned char) 6) 16564565Sgshapiro#define TAG_LOG_SERVER ((unsigned char) 7) 16664565Sgshapiro#define TAG_COOKIE_SERVER ((unsigned char) 8) 16764565Sgshapiro#define TAG_LPR_SERVER ((unsigned char) 9) 16864565Sgshapiro#define TAG_IMPRESS_SERVER ((unsigned char) 10) 16964565Sgshapiro#define TAG_RLP_SERVER ((unsigned char) 11) 17064565Sgshapiro#define TAG_HOST_NAME ((unsigned char) 12) 17164565Sgshapiro#define TAG_BOOT_SIZE ((unsigned char) 13) 17238032Speter/* RFC 1395 */ 17338032Speter#define TAG_DUMP_FILE ((unsigned char) 14) 17490795Sgshapiro#define TAG_DOMAIN_NAME ((unsigned char) 15) 17590795Sgshapiro#define TAG_SWAP_SERVER ((unsigned char) 16) 17690795Sgshapiro#define TAG_ROOT_PATH ((unsigned char) 17) 17790795Sgshapiro/* End of stuff from bootp.h */ 17838032Speter 17938032Speter#ifdef NFS_BOOT_DHCP 18064565Sgshapiro#define TAG_REQ_ADDR ((unsigned char) 50) 18138032Speter#define TAG_LEASETIME ((unsigned char) 51) 18238032Speter#define TAG_OVERLOAD ((unsigned char) 52) 18364565Sgshapiro#define TAG_DHCP_MSGTYPE ((unsigned char) 53) 18464565Sgshapiro#define TAG_SERVERID ((unsigned char) 54) 18538032Speter#define TAG_PARAM_REQ ((unsigned char) 55) 18664565Sgshapiro#define TAG_MSG ((unsigned char) 56) 18764565Sgshapiro#define TAG_MAXSIZE ((unsigned char) 57) 18864565Sgshapiro#define TAG_T1 ((unsigned char) 58) 18964565Sgshapiro#define TAG_T2 ((unsigned char) 59) 19038032Speter#define TAG_CLASSID ((unsigned char) 60) 19138032Speter#define TAG_CLIENTID ((unsigned char) 61) 19238032Speter#endif 19338032Speter 19438032Speter#ifdef NFS_BOOT_DHCP 19538032Speter#define DHCPDISCOVER 1 19638032Speter#define DHCPOFFER 2 19790795Sgshapiro#define DHCPREQUEST 3 19890795Sgshapiro#define DHCPDECLINE 4 19990795Sgshapiro#define DHCPACK 5 20038032Speter#define DHCPNAK 6 20138032Speter#define DHCPRELEASE 7 20264565Sgshapiro#endif 20338032Speter 20464565Sgshapiro#ifdef NFS_BOOT_DHCP 20564565Sgshapiro#define BOOTP_SIZE_MAX (sizeof(struct bootp)+312-64) 20638032Speter#else 20764565Sgshapiro/* 20864565Sgshapiro * The "extended" size is somewhat arbitrary, but is 20964565Sgshapiro * constrained by the maximum message size specified 21064565Sgshapiro * by RFC1533 (567 total). This value increases the 21164565Sgshapiro * space for options from 64 bytes to 256 bytes. 21238032Speter */ 21338032Speter#define BOOTP_SIZE_MAX (sizeof(struct bootp)+256-64) 21438032Speter#endif 21564565Sgshapiro#define BOOTP_SIZE_MIN (sizeof(struct bootp)) 21664565Sgshapiro 21738032Speter/* Convenience macro */ 21838032Speter#define INTOHL(ina) ((u_int32_t)ntohl((ina).s_addr)) 21990795Sgshapiro 22090795Sgshapirostatic int bootpc_call (struct nfs_diskless *, struct lwp *); 22190795Sgshapirostatic void bootp_extract (struct bootp *, int, struct nfs_diskless *); 22290795Sgshapiro 22338032Speter#ifdef DEBUG_NFS_BOOT_DHCP 22438032Speter#define DPRINTF(s) printf s 22538032Speter#else 22638032Speter#define DPRINTF(s) 22764565Sgshapiro#endif 22864565Sgshapiro 22938032Speter 23038032Speter/* 23190795Sgshapiro * Get our boot parameters using BOOTP. 23238032Speter */ 23364565Sgshapiroint 23464565Sgshapironfs_bootdhcp(struct nfs_diskless *nd, struct lwp *lwp) 23590795Sgshapiro{ 23664565Sgshapiro struct ifnet *ifp = nd->nd_ifp; 23764565Sgshapiro int error; 23890795Sgshapiro 23938032Speter /* 24038032Speter * Do enough of ifconfig(8) so that the chosen interface 24138032Speter * can talk to the servers. Use address zero for now. 24238032Speter */ 24338032Speter error = nfs_boot_setaddress(ifp, lwp, INADDR_ANY, INADDR_ANY, 24438032Speter INADDR_BROADCAST); 24538032Speter if (error) { 24638032Speter printf("nfs_boot: set ifaddr zero, error=%d\n", error); 24764565Sgshapiro return (error); 24864565Sgshapiro } 24964565Sgshapiro 25064565Sgshapiro /* This function call does the real send/recv work. */ 25164565Sgshapiro error = bootpc_call(nd, lwp); 25238032Speter 25338032Speter /* Get rid of the temporary (zero) IP address. */ 25438032Speter (void) nfs_boot_deladdress(ifp, lwp, INADDR_ANY); 25538032Speter 25638032Speter /* NOW we can test the error from bootpc_call. */ 25738032Speter if (error) 25838032Speter goto out; 25964565Sgshapiro 26064565Sgshapiro /* 26138032Speter * Do ifconfig with our real IP address and mask. 26290795Sgshapiro */ 26390795Sgshapiro error = nfs_boot_setaddress(ifp, lwp, nd->nd_myip.s_addr, 26490795Sgshapiro nd->nd_mask.s_addr, INADDR_ANY); 26590795Sgshapiro if (error) { 26690795Sgshapiro printf("nfs_boot: set ifaddr real, error=%d\n", error); 26738032Speter goto out; 26838032Speter } 26938032Speter 27090795Sgshapiroout: 27138032Speter if (error) { 27238032Speter (void) nfs_boot_ifupdown(ifp, lwp, 0); 27366497Sgshapiro nfs_boot_flushrt(ifp); 27490795Sgshapiro } 27566497Sgshapiro return (error); 27666497Sgshapiro} 27738032Speter 27838032Speterstruct bootpcontext { 27990795Sgshapiro int xid; 28038032Speter const u_char *haddr; 28138080Speter u_char halen; 28238032Speter struct bootp *replybuf; 28338080Speter int replylen; 28438080Speter#ifdef NFS_BOOT_DHCP 28538080Speter char expected_dhcpmsgtype, dhcp_ok; 28638032Speter struct in_addr dhcp_serverip; 28738032Speter#endif 28838032Speter}; 28938032Speter 29090795Sgshapirostatic int bootpset (struct mbuf*, void*, int); 29138032Speterstatic int bootpcheck (struct mbuf*, void*); 29290795Sgshapiro 29338032Speterstatic int 29438032Speterbootpset(struct mbuf *m, void *context, int waited) 29590795Sgshapiro{ 29638032Speter struct bootp *bootp; 29738032Speter 29864565Sgshapiro /* we know it's contigous (in 1 mbuf cluster) */ 29990795Sgshapiro bootp = mtod(m, struct bootp*); 30064565Sgshapiro 30138032Speter bootp->bp_secs = htons(waited); 30238032Speter 30338032Speter return (0); 30464565Sgshapiro} 30564565Sgshapiro 30664565Sgshapirostatic int 30764565Sgshapirobootpcheck(struct mbuf *m, void *context) 30864565Sgshapiro{ 30964565Sgshapiro struct bootp *bootp; 31066497Sgshapiro struct bootpcontext *bpc = context; 31164565Sgshapiro u_int tag, len; 31238032Speter u_char *p, *limit; 31338032Speter 31464565Sgshapiro /* 31538032Speter * Is this a valid reply? 31638032Speter */ 31764565Sgshapiro if (m->m_pkthdr.len < BOOTP_SIZE_MIN) { 31864565Sgshapiro DPRINTF(("bootpcheck: short packet %d < %d\n", m->m_pkthdr.len, 31964565Sgshapiro BOOTP_SIZE_MIN)); 32064565Sgshapiro return (-1); 32138032Speter } 32290795Sgshapiro if (m->m_pkthdr.len > BOOTP_SIZE_MAX) { 32338032Speter DPRINTF(("Bootpcheck: long packet %d > %d\n", m->m_pkthdr.len, 32438032Speter BOOTP_SIZE_MAX)); 32566497Sgshapiro return (-1); 32666497Sgshapiro } 32766497Sgshapiro 32866497Sgshapiro /* 32966497Sgshapiro * don't make first checks more expensive than necessary 33064565Sgshapiro */ 33166497Sgshapiro if (m->m_len < offsetof(struct bootp, bp_sname)) { 33238032Speter m = m_pullup(m, offsetof(struct bootp, bp_sname)); 33364565Sgshapiro if (m == NULL) { 33464565Sgshapiro DPRINTF(("bootpcheck: m_pullup failed\n")); 33590795Sgshapiro return (-1); 33690795Sgshapiro } 33766497Sgshapiro } 33890795Sgshapiro bootp = mtod(m, struct bootp*); 33990795Sgshapiro 34038032Speter if (bootp->bp_op != BOOTREPLY) { 34138032Speter DPRINTF(("bootpcheck: op %d is not reply\n", bootp->bp_op)); 34238032Speter return (-1); 34364565Sgshapiro } 34464565Sgshapiro if (bootp->bp_hlen != bpc->halen) { 34564565Sgshapiro DPRINTF(("bootpcheck: hlen %d != %d\n", bootp->bp_hlen, 34664565Sgshapiro bpc->halen)); 34764565Sgshapiro return (-1); 34864565Sgshapiro } 34964565Sgshapiro if (memcmp(bootp->bp_chaddr, bpc->haddr, bpc->halen)) { 35064565Sgshapiro#ifdef DEBUG_NFS_BOOT_DHCP 35138032Speter char *bp_chaddr, *haddr; 35238032Speter 35364565Sgshapiro bp_chaddr = malloc(3 * bpc->halen, M_TEMP, M_WAITOK); 35438032Speter haddr = malloc(3 * bpc->halen, M_TEMP, M_WAITOK); 35538032Speter 35638032Speter DPRINTF(("bootpcheck: incorrect hwaddr %s != %s\n", 35738032Speter ether_snprintf(bp_chaddr, 3 * bpc->halen, 35890795Sgshapiro bootp->bp_chaddr), 35938032Speter ether_snprintf(haddr, 3 * bpc->halen, bpc->haddr))); 36090795Sgshapiro 36164565Sgshapiro free(bp_chaddr, M_TEMP); 36264565Sgshapiro free(haddr, M_TEMP); 36390795Sgshapiro#endif 36464565Sgshapiro return (-1); 36564565Sgshapiro } 36664565Sgshapiro if (bootp->bp_xid != bpc->xid) { 36764565Sgshapiro DPRINTF(("bootpcheck: xid %d != %d\n", bootp->bp_xid, 36864565Sgshapiro bpc->xid)); 36964565Sgshapiro return (-1); 37064565Sgshapiro } 37138032Speter 37264565Sgshapiro /* 37364565Sgshapiro * OK, it's worth to look deeper. 37490795Sgshapiro * We copy the mbuf into a flat buffer here because 37538032Speter * m_pullup() is a bit limited for this purpose 37638032Speter * (doesn't allocate a cluster if necessary). 37738032Speter */ 37890795Sgshapiro bpc->replylen = m->m_pkthdr.len; 37990795Sgshapiro m_copydata(m, 0, bpc->replylen, (void *)bpc->replybuf); 38090795Sgshapiro bootp = bpc->replybuf; 38164565Sgshapiro 38238032Speter /* 38338032Speter * Check if the IP address we get looks correct. 38464565Sgshapiro * (DHCP servers can send junk to unknown clients.) 38564565Sgshapiro * XXX more checks might be needed 38690795Sgshapiro */ 38790795Sgshapiro if (bootp->bp_yiaddr.s_addr == INADDR_ANY || 388249729Sgshapiro bootp->bp_yiaddr.s_addr == INADDR_BROADCAST) { 38938032Speter printf("nfs_boot: wrong IP addr %s", 39090795Sgshapiro inet_ntoa(bootp->bp_yiaddr)); 39190795Sgshapiro goto warn; 39238032Speter } 39390795Sgshapiro 39490795Sgshapiro /* 39538032Speter * Check the vendor data. 39638032Speter */ 39738032Speter if (memcmp(bootp->bp_vend, vm_rfc1048, 4)) { 39838032Speter printf("nfs_boot: reply missing options"); 39938032Speter goto warn; 40064565Sgshapiro } 40138032Speter p = &bootp->bp_vend[4]; 40238032Speter limit = ((u_char*)bootp) + bpc->replylen; 40338032Speter while (p < limit) { 40438032Speter tag = *p++; 40538032Speter if (tag == TAG_END) 40664565Sgshapiro break; 40764565Sgshapiro if (tag == TAG_PAD) 40838032Speter continue; 40938032Speter len = *p++; 41064565Sgshapiro if ((p + len) > limit) { 41138032Speter printf("nfs_boot: option %d too long", tag); 41238032Speter goto warn; 41390795Sgshapiro } 41490795Sgshapiro switch (tag) { 41538032Speter#ifdef NFS_BOOT_DHCP 41638032Speter case TAG_DHCP_MSGTYPE: 41738032Speter if (*p != bpc->expected_dhcpmsgtype) 41864565Sgshapiro return (-1); 41938032Speter bpc->dhcp_ok = 1; 42038032Speter break; 42164565Sgshapiro case TAG_SERVERID: 42238032Speter memcpy(&bpc->dhcp_serverip.s_addr, p, 42338032Speter sizeof(bpc->dhcp_serverip.s_addr)); 42438032Speter break; 42538032Speter#endif 42664565Sgshapiro default: 42738032Speter break; 42890795Sgshapiro } 42990795Sgshapiro p += len; 43090795Sgshapiro } 43190795Sgshapiro return (0); 43290795Sgshapiro 43390795Sgshapirowarn: 43490795Sgshapiro printf(" (bad reply from %s)\n", inet_ntoa(bootp->bp_siaddr)); 43590795Sgshapiro return (-1); 43690795Sgshapiro} 43790795Sgshapiro 43838032Speterstatic int 43938032Speterbootpc_call(struct nfs_diskless *nd, struct lwp *lwp) 440{ 441 struct socket *so; 442 struct ifnet *ifp = nd->nd_ifp; 443 static u_int32_t xid = ~0xFF; 444 struct bootp *bootp; /* request */ 445 struct mbuf *m, *nam; 446 struct sockaddr_in *sin; 447 int error; 448 const u_char *haddr; 449 u_char hafmt, halen; 450 struct bootpcontext bpc; 451#ifdef NFS_BOOT_DHCP 452 char vci[64]; 453 int vcilen; 454#endif 455 456 error = socreate(AF_INET, &so, SOCK_DGRAM, 0, lwp, NULL); 457 if (error) { 458 printf("bootp: socreate, error=%d\n", error); 459 return (error); 460 } 461 462 /* 463 * Initialize to NULL anything that will hold an allocation, 464 * and free each at the end if not null. 465 */ 466 bpc.replybuf = NULL; 467 m = nam = NULL; 468 469 /* Record our H/W (Ethernet) address. */ 470 { const struct sockaddr_dl *sdl = ifp->if_sadl; 471 switch (sdl->sdl_type) { 472 case IFT_ISO88025: 473 hafmt = HTYPE_IEEE802; 474 break; 475 case IFT_ETHER: 476 case IFT_FDDI: 477 hafmt = HTYPE_ETHERNET; 478 break; 479 default: 480 printf("bootp: unsupported interface type %d\n", 481 sdl->sdl_type); 482 error = EINVAL; 483 goto out; 484 } 485 halen = sdl->sdl_alen; 486 haddr = (const unsigned char *)CLLADDR(sdl); 487 } 488 489 /* 490 * Skip the route table when sending on this socket. 491 * If this is not done, ip_output finds the loopback 492 * interface (why?) and then fails because broadcast 493 * is not supported on that interface... 494 */ 495 { int32_t opt; 496 497 opt = 1; 498 error = so_setsockopt(NULL, so, SOL_SOCKET, SO_DONTROUTE, &opt, 499 sizeof(opt)); 500 } 501 if (error) { 502 DPRINTF(("bootpc_call: SO_DONTROUTE failed %d\n", error)); 503 goto out; 504 } 505 506 /* Enable broadcast. */ 507 if ((error = nfs_boot_enbroadcast(so))) { 508 DPRINTF(("bootpc_call: SO_BROADCAST failed %d\n", error)); 509 goto out; 510 } 511 512 /* 513 * Set some TTL so we can boot through routers. 514 * Real BOOTP forwarding agents don't need this; they obey "bp_hops" 515 * and set "bp_giaddr", thus rewrite the packet anyway. 516 * The "helper-address" feature of some popular router vendor seems 517 * to do simple IP forwarding and drops packets with (ip_ttl == 1). 518 */ 519 { u_char opt; 520 521 opt = 7; 522 error = so_setsockopt(NULL, so, IPPROTO_IP, IP_MULTICAST_TTL, 523 &opt, sizeof(opt)); 524 } 525 if (error) { 526 DPRINTF(("bootpc_call: IP_MULTICAST_TTL failed %d\n", error)); 527 goto out; 528 } 529 530 /* Set the receive timeout for the socket. */ 531 if ((error = nfs_boot_setrecvtimo(so))) { 532 DPRINTF(("bootpc_call: SO_RCVTIMEO failed %d\n", error)); 533 goto out; 534 } 535 536 /* 537 * Bind the local endpoint to a bootp client port. 538 */ 539 if ((error = nfs_boot_sobind_ipport(so, IPPORT_BOOTPC, lwp))) { 540 DPRINTF(("bootpc_call: bind failed %d\n", error)); 541 goto out; 542 } 543 544 /* 545 * Setup socket address for the server. 546 */ 547 nam = m_get(M_WAIT, MT_SONAME); 548 sin = mtod(nam, struct sockaddr_in *); 549 sin->sin_len = nam->m_len = sizeof(*sin); 550 sin->sin_family = AF_INET; 551 sin->sin_addr.s_addr = INADDR_BROADCAST; 552 sin->sin_port = htons(IPPORT_BOOTPS); 553 554 /* 555 * Allocate buffer used for request 556 */ 557 m = m_gethdr(M_WAIT, MT_DATA); 558 m_clget(m, M_WAIT); 559 bootp = mtod(m, struct bootp*); 560 m->m_pkthdr.len = m->m_len = BOOTP_SIZE_MAX; 561 m->m_pkthdr.rcvif = NULL; 562 563 /* 564 * Build the BOOTP reqest message. 565 * Note: xid is host order! (opaque to server) 566 */ 567 memset((void *)bootp, 0, BOOTP_SIZE_MAX); 568 bootp->bp_op = BOOTREQUEST; 569 bootp->bp_htype = hafmt; 570 bootp->bp_hlen = halen; /* Hardware address length */ 571 bootp->bp_xid = ++xid; 572 memcpy(bootp->bp_chaddr, haddr, halen); 573#ifdef NFS_BOOT_BOOTP_REQFILE 574 strncpy(bootp->bp_file, NFS_BOOT_BOOTP_REQFILE, sizeof(bootp->bp_file)); 575#endif 576 /* Fill-in the vendor data. */ 577 memcpy(bootp->bp_vend, vm_rfc1048, 4); 578#ifdef NFS_BOOT_DHCP 579 bootp->bp_vend[4] = TAG_DHCP_MSGTYPE; 580 bootp->bp_vend[5] = 1; 581 bootp->bp_vend[6] = DHCPDISCOVER; 582 /* 583 * Insert a NetBSD Vendor Class Identifier option. 584 */ 585 snprintf(vci, sizeof(vci), "%s:%s:kernel:%s", ostype, MACHINE, 586 osrelease); 587 vcilen = strlen(vci); 588 bootp->bp_vend[7] = TAG_CLASSID; 589 bootp->bp_vend[8] = vcilen; 590 memcpy(&bootp->bp_vend[9], vci, vcilen); 591 bootp->bp_vend[9 + vcilen] = TAG_END; 592#else 593 bootp->bp_vend[4] = TAG_END; 594#endif 595 596 bpc.xid = xid; 597 bpc.haddr = haddr; 598 bpc.halen = halen; 599 bpc.replybuf = malloc(BOOTP_SIZE_MAX, M_DEVBUF, M_WAITOK); 600 if (bpc.replybuf == NULL) 601 panic("nfs_boot: malloc reply buf"); 602#ifdef NFS_BOOT_DHCP 603 bpc.expected_dhcpmsgtype = DHCPOFFER; 604 bpc.dhcp_ok = 0; 605#endif 606 607 error = nfs_boot_sendrecv(so, nam, bootpset, m, 608 bootpcheck, 0, 0, &bpc, lwp); 609 if (error) 610 goto out; 611 612#ifdef NFS_BOOT_DHCP 613 if (bpc.dhcp_ok) { 614 u_int32_t leasetime; 615 bootp->bp_vend[6] = DHCPREQUEST; 616 bootp->bp_vend[7] = TAG_REQ_ADDR; 617 bootp->bp_vend[8] = 4; 618 memcpy(&bootp->bp_vend[9], &bpc.replybuf->bp_yiaddr, 4); 619 bootp->bp_vend[13] = TAG_SERVERID; 620 bootp->bp_vend[14] = 4; 621 memcpy(&bootp->bp_vend[15], &bpc.dhcp_serverip.s_addr, 4); 622 bootp->bp_vend[19] = TAG_LEASETIME; 623 bootp->bp_vend[20] = 4; 624 leasetime = htonl(300); 625 memcpy(&bootp->bp_vend[21], &leasetime, 4); 626 bootp->bp_vend[25] = TAG_CLASSID; 627 bootp->bp_vend[26] = vcilen; 628 memcpy(&bootp->bp_vend[27], vci, vcilen); 629 bootp->bp_vend[27 + vcilen] = TAG_END; 630 631 bpc.expected_dhcpmsgtype = DHCPACK; 632 633 error = nfs_boot_sendrecv(so, nam, bootpset, m, 634 bootpcheck, 0, 0, &bpc, lwp); 635 if (error) 636 goto out; 637 } 638#endif 639 640 /* 641 * bootpcheck() has copied the receive mbuf into 642 * the buffer at bpc.replybuf. 643 */ 644#ifdef NFS_BOOT_DHCP 645 printf("nfs_boot: %s next-server: %s\n", 646 (bpc.dhcp_ok ? "DHCP" : "BOOTP"), 647#else 648 printf("nfs_boot: BOOTP next-server: %s\n", 649#endif 650 inet_ntoa(bpc.replybuf->bp_siaddr)); 651 652 bootp_extract(bpc.replybuf, bpc.replylen, nd); 653 654out: 655 if (bpc.replybuf) 656 free(bpc.replybuf, M_DEVBUF); 657 if (m) 658 m_freem(m); 659 if (nam) 660 m_freem(nam); 661 soclose(so); 662 return (error); 663} 664 665static void 666bootp_extract(struct bootp *bootp, int replylen, struct nfs_diskless *nd) 667{ 668 struct sockaddr_in *sin; 669 struct in_addr netmask; 670 struct in_addr gateway; 671 struct in_addr rootserver; 672 char *myname; /* my hostname */ 673 char *mydomain; /* my domainname */ 674 char *rootpath; 675 int mynamelen; 676 int mydomainlen; 677 int rootpathlen; 678 int overloaded; 679 u_int tag, len; 680 u_char *p, *limit; 681 682 /* Default these to "unspecified". */ 683 netmask.s_addr = 0; 684 gateway.s_addr = 0; 685 mydomain = myname = rootpath = NULL; 686 mydomainlen = mynamelen = rootpathlen = 0; 687 688 /* default root server to bootp next-server */ 689 rootserver = bootp->bp_siaddr; 690 /* assume that server name field is not overloaded by default */ 691 overloaded = 0; 692 693 p = &bootp->bp_vend[4]; 694 limit = ((u_char*)bootp) + replylen; 695 while (p < limit) { 696 tag = *p++; 697 if (tag == TAG_END) 698 break; 699 if (tag == TAG_PAD) 700 continue; 701 len = *p++; 702#if 0 /* already done in bootpcheck() */ 703 if ((p + len) > limit) { 704 printf("nfs_boot: option %d too long\n", tag); 705 break; 706 } 707#endif 708 switch (tag) { 709 case TAG_SUBNET_MASK: 710 memcpy(&netmask, p, 4); 711 break; 712 case TAG_GATEWAY: 713 /* Routers */ 714 memcpy(&gateway, p, 4); 715 break; 716 case TAG_HOST_NAME: 717 if (len >= sizeof(hostname)) { 718 printf("nfs_boot: host name >= %lu bytes", 719 (u_long)sizeof(hostname)); 720 break; 721 } 722 myname = p; 723 mynamelen = len; 724 break; 725 case TAG_DOMAIN_NAME: 726 if (len >= sizeof(domainname)) { 727 printf("nfs_boot: domain name >= %lu bytes", 728 (u_long)sizeof(domainname)); 729 break; 730 } 731 mydomain = p; 732 mydomainlen = len; 733 break; 734 case TAG_ROOT_PATH: 735 /* Leave some room for the server name. */ 736 if (len >= (MNAMELEN-10)) { 737 printf("nfs_boot: rootpath >=%d bytes", 738 (MNAMELEN-10)); 739 break; 740 } 741 rootpath = p; 742 rootpathlen = len; 743 break; 744 case TAG_SWAP_SERVER: 745 /* override NFS server address */ 746 memcpy(&rootserver, p, 4); 747 break; 748#ifdef NFS_BOOT_DHCP 749 case TAG_OVERLOAD: 750 if (len > 0 && ((*p & 0x02) != 0)) 751 /* 752 * The server name field in the dhcp packet 753 * is overloaded and we can't find server 754 * name there. 755 */ 756 overloaded = 1; 757 break; 758#endif 759 default: 760 break; 761 } 762 p += len; 763 } 764 765 /* 766 * Store and print network config info. 767 */ 768 if (myname) { 769 myname[mynamelen] = '\0'; 770 strncpy(hostname, myname, sizeof(hostname)); 771 hostnamelen = mynamelen; 772 printf("nfs_boot: my_name=%s\n", hostname); 773 } 774 if (mydomain) { 775 mydomain[mydomainlen] = '\0'; 776 strncpy(domainname, mydomain, sizeof(domainname)); 777 domainnamelen = mydomainlen; 778 printf("nfs_boot: my_domain=%s\n", domainname); 779 } 780 nd->nd_myip = bootp->bp_yiaddr; 781 if (nd->nd_myip.s_addr) 782 printf("nfs_boot: my_addr=%s\n", inet_ntoa(nd->nd_myip)); 783 nd->nd_mask = netmask; 784 if (nd->nd_mask.s_addr) 785 printf("nfs_boot: my_mask=%s\n", inet_ntoa(nd->nd_mask)); 786 nd->nd_gwip = gateway; 787 if (nd->nd_gwip.s_addr) 788 printf("nfs_boot: gateway=%s\n", inet_ntoa(nd->nd_gwip)); 789 790 /* 791 * Store the information about our NFS root mount. 792 * The caller will print it, so be silent here. 793 */ 794 { 795 struct nfs_dlmount *ndm = &nd->nd_root; 796 797 /* Server IP address. */ 798 sin = (struct sockaddr_in *) &ndm->ndm_saddr; 799 memset((void *)sin, 0, sizeof(*sin)); 800 sin->sin_len = sizeof(*sin); 801 sin->sin_family = AF_INET; 802 sin->sin_addr = rootserver; 803 /* Server name. */ 804 if (!overloaded && bootp->bp_sname[0] != 0 && 805 !memcmp(&rootserver, &bootp->bp_siaddr, 806 sizeof(struct in_addr))) { 807 /* standard root server, we have the name */ 808 strncpy(ndm->ndm_host, bootp->bp_sname, BP_SNAME_LEN-1); 809 } else { 810 /* Show the server IP address numerically. */ 811 strncpy(ndm->ndm_host, inet_ntoa(rootserver), 812 BP_SNAME_LEN-1); 813 } 814 len = strlen(ndm->ndm_host); 815 if (rootpath && 816 len + 1 + rootpathlen + 1 <= sizeof(ndm->ndm_host)) { 817 ndm->ndm_host[len++] = ':'; 818 strncpy(ndm->ndm_host + len, 819 rootpath, rootpathlen); 820 ndm->ndm_host[len + rootpathlen] = '\0'; 821 } /* else: upper layer will handle error */ 822 } 823 824#ifdef TFTPROOT 825#if BP_FILE_LEN > MNAMELEN 826#define BOOTFILELEN MNAMELEN 827#else 828#define BOOTFILELEN BP_FILE_LEN 829#endif 830 strncpy(nd->nd_bootfile, bootp->bp_file, BOOTFILELEN); 831 nd->nd_bootfile[BOOTFILELEN - 1] = '\0'; 832#undef BOOTFILELEN 833#endif /* TFTPROOT */ 834} 835