Annotation of embedaddon/dhcp/server/bootp.c, revision 1.1
1.1 ! misho 1: /* bootp.c
! 2:
! 3: BOOTP Protocol support. */
! 4:
! 5: /*
! 6: * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
! 7: * Copyright (c) 1995-2003 by Internet Software Consortium
! 8: *
! 9: * Permission to use, copy, modify, and distribute this software for any
! 10: * purpose with or without fee is hereby granted, provided that the above
! 11: * copyright notice and this permission notice appear in all copies.
! 12: *
! 13: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
! 14: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 15: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
! 16: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 17: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 18: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
! 19: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 20: *
! 21: * Internet Systems Consortium, Inc.
! 22: * 950 Charter Street
! 23: * Redwood City, CA 94063
! 24: * <info@isc.org>
! 25: * https://www.isc.org/
! 26: *
! 27: * This software has been written for Internet Systems Consortium
! 28: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
! 29: * To learn more about Internet Systems Consortium, see
! 30: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
! 31: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
! 32: * ``http://www.nominum.com''.
! 33: */
! 34:
! 35: #include "dhcpd.h"
! 36: #include <errno.h>
! 37:
! 38: #if defined (TRACING)
! 39: # define send_packet trace_packet_send
! 40: #endif
! 41:
! 42: void bootp (packet)
! 43: struct packet *packet;
! 44: {
! 45: int result;
! 46: struct host_decl *hp = (struct host_decl *)0;
! 47: struct host_decl *host = (struct host_decl *)0;
! 48: struct packet outgoing;
! 49: struct dhcp_packet raw;
! 50: struct sockaddr_in to;
! 51: struct in_addr from;
! 52: struct hardware hto;
! 53: struct option_state *options = (struct option_state *)0;
! 54: struct lease *lease = (struct lease *)0;
! 55: unsigned i;
! 56: struct data_string d1;
! 57: struct option_cache *oc;
! 58: char msgbuf [1024];
! 59: int ignorep;
! 60: int peer_has_leases = 0;
! 61:
! 62: if (packet -> raw -> op != BOOTREQUEST)
! 63: return;
! 64:
! 65: /* %Audit% This is log output. %2004.06.17,Safe%
! 66: * If we truncate we hope the user can get a hint from the log.
! 67: */
! 68: snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %s via %s",
! 69: print_hw_addr (packet -> raw -> htype,
! 70: packet -> raw -> hlen,
! 71: packet -> raw -> chaddr),
! 72: packet -> raw -> giaddr.s_addr
! 73: ? inet_ntoa (packet -> raw -> giaddr)
! 74: : packet -> interface -> name);
! 75:
! 76: if (!locate_network (packet)) {
! 77: log_info ("%s: network unknown", msgbuf);
! 78: return;
! 79: }
! 80:
! 81: find_lease (&lease, packet, packet -> shared_network,
! 82: 0, 0, (struct lease *)0, MDL);
! 83:
! 84: if (lease && lease->host)
! 85: host_reference(&hp, lease->host, MDL);
! 86:
! 87: if (!lease || ((lease->flags & STATIC_LEASE) == 0)) {
! 88: struct host_decl *h;
! 89:
! 90: /* We didn't find an applicable fixed-address host
! 91: declaration. Just in case we may be able to dynamically
! 92: assign an address, see if there's a host declaration
! 93: that doesn't have an ip address associated with it. */
! 94:
! 95: if (!hp)
! 96: find_hosts_by_haddr(&hp, packet->raw->htype,
! 97: packet->raw->chaddr,
! 98: packet->raw->hlen, MDL);
! 99:
! 100: for (h = hp; h; h = h -> n_ipaddr) {
! 101: if (!h -> fixed_addr) {
! 102: host_reference(&host, h, MDL);
! 103: break;
! 104: }
! 105: }
! 106:
! 107: if (hp)
! 108: host_dereference(&hp, MDL);
! 109:
! 110: if (host) {
! 111: host_reference(&hp, host, MDL);
! 112: host_dereference(&host, MDL);
! 113: }
! 114:
! 115: /* Allocate a lease if we have not yet found one. */
! 116: if (!lease)
! 117: allocate_lease (&lease, packet,
! 118: packet -> shared_network -> pools,
! 119: &peer_has_leases);
! 120:
! 121: if (lease == NULL) {
! 122: log_info("%s: BOOTP from dynamic client and no "
! 123: "dynamic leases", msgbuf);
! 124: goto out;
! 125: }
! 126:
! 127: #if defined(FAILOVER_PROTOCOL)
! 128: if ((lease->pool != NULL) &&
! 129: (lease->pool->failover_peer != NULL)) {
! 130: dhcp_failover_state_t *peer;
! 131:
! 132: peer = lease->pool->failover_peer;
! 133:
! 134: /* If we are in a failover state that bars us from
! 135: * answering, do not do so.
! 136: * If we are in a cooperative state, load balance
! 137: * (all) responses.
! 138: */
! 139: if ((peer->service_state == not_responding) ||
! 140: (peer->service_state == service_startup)) {
! 141: log_info("%s: not responding%s",
! 142: msgbuf, peer->nrr);
! 143: goto out;
! 144: } else if((peer->service_state == cooperating) &&
! 145: !load_balance_mine(packet, peer)) {
! 146: log_info("%s: load balance to peer %s",
! 147: msgbuf, peer->name);
! 148: goto out;
! 149: }
! 150: }
! 151: #endif
! 152:
! 153: ack_lease (packet, lease, 0, 0, msgbuf, 0, hp);
! 154: goto out;
! 155: }
! 156:
! 157: /* Run the executable statements to compute the client and server
! 158: options. */
! 159: option_state_allocate (&options, MDL);
! 160:
! 161: /* Execute the subnet statements. */
! 162: execute_statements_in_scope ((struct binding_value **)0,
! 163: packet, lease, (struct client_state *)0,
! 164: packet -> options, options,
! 165: &lease -> scope, lease -> subnet -> group,
! 166: (struct group *)0);
! 167:
! 168: /* Execute statements from class scopes. */
! 169: for (i = packet -> class_count; i > 0; i--) {
! 170: execute_statements_in_scope
! 171: ((struct binding_value **)0,
! 172: packet, lease, (struct client_state *)0,
! 173: packet -> options, options,
! 174: &lease -> scope, packet -> classes [i - 1] -> group,
! 175: lease -> subnet -> group);
! 176: }
! 177:
! 178: /* Execute the host statements. */
! 179: execute_statements_in_scope ((struct binding_value **)0,
! 180: packet, lease, (struct client_state *)0,
! 181: packet -> options, options,
! 182: &lease -> scope,
! 183: hp -> group, lease -> subnet -> group);
! 184:
! 185: /* Drop the request if it's not allowed for this client. */
! 186: if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) &&
! 187: !evaluate_boolean_option_cache (&ignorep, packet, lease,
! 188: (struct client_state *)0,
! 189: packet -> options, options,
! 190: &lease -> scope, oc, MDL)) {
! 191: if (!ignorep)
! 192: log_info ("%s: bootp disallowed", msgbuf);
! 193: goto out;
! 194: }
! 195:
! 196: if ((oc = lookup_option (&server_universe,
! 197: options, SV_ALLOW_BOOTING)) &&
! 198: !evaluate_boolean_option_cache (&ignorep, packet, lease,
! 199: (struct client_state *)0,
! 200: packet -> options, options,
! 201: &lease -> scope, oc, MDL)) {
! 202: if (!ignorep)
! 203: log_info ("%s: booting disallowed", msgbuf);
! 204: goto out;
! 205: }
! 206:
! 207: /* Set up the outgoing packet... */
! 208: memset (&outgoing, 0, sizeof outgoing);
! 209: memset (&raw, 0, sizeof raw);
! 210: outgoing.raw = &raw;
! 211:
! 212: /* If we didn't get a known vendor magic number on the way in,
! 213: just copy the input options to the output. */
! 214: if (!packet -> options_valid &&
! 215: !(evaluate_boolean_option_cache
! 216: (&ignorep, packet, lease, (struct client_state *)0,
! 217: packet -> options, options, &lease -> scope,
! 218: lookup_option (&server_universe, options,
! 219: SV_ALWAYS_REPLY_RFC1048), MDL))) {
! 220: memcpy (outgoing.raw -> options,
! 221: packet -> raw -> options, DHCP_MAX_OPTION_LEN);
! 222: outgoing.packet_length = BOOTP_MIN_LEN;
! 223: } else {
! 224:
! 225: /* Use the subnet mask from the subnet declaration if no other
! 226: mask has been provided. */
! 227:
! 228: oc = (struct option_cache *)0;
! 229: i = DHO_SUBNET_MASK;
! 230: if (!lookup_option (&dhcp_universe, options, i)) {
! 231: if (option_cache_allocate (&oc, MDL)) {
! 232: if (make_const_data
! 233: (&oc -> expression,
! 234: lease -> subnet -> netmask.iabuf,
! 235: lease -> subnet -> netmask.len,
! 236: 0, 0, MDL)) {
! 237: option_code_hash_lookup(&oc->option,
! 238: dhcp_universe.code_hash,
! 239: &i, 0, MDL);
! 240: save_option (&dhcp_universe,
! 241: options, oc);
! 242: }
! 243: option_cache_dereference (&oc, MDL);
! 244: }
! 245: }
! 246:
! 247: /* Pack the options into the buffer. Unlike DHCP, we
! 248: can't pack options into the filename and server
! 249: name buffers. */
! 250:
! 251: outgoing.packet_length =
! 252: cons_options (packet, outgoing.raw, lease,
! 253: (struct client_state *)0, 0,
! 254: packet -> options, options,
! 255: &lease -> scope,
! 256: 0, 0, 1, (struct data_string *)0,
! 257: (const char *)0);
! 258: if (outgoing.packet_length < BOOTP_MIN_LEN)
! 259: outgoing.packet_length = BOOTP_MIN_LEN;
! 260: }
! 261:
! 262: /* Take the fields that we care about... */
! 263: raw.op = BOOTREPLY;
! 264: raw.htype = packet -> raw -> htype;
! 265: raw.hlen = packet -> raw -> hlen;
! 266: memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
! 267: raw.hops = packet -> raw -> hops;
! 268: raw.xid = packet -> raw -> xid;
! 269: raw.secs = packet -> raw -> secs;
! 270: raw.flags = packet -> raw -> flags;
! 271: raw.ciaddr = packet -> raw -> ciaddr;
! 272:
! 273: /* yiaddr is an ipv4 address, it must be 4 octets. */
! 274: memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4);
! 275:
! 276: /* If we're always supposed to broadcast to this client, set
! 277: the broadcast bit in the bootp flags field. */
! 278: if ((oc = lookup_option (&server_universe,
! 279: options, SV_ALWAYS_BROADCAST)) &&
! 280: evaluate_boolean_option_cache (&ignorep, packet, lease,
! 281: (struct client_state *)0,
! 282: packet -> options, options,
! 283: &lease -> scope, oc, MDL))
! 284: raw.flags |= htons (BOOTP_BROADCAST);
! 285:
! 286: /* Figure out the address of the next server. */
! 287: memset (&d1, 0, sizeof d1);
! 288: oc = lookup_option (&server_universe, options, SV_NEXT_SERVER);
! 289: if (oc &&
! 290: evaluate_option_cache (&d1, packet, lease,
! 291: (struct client_state *)0,
! 292: packet -> options, options,
! 293: &lease -> scope, oc, MDL)) {
! 294: /* If there was more than one answer, take the first. */
! 295: if (d1.len >= 4 && d1.data)
! 296: memcpy (&raw.siaddr, d1.data, 4);
! 297: data_string_forget (&d1, MDL);
! 298: } else {
! 299: if ((lease->subnet->shared_network->interface != NULL) &&
! 300: lease->subnet->shared_network->interface->address_count)
! 301: raw.siaddr =
! 302: lease->subnet->shared_network->interface->addresses[0];
! 303: else if (packet->interface->address_count)
! 304: raw.siaddr = packet->interface->addresses[0];
! 305: }
! 306:
! 307: raw.giaddr = packet -> raw -> giaddr;
! 308:
! 309: /* Figure out the filename. */
! 310: oc = lookup_option (&server_universe, options, SV_FILENAME);
! 311: if (oc &&
! 312: evaluate_option_cache (&d1, packet, lease,
! 313: (struct client_state *)0,
! 314: packet -> options, options,
! 315: &lease -> scope, oc, MDL)) {
! 316: memcpy (raw.file, d1.data,
! 317: d1.len > sizeof raw.file ? sizeof raw.file : d1.len);
! 318: if (sizeof raw.file > d1.len)
! 319: memset (&raw.file [d1.len],
! 320: 0, (sizeof raw.file) - d1.len);
! 321: data_string_forget (&d1, MDL);
! 322: } else
! 323: memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
! 324:
! 325: /* Choose a server name as above. */
! 326: oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
! 327: if (oc &&
! 328: evaluate_option_cache (&d1, packet, lease,
! 329: (struct client_state *)0,
! 330: packet -> options, options,
! 331: &lease -> scope, oc, MDL)) {
! 332: memcpy (raw.sname, d1.data,
! 333: d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len);
! 334: if (sizeof raw.sname > d1.len)
! 335: memset (&raw.sname [d1.len],
! 336: 0, (sizeof raw.sname) - d1.len);
! 337: data_string_forget (&d1, MDL);
! 338: }
! 339:
! 340: /* Execute the commit statements, if there are any. */
! 341: execute_statements ((struct binding_value **)0,
! 342: packet, lease, (struct client_state *)0,
! 343: packet -> options,
! 344: options, &lease -> scope, lease -> on_commit);
! 345:
! 346: /* We're done with the option state. */
! 347: option_state_dereference (&options, MDL);
! 348:
! 349: /* Set up the hardware destination address... */
! 350: hto.hbuf [0] = packet -> raw -> htype;
! 351: hto.hlen = packet -> raw -> hlen + 1;
! 352: memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
! 353:
! 354: if (packet->interface->address_count) {
! 355: from = packet->interface->addresses[0];
! 356: } else {
! 357: log_error("%s: Interface %s appears to have no IPv4 "
! 358: "addresses, and so dhcpd cannot select a source "
! 359: "address.", msgbuf, packet->interface->name);
! 360: goto out;
! 361: }
! 362:
! 363: /* Report what we're doing... */
! 364: log_info ("%s", msgbuf);
! 365: log_info ("BOOTREPLY for %s to %s (%s) via %s",
! 366: piaddr (lease->ip_addr), hp -> name,
! 367: print_hw_addr (packet -> raw -> htype,
! 368: packet -> raw -> hlen,
! 369: packet -> raw -> chaddr),
! 370: packet -> raw -> giaddr.s_addr
! 371: ? inet_ntoa (packet -> raw -> giaddr)
! 372: : packet -> interface -> name);
! 373:
! 374: /* Set up the parts of the address that are in common. */
! 375: to.sin_family = AF_INET;
! 376: #ifdef HAVE_SA_LEN
! 377: to.sin_len = sizeof to;
! 378: #endif
! 379: memset (to.sin_zero, 0, sizeof to.sin_zero);
! 380:
! 381: /* If this was gatewayed, send it back to the gateway... */
! 382: if (raw.giaddr.s_addr) {
! 383: to.sin_addr = raw.giaddr;
! 384: to.sin_port = local_port;
! 385:
! 386: if (fallback_interface) {
! 387: result = send_packet (fallback_interface,
! 388: (struct packet *)0,
! 389: &raw, outgoing.packet_length,
! 390: from, &to, &hto);
! 391: goto out;
! 392: }
! 393:
! 394: /* If it comes from a client that already knows its address
! 395: and is not requesting a broadcast response, and we can
! 396: unicast to a client without using the ARP protocol, sent it
! 397: directly to that client. */
! 398: } else if (!(raw.flags & htons (BOOTP_BROADCAST)) &&
! 399: can_unicast_without_arp (packet -> interface)) {
! 400: to.sin_addr = raw.yiaddr;
! 401: to.sin_port = remote_port;
! 402:
! 403: /* Otherwise, broadcast it on the local network. */
! 404: } else {
! 405: to.sin_addr = limited_broadcast;
! 406: to.sin_port = remote_port; /* XXX */
! 407: }
! 408:
! 409: errno = 0;
! 410: result = send_packet (packet -> interface,
! 411: packet, &raw, outgoing.packet_length,
! 412: from, &to, &hto);
! 413: out:
! 414: if (options)
! 415: option_state_dereference (&options, MDL);
! 416: if (lease)
! 417: lease_dereference (&lease, MDL);
! 418: if (hp)
! 419: host_dereference (&hp, MDL);
! 420: if (host)
! 421: host_dereference (&host, MDL);
! 422: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>