1 /* $NetBSD: bootp.c,v 1.21 2001/07/19 16:13:01 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1992 Regents of the University of California. 5 * All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Lawrence Berkeley Laboratory and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 * 39 * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL) 40 */ 41 42 #include <sys/types.h> 43 #include <netinet/in.h> 44 #include <netinet/in_systm.h> 45 46 #ifdef _STANDALONE 47 #include <lib/libkern/libkern.h> 48 #else 49 #include <string.h> 50 #endif 51 52 #include "stand.h" 53 #include "net.h" 54 #include "netif.h" 55 #include "bootp.h" 56 57 struct in_addr servip; 58 #ifdef SUPPORT_LINUX 59 char linuxcmdline[256]; 60 #ifndef TAG_LINUX_CMDLINE 61 #define TAG_LINUX_CMDLINE 123 62 #endif 63 #endif 64 65 static n_long nmask, smask; 66 67 static time_t bot; 68 69 static char vm_rfc1048[4] = VM_RFC1048; 70 #ifdef BOOTP_VEND_CMU 71 static char vm_cmu[4] = VM_CMU; 72 #endif 73 74 /* Local forwards */ 75 static ssize_t bootpsend __P((struct iodesc *, void *, size_t)); 76 static ssize_t bootprecv __P((struct iodesc *, void *, size_t, time_t)); 77 static int vend_rfc1048 __P((u_char *, u_int)); 78 #ifdef BOOTP_VEND_CMU 79 static void vend_cmu __P((u_char *)); 80 #endif 81 82 #ifdef SUPPORT_DHCP 83 static char expected_dhcpmsgtype = -1, dhcp_ok; 84 struct in_addr dhcp_serverip; 85 #endif 86 87 /* 88 * Boot programs can patch this at run-time to change the behavior 89 * of bootp/dhcp. 90 */ 91 int bootp_flags; 92 93 /* Fetch required bootp information */ 94 void 95 bootp(sock) 96 int sock; 97 { 98 struct iodesc *d; 99 struct bootp *bp; 100 struct { 101 u_char header[HEADER_SIZE]; 102 struct bootp wbootp; 103 } wbuf; 104 struct { 105 u_char header[HEADER_SIZE]; 106 struct bootp rbootp; 107 } rbuf; 108 109 #ifdef BOOTP_DEBUG 110 if (debug) 111 printf("bootp: socket=%d\n", sock); 112 #endif 113 if (!bot) 114 bot = getsecs(); 115 116 if (!(d = socktodesc(sock))) { 117 printf("bootp: bad socket. %d\n", sock); 118 return; 119 } 120 #ifdef BOOTP_DEBUG 121 if (debug) 122 printf("bootp: d=%lx\n", (long)d); 123 #endif 124 125 bp = &wbuf.wbootp; 126 bzero(bp, sizeof(*bp)); 127 128 bp->bp_op = BOOTREQUEST; 129 bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */ 130 bp->bp_hlen = 6; 131 bp->bp_xid = htonl(d->xid); 132 MACPY(d->myea, bp->bp_chaddr); 133 strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file)); 134 bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)); 135 #ifdef SUPPORT_DHCP 136 bp->bp_vend[4] = TAG_DHCP_MSGTYPE; 137 bp->bp_vend[5] = 1; 138 bp->bp_vend[6] = DHCPDISCOVER; 139 /* 140 * If we are booting from PXE, we want to send the string 141 * "PXEClient" to the DHCP server in case it only wants to 142 * respond to PXE clients. 143 */ 144 if (bootp_flags & BOOTP_PXE) { 145 bp->bp_vend[7] = TAG_CLASSID; 146 bp->bp_vend[8] = 9; 147 bcopy("PXEClient", &bp->bp_vend[9], 9); 148 bp->bp_vend[18] = TAG_END; 149 } else 150 bp->bp_vend[7] = TAG_END; 151 #else 152 bp->bp_vend[4] = TAG_END; 153 #endif 154 155 d->myip.s_addr = INADDR_ANY; 156 d->myport = htons(IPPORT_BOOTPC); 157 d->destip.s_addr = INADDR_BROADCAST; 158 d->destport = htons(IPPORT_BOOTPS); 159 160 #ifdef SUPPORT_DHCP 161 expected_dhcpmsgtype = DHCPOFFER; 162 dhcp_ok = 0; 163 #endif 164 165 if (sendrecv(d, 166 bootpsend, bp, sizeof(*bp), 167 bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp)) 168 == -1) { 169 printf("bootp: no reply\n"); 170 return; 171 } 172 173 #ifdef SUPPORT_DHCP 174 if (dhcp_ok) { 175 u_int32_t leasetime; 176 bp->bp_vend[6] = DHCPREQUEST; 177 bp->bp_vend[7] = TAG_REQ_ADDR; 178 bp->bp_vend[8] = 4; 179 bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4); 180 bp->bp_vend[13] = TAG_SERVERID; 181 bp->bp_vend[14] = 4; 182 bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4); 183 bp->bp_vend[19] = TAG_LEASETIME; 184 bp->bp_vend[20] = 4; 185 leasetime = htonl(300); 186 bcopy(&leasetime, &bp->bp_vend[21], 4); 187 if (bootp_flags & BOOTP_PXE) { 188 bp->bp_vend[25] = TAG_CLASSID; 189 bp->bp_vend[26] = 9; 190 bcopy("PXEClient", &bp->bp_vend[27], 9); 191 bp->bp_vend[36] = TAG_END; 192 } 193 bp->bp_vend[25] = TAG_END; 194 195 expected_dhcpmsgtype = DHCPACK; 196 197 if (sendrecv(d, 198 bootpsend, bp, sizeof(*bp), 199 bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp)) 200 == -1) { 201 printf("DHCPREQUEST failed\n"); 202 return; 203 } 204 } 205 #endif 206 207 myip = d->myip = rbuf.rbootp.bp_yiaddr; 208 servip = rbuf.rbootp.bp_siaddr; 209 if (rootip.s_addr == INADDR_ANY) 210 rootip = servip; 211 bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile)); 212 bootfile[sizeof(bootfile) - 1] = '\0'; 213 214 if (IN_CLASSA(myip.s_addr)) 215 nmask = IN_CLASSA_NET; 216 else if (IN_CLASSB(myip.s_addr)) 217 nmask = IN_CLASSB_NET; 218 else 219 nmask = IN_CLASSC_NET; 220 #ifdef BOOTP_DEBUG 221 if (debug) 222 printf("'native netmask' is %s\n", intoa(nmask)); 223 #endif 224 225 /* Check subnet mask against net mask; toss if bogus */ 226 if ((nmask & smask) != nmask) { 227 #ifdef BOOTP_DEBUG 228 if (debug) 229 printf("subnet mask (%s) bad\n", intoa(smask)); 230 #endif 231 smask = 0; 232 } 233 234 /* Get subnet (or natural net) mask */ 235 netmask = nmask; 236 if (smask) 237 netmask = smask; 238 #ifdef BOOTP_DEBUG 239 if (debug) 240 printf("mask: %s\n", intoa(netmask)); 241 #endif 242 243 /* We need a gateway if root is on a different net */ 244 if (!SAMENET(myip, rootip, netmask)) { 245 #ifdef BOOTP_DEBUG 246 if (debug) 247 printf("need gateway for root ip\n"); 248 #endif 249 } 250 251 /* Toss gateway if on a different net */ 252 if (!SAMENET(myip, gateip, netmask)) { 253 #ifdef BOOTP_DEBUG 254 if (debug) 255 printf("gateway ip (%s) bad\n", inet_ntoa(gateip)); 256 #endif 257 gateip.s_addr = 0; 258 } 259 260 /* Bump xid so next request will be unique. */ 261 ++d->xid; 262 } 263 264 /* Transmit a bootp request */ 265 static ssize_t 266 bootpsend(d, pkt, len) 267 struct iodesc *d; 268 void *pkt; 269 size_t len; 270 { 271 struct bootp *bp; 272 273 #ifdef BOOTP_DEBUG 274 if (debug) 275 printf("bootpsend: d=%lx called.\n", (long)d); 276 #endif 277 278 bp = pkt; 279 bp->bp_secs = htons((u_short)(getsecs() - bot)); 280 281 #ifdef BOOTP_DEBUG 282 if (debug) 283 printf("bootpsend: calling sendudp\n"); 284 #endif 285 286 return (sendudp(d, pkt, len)); 287 } 288 289 static ssize_t 290 bootprecv(d, pkt, len, tleft) 291 struct iodesc *d; 292 void *pkt; 293 size_t len; 294 time_t tleft; 295 { 296 ssize_t n; 297 struct bootp *bp; 298 299 #ifdef BOOTP_DEBUGx 300 if (debug) 301 printf("bootp_recvoffer: called\n"); 302 #endif 303 304 n = readudp(d, pkt, len, tleft); 305 if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE) 306 goto bad; 307 308 bp = (struct bootp *)pkt; 309 310 #ifdef BOOTP_DEBUG 311 if (debug) 312 printf("bootprecv: checked. bp = 0x%lx, n = %d\n", 313 (long)bp, (int)n); 314 #endif 315 if (bp->bp_xid != htonl(d->xid)) { 316 #ifdef BOOTP_DEBUG 317 if (debug) { 318 printf("bootprecv: expected xid 0x%lx, got 0x%x\n", 319 d->xid, ntohl(bp->bp_xid)); 320 } 321 #endif 322 goto bad; 323 } 324 325 /* protect against bogus addresses sent by DHCP servers */ 326 if (bp->bp_yiaddr.s_addr == INADDR_ANY || 327 bp->bp_yiaddr.s_addr == INADDR_BROADCAST) 328 goto bad; 329 330 #ifdef BOOTP_DEBUG 331 if (debug) 332 printf("bootprecv: got one!\n"); 333 #endif 334 335 /* Suck out vendor info */ 336 if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) { 337 if (vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0) 338 goto bad; 339 } 340 #ifdef BOOTP_VEND_CMU 341 else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0) 342 vend_cmu(bp->bp_vend); 343 #endif 344 else 345 printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend); 346 347 return (n); 348 bad: 349 errno = 0; 350 return (-1); 351 } 352 353 static int 354 vend_rfc1048(cp, len) 355 u_char *cp; 356 u_int len; 357 { 358 u_char *ep; 359 int size; 360 u_char tag; 361 362 #ifdef BOOTP_DEBUG 363 if (debug) 364 printf("vend_rfc1048 bootp info. len=%d\n", len); 365 #endif 366 ep = cp + len; 367 368 /* Step over magic cookie */ 369 cp += sizeof(int); 370 371 while (cp < ep) { 372 tag = *cp++; 373 size = *cp++; 374 if (tag == TAG_END) 375 break; 376 377 if (tag == TAG_SUBNET_MASK) { 378 bcopy(cp, &smask, sizeof(smask)); 379 } 380 if (tag == TAG_GATEWAY) { 381 bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr)); 382 } 383 if (tag == TAG_SWAPSERVER) { 384 /* let it override bp_siaddr */ 385 bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr)); 386 } 387 if (tag == TAG_ROOTPATH) { 388 strncpy(rootpath, (char *)cp, sizeof(rootpath)); 389 rootpath[size] = '\0'; 390 } 391 if (tag == TAG_HOSTNAME) { 392 strncpy(hostname, (char *)cp, sizeof(hostname)); 393 hostname[size] = '\0'; 394 } 395 #ifdef SUPPORT_DHCP 396 if (tag == TAG_DHCP_MSGTYPE) { 397 if (*cp != expected_dhcpmsgtype) 398 return (-1); 399 dhcp_ok = 1; 400 } 401 if (tag == TAG_SERVERID) { 402 bcopy(cp, &dhcp_serverip.s_addr, 403 sizeof(dhcp_serverip.s_addr)); 404 } 405 #endif 406 #ifdef SUPPORT_LINUX 407 if (tag == TAG_LINUX_CMDLINE) { 408 strncpy(linuxcmdline, (char *)cp, sizeof(linuxcmdline)); 409 linuxcmdline[size] = '\0'; 410 } 411 #endif 412 cp += size; 413 } 414 return (0); 415 } 416 417 #ifdef BOOTP_VEND_CMU 418 static void 419 vend_cmu(cp) 420 u_char *cp; 421 { 422 struct cmu_vend *vp; 423 424 #ifdef BOOTP_DEBUG 425 if (debug) 426 printf("vend_cmu bootp info.\n"); 427 #endif 428 vp = (struct cmu_vend *)cp; 429 430 if (vp->v_smask.s_addr != 0) { 431 smask = vp->v_smask.s_addr; 432 } 433 if (vp->v_dgate.s_addr != 0) { 434 gateip = vp->v_dgate; 435 } 436 } 437 #endif 438