1 /* $OpenBSD: bootp.c,v 1.16 2016/02/06 23:50:10 krw Exp $ */ 2 3 /* 4 * BOOTP Protocol support. 5 */ 6 7 /* 8 * Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium. 9 * All rights reserved. 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 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of The Internet Software Consortium nor the names 21 * of its contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND 25 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR 29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 32 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 35 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * This software has been written for the Internet Software Consortium 39 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie 40 * Enterprises. To learn more about the Internet Software Consortium, 41 * see ``http://www.vix.com/isc''. To learn more about Vixie 42 * Enterprises, see ``http://www.vix.com''. 43 */ 44 45 #include <sys/socket.h> 46 47 #include <arpa/inet.h> 48 49 #include <net/if.h> 50 51 #include <netinet/in.h> 52 53 #include <errno.h> 54 #include <stdio.h> 55 #include <string.h> 56 57 #include "dhcp.h" 58 #include "tree.h" 59 #include "dhcpd.h" 60 61 void 62 bootp(struct packet *packet) 63 { 64 struct host_decl *hp, *host = NULL; 65 struct packet outgoing; 66 struct dhcp_packet raw; 67 struct sockaddr_in to; 68 struct in_addr from; 69 struct tree_cache *options[256]; 70 struct shared_network *s; 71 struct subnet *subnet = NULL; 72 struct lease *lease; 73 struct iaddr ip_address; 74 int i; 75 76 if (packet->raw->op != BOOTREQUEST) 77 return; 78 79 note("BOOTREQUEST from %s via %s%s", print_hw_addr(packet->raw->htype, 80 packet->raw->hlen, packet->raw->chaddr), 81 packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : 82 packet->interface->name, 83 packet->options_valid ? "" : " (non-rfc1048)"); 84 85 if (!locate_network(packet)) 86 return; 87 88 hp = find_hosts_by_haddr(packet->raw->htype, packet->raw->chaddr, 89 packet->raw->hlen); 90 91 s = packet->shared_network; 92 lease = find_lease(packet, s, 0); 93 94 /* 95 * Find an IP address in the host_decl that matches the specified 96 * network. 97 */ 98 if (hp) 99 subnet = find_host_for_network(&hp, &ip_address, s); 100 101 if (!subnet) { 102 /* 103 * We didn't find an applicable host declaration. Just in case 104 * we may be able to dynamically assign an address, see if 105 * there's a host declaration that doesn't have an ip address 106 * associated with it. 107 */ 108 if (hp) 109 for (; hp; hp = hp->n_ipaddr) 110 if (!hp->fixed_addr) { 111 host = hp; 112 break; 113 } 114 115 if (host && (!host->group->allow_booting)) { 116 note("Ignoring excluded BOOTP client %s", host->name ? 117 host->name : print_hw_addr (packet->raw->htype, 118 packet->raw->hlen, packet->raw->chaddr)); 119 return; 120 } 121 122 if (host && (!host->group->allow_bootp)) { 123 note("Ignoring BOOTP request from client %s", 124 host->name ? host->name : 125 print_hw_addr(packet->raw->htype, 126 packet->raw->hlen, packet->raw->chaddr)); 127 return; 128 } 129 130 /* 131 * If we've been told not to boot unknown clients, and we didn't 132 * find any host record for this client, ignore it. 133 */ 134 if (!host && !(s->group->boot_unknown_clients)) { 135 note("Ignoring unknown BOOTP client %s via %s", 136 print_hw_addr(packet->raw->htype, 137 packet->raw->hlen, packet->raw->chaddr), 138 packet->raw->giaddr.s_addr ? 139 inet_ntoa(packet->raw->giaddr) : 140 packet->interface->name); 141 return; 142 } 143 144 /* 145 * If we've been told not to boot with bootp on this network, 146 * ignore it. 147 */ 148 if (!host && !(s->group->allow_bootp)) { 149 note("Ignoring BOOTP request from client %s via %s", 150 print_hw_addr(packet->raw->htype, 151 packet->raw->hlen, packet->raw->chaddr), 152 packet->raw->giaddr.s_addr ? 153 inet_ntoa(packet->raw->giaddr) : 154 packet->interface->name); 155 return; 156 } 157 158 /* 159 * If the packet is from a host we don't know and there are no 160 * dynamic bootp addresses on the network it came in on, drop 161 * it on the floor. 162 */ 163 if (!(s->group->dynamic_bootp)) { 164 lose: 165 note("No applicable record for BOOTP host %s via %s", 166 print_hw_addr(packet->raw->htype, 167 packet->raw->hlen, packet->raw->chaddr), 168 packet->raw->giaddr.s_addr ? 169 inet_ntoa(packet->raw->giaddr) : 170 packet->interface->name); 171 return; 172 } 173 174 /* 175 * If a lease has already been assigned to this client and it's 176 * still okay to use dynamic bootp on that lease, reassign it. 177 */ 178 if (lease) { 179 /* 180 * If this lease can be used for dynamic bootp, do so. 181 */ 182 if ((lease->flags & DYNAMIC_BOOTP_OK)) { 183 /* 184 * If it's not a DYNAMIC_BOOTP lease, release it 185 * before reassigning it so that we don't get a 186 * lease conflict. 187 */ 188 if (!(lease->flags & BOOTP_LEASE)) 189 release_lease(lease); 190 191 lease->host = host; 192 ack_lease(packet, lease, 0, 0); 193 return; 194 } 195 196 /* 197 * If dynamic BOOTP is no longer allowed for this 198 * lease, set it free. 199 */ 200 release_lease(lease); 201 } 202 203 /* 204 * If there are dynamic bootp addresses that might be 205 * available, try to snag one. 206 */ 207 for (lease = s->last_lease; 208 lease && lease->ends <= cur_time; 209 lease = lease->prev) { 210 if ((lease->flags & DYNAMIC_BOOTP_OK)) { 211 lease->host = host; 212 ack_lease(packet, lease, 0, 0); 213 return; 214 } 215 } 216 goto lose; 217 } 218 219 /* Make sure we're allowed to boot this client. */ 220 if (hp && (!hp->group->allow_booting)) { 221 note("Ignoring excluded BOOTP client %s", hp->name); 222 return; 223 } 224 225 /* Make sure we're allowed to boot this client with bootp. */ 226 if (hp && (!hp->group->allow_bootp)) { 227 note("Ignoring BOOTP request from client %s", hp->name); 228 return; 229 } 230 231 /* Set up the outgoing packet... */ 232 memset(&outgoing, 0, sizeof outgoing); 233 memset(&raw, 0, sizeof raw); 234 outgoing.raw = &raw; 235 236 /* 237 * If we didn't get a known vendor magic number on the way in, just 238 * copy the input options to the output. 239 */ 240 if (!packet->options_valid && !subnet->group->always_reply_rfc1048 && 241 (!hp || !hp->group->always_reply_rfc1048)) { 242 memcpy(outgoing.raw->options, packet->raw->options, 243 DHCP_OPTION_LEN); 244 outgoing.packet_length = BOOTP_MIN_LEN; 245 } else { 246 struct tree_cache netmask_tree; /* -- RBF */ 247 248 /* 249 * Come up with a list of options that we want to send to this 250 * client. Start with the per-subnet options, and then override 251 * those with client-specific options. 252 */ 253 254 memcpy(options, subnet->group->options, sizeof(options)); 255 256 for (i = 0; i < 256; i++) 257 if (hp->group->options[i]) 258 options[i] = hp->group->options[i]; 259 260 /* 261 * Use the subnet mask from the subnet declaration if no other 262 * mask has been provided. 263 */ 264 if (!options[DHO_SUBNET_MASK]) { 265 options[DHO_SUBNET_MASK] = &netmask_tree; 266 netmask_tree.flags = TC_TEMPORARY; 267 netmask_tree.value = lease->subnet->netmask.iabuf; 268 netmask_tree.len = lease->subnet->netmask.len; 269 netmask_tree.buf_size = lease->subnet->netmask.len; 270 netmask_tree.timeout = -1; 271 netmask_tree.tree = NULL; 272 } 273 274 /* 275 * Pack the options into the buffer. Unlike DHCP, we can't pack 276 * options into the filename and server name buffers. 277 */ 278 279 outgoing.packet_length = cons_options(packet, outgoing.raw, 280 0, options, 0, 0, 1, NULL, 0); 281 282 if (outgoing.packet_length < BOOTP_MIN_LEN) 283 outgoing.packet_length = BOOTP_MIN_LEN; 284 } 285 286 /* Take the fields that we care about... */ 287 raw.op = BOOTREPLY; 288 raw.htype = packet->raw->htype; 289 raw.hlen = packet->raw->hlen; 290 memcpy(raw.chaddr, packet->raw->chaddr, sizeof(raw.chaddr)); 291 raw.hops = packet->raw->hops; 292 raw.xid = packet->raw->xid; 293 raw.secs = packet->raw->secs; 294 raw.flags = packet->raw->flags; 295 raw.ciaddr = packet->raw->ciaddr; 296 memcpy(&raw.yiaddr, ip_address.iabuf, sizeof(raw.yiaddr)); 297 298 /* Figure out the address of the next server. */ 299 if (hp && hp->group->next_server.len) 300 memcpy(&raw.siaddr, hp->group->next_server.iabuf, 4); 301 else if (subnet->group->next_server.len) 302 memcpy(&raw.siaddr, subnet->group->next_server.iabuf, 4); 303 else if (subnet->interface_address.len) 304 memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4); 305 else 306 raw.siaddr = packet->interface->primary_address; 307 308 raw.giaddr = packet->raw->giaddr; 309 if (hp->group->server_name) 310 strncpy(raw.sname, hp->group->server_name, sizeof(raw.sname)); 311 else if (subnet->group->server_name) 312 strncpy(raw.sname, subnet->group->server_name, 313 sizeof(raw.sname)); 314 315 if (hp->group->filename) 316 strncpy(raw.file, hp->group->filename, sizeof(raw.file)); 317 else if (subnet->group->filename) 318 strncpy(raw.file, subnet->group->filename, sizeof(raw.file)); 319 else 320 memcpy(raw.file, packet->raw->file, sizeof(raw.file)); 321 322 from = packet->interface->primary_address; 323 324 /* Report what we're doing... */ 325 note("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address), 326 hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen, 327 packet->raw->chaddr), packet->raw->giaddr.s_addr ? 328 inet_ntoa(packet->raw->giaddr) : packet->interface->name); 329 330 /* Set up the parts of the address that are in common. */ 331 memset(&to, 0, sizeof(to)); 332 to.sin_family = AF_INET; 333 #ifdef HAVE_SA_LEN 334 to.sin_len = sizeof(to); 335 #endif 336 337 /* If this was gatewayed, send it back to the gateway... */ 338 if (raw.giaddr.s_addr) { 339 to.sin_addr = raw.giaddr; 340 to.sin_port = server_port; 341 342 (void) packet->interface->send_packet(packet->interface, &raw, 343 outgoing.packet_length, from, &to, packet->haddr); 344 return; 345 } 346 347 /* 348 * If it comes from a client that already knows its address and is not 349 * requesting a broadcast response, and we can unicast to a client 350 * without using the ARP protocol, sent it directly to that client. 351 */ 352 else if (!(raw.flags & htons(BOOTP_BROADCAST))) { 353 to.sin_addr = raw.yiaddr; 354 to.sin_port = client_port; 355 } else { 356 /* Otherwise, broadcast it on the local network. */ 357 to.sin_addr.s_addr = INADDR_BROADCAST; 358 to.sin_port = client_port; /* XXX */ 359 } 360 361 errno = 0; 362 (void) packet->interface->send_packet(packet->interface, &raw, 363 outgoing.packet_length, from, &to, packet->haddr); 364 } 365