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