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