dhcp.c 90.8 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1 2
/* dhcp.c

3
   DHCP Protocol engine. */
Ted Lemon's avatar
Ted Lemon committed
4 5

/*
Ted Lemon's avatar
Ted Lemon committed
6 7 8 9 10 11
 * Copyright (c) 1996-1999 Internet Software Consortium.
 * Use is subject to license terms which appear in the file named
 * ISC-LICENSE that should have accompanied this file when you
 * received it.   If a file named ISC-LICENSE did not accompany this
 * file, or you are not sure the one you have is correct, you may
 * obtain an applicable copy of the license at:
Ted Lemon's avatar
Ted Lemon committed
12
 *
Ted Lemon's avatar
Ted Lemon committed
13
 *             http://www.isc.org/isc-license-1.0.html. 
Ted Lemon's avatar
Ted Lemon committed
14
 *
Ted Lemon's avatar
Ted Lemon committed
15 16 17
 * This file is part of the ISC DHCP distribution.   The documentation
 * associated with this file is listed in the file DOCUMENTATION,
 * included in the top-level directory of this release.
Ted Lemon's avatar
Ted Lemon committed
18
 *
Ted Lemon's avatar
Ted Lemon committed
19 20
 * Support and other services are available for ISC products - see
 * http://www.isc.org for more information.
Ted Lemon's avatar
Ted Lemon committed
21 22 23 24
 */

#ifndef lint
static char copyright[] =
25
"$Id: dhcp.c,v 1.133 2000/01/25 01:35:38 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium.  All rights reserved.\n";
Ted Lemon's avatar
Ted Lemon committed
26 27 28 29
#endif /* not lint */

#include "dhcpd.h"

30 31
int outstanding_pings;

32
static char dhcp_message [256];
33

Ted Lemon's avatar
Ted Lemon committed
34 35 36
void dhcp (packet)
	struct packet *packet;
{
37
	if (!locate_network (packet) && packet -> packet_type != DHCPREQUEST)
38 39
		return;

40 41 42
	/* Classify the client. */
	classify_client (packet);

Ted Lemon's avatar
Ted Lemon committed
43 44 45 46
	switch (packet -> packet_type) {
	      case DHCPDISCOVER:
		dhcpdiscover (packet);
		break;
47

Ted Lemon's avatar
Ted Lemon committed
48 49 50
	      case DHCPREQUEST:
		dhcprequest (packet);
		break;
51

Ted Lemon's avatar
Ted Lemon committed
52 53 54
	      case DHCPRELEASE:
		dhcprelease (packet);
		break;
55

56 57 58 59 60 61 62 63
	      case DHCPDECLINE:
		dhcpdecline (packet);
		break;

	      case DHCPINFORM:
		dhcpinform (packet);
		break;

Ted Lemon's avatar
Ted Lemon committed
64 65
	      default:
		break;
66
	}
Ted Lemon's avatar
Ted Lemon committed
67
}
68

Ted Lemon's avatar
Ted Lemon committed
69 70 71
void dhcpdiscover (packet)
	struct packet *packet;
{
72
	struct lease *lease;
Ted Lemon's avatar
Ted Lemon committed
73
	char msgbuf [1024]; /* XXX */
74
	TIME when;
Ted Lemon's avatar
Ted Lemon committed
75
	char *s;
Ted Lemon's avatar
Ted Lemon committed
76

77 78
	lease = find_lease (packet, packet -> shared_network, 0);

Ted Lemon's avatar
Ted Lemon committed
79 80 81 82 83 84 85 86 87 88 89
	if (lease && lease -> client_hostname &&
	    db_printable (lease -> client_hostname))
		s = lease -> client_hostname;
	else
		s = (char *)0;

	/* Say what we're doing... */
	sprintf (msgbuf, "DHCPDISCOVER from %s %s%s%svia %s",
		 (packet -> raw -> htype
		  ? print_hw_addr (packet -> raw -> htype,
				   packet -> raw -> hlen,
90
				   packet -> raw -> chaddr)
Ted Lemon's avatar
Ted Lemon committed
91 92 93 94 95 96 97 98 99
		  : (lease
		     ? print_hex_1 (lease -> uid_len, lease -> uid, 
				    lease -> uid_len)
		     : "<no identifier>")),
		  s ? "(" : "", s ? s : "", s ? ") " : "",
		  packet -> raw -> giaddr.s_addr
		  ? inet_ntoa (packet -> raw -> giaddr)
		  : packet -> interface -> name);

100 101
	/* Sourceless packets don't make sense here. */
	if (!packet -> shared_network) {
102
		log_info ("Packet from unknown subnet: %s",
103 104 105 106
		      inet_ntoa (packet -> raw -> giaddr));
		return;
	}

107 108
	/* If we didn't find a lease, try to allocate one... */
	if (!lease) {
Ted Lemon's avatar
Ted Lemon committed
109 110 111
		lease = allocate_lease (packet,
					packet -> shared_network -> pools, 0);
		if (!lease) {
112
			log_info ("no free leases on network %s match %s",
Ted Lemon's avatar
Ted Lemon committed
113 114 115 116
			      packet -> shared_network -> name,
			      print_hw_addr (packet -> raw -> htype,
					     packet -> raw -> hlen,
					     packet -> raw -> chaddr));
117 118 119 120
			return;
		}
	}

121 122 123 124
	/* If it's an expired lease, get rid of any bindings. */
	if (lease -> ends < cur_time && lease -> scope.bindings)
		free_bindings (&lease -> scope, "dhcpdiscover");

125 126 127 128 129 130 131
	/* Set the lease to really expire in 2 minutes, unless it has
	   not yet expired, in which case leave its expiry time alone. */
	when = cur_time + 120;
	if (when < lease -> ends)
		when = lease -> ends;

	ack_lease (packet, lease, DHCPOFFER, when, msgbuf);
Ted Lemon's avatar
Ted Lemon committed
132 133 134 135 136
}

void dhcprequest (packet)
	struct packet *packet;
{
137
	struct lease *lease;
138
	struct iaddr cip;
139
	struct subnet *subnet;
140
	int ours = 0;
141 142 143
	struct option_cache *oc;
	struct data_string data;
	int status;
Ted Lemon's avatar
Ted Lemon committed
144 145
	char msgbuf [1024]; /* XXX */
	char *s;
146

147
	oc = lookup_option (&dhcp_universe, packet -> options,
148 149 150
			    DHO_DHCP_REQUESTED_ADDRESS);
	memset (&data, 0, sizeof data);
	if (oc &&
151 152
	    evaluate_option_cache (&data, packet, (struct lease *)0,
				   packet -> options, (struct option_state *)0,
153
				   &global_scope, oc, MDL)) {
154
		cip.len = 4;
155 156
		memcpy (cip.iabuf, data.data, 4);
		data_string_forget (&data, "dhcprequest");
Ted Lemon's avatar
Ted Lemon committed
157
	} else {
158
		oc = (struct option_cache *)0;
Ted Lemon's avatar
Ted Lemon committed
159 160 161
		cip.len = 4;
		memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4);
	}
162
	subnet = find_subnet (cip);
Ted Lemon's avatar
Ted Lemon committed
163

164 165
	/* Find the lease that matches the address requested by the
	   client. */
166 167

	if (subnet)
168
		lease = find_lease (packet, subnet -> shared_network, &ours);
169 170
	else
		lease = (struct lease *)0;
171

Ted Lemon's avatar
Ted Lemon committed
172 173 174 175 176 177 178
	if (lease && lease -> client_hostname &&
	    db_printable (lease -> client_hostname))
		s = lease -> client_hostname;
	else
		s = (char *)0;

	/* Say what we're doing... */
179
	sprintf (msgbuf, "DHCPREQUEST for %s from %s %s%s%svia %s",
180
		 piaddr (cip),
Ted Lemon's avatar
Ted Lemon committed
181 182 183
		 (packet -> raw -> htype
		  ? print_hw_addr (packet -> raw -> htype,
				   packet -> raw -> hlen,
184
				   packet -> raw -> chaddr)
Ted Lemon's avatar
Ted Lemon committed
185 186 187 188 189 190
		  : (lease
		     ? print_hex_1 (lease -> uid_len, lease -> uid, 
				    lease -> uid_len)
		     : "<no identifier>")),
		 s ? "(" : "", s ? s : "", s ? ") " : "",
		  packet -> raw -> giaddr.s_addr
191
		  ? inet_ntoa (packet -> raw -> giaddr)
Ted Lemon's avatar
Ted Lemon committed
192
		  : packet -> interface -> name);
193

194 195 196 197 198
	/* If a client on a given network REQUESTs a lease on an
	   address on a different network, NAK it.  If the Requested
	   Address option was used, the protocol says that it must
	   have been broadcast, so we can trust the source network
	   information.
199 200 201 202 203 204

	   If ciaddr was specified and Requested Address was not, then
	   we really only know for sure what network a packet came from
	   if it came through a BOOTP gateway - if it came through an
	   IP router, we'll just have to assume that it's cool.

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	   If we don't think we know where the packet came from, it
	   came through a gateway from an unknown network, so it's not
	   from a RENEWING client.  If we recognize the network it
	   *thinks* it's on, we can NAK it even though we don't
	   recognize the network it's *actually* on; otherwise we just
	   have to ignore it.

	   We don't currently try to take advantage of access to the
	   raw packet, because it's not available on all platforms.
	   So a packet that was unicast to us through a router from a
	   RENEWING client is going to look exactly like a packet that
	   was broadcast to us from an INIT-REBOOT client.

	   Since we can't tell the difference between these two kinds
	   of packets, if the packet appears to have come in off the
	   local wire, we have to treat it as if it's a RENEWING
	   client.  This means that we can't NAK a RENEWING client on
	   the local wire that has a bogus address.  The good news is
	   that we won't ACK it either, so it should revert to INIT
	   state and send us a DHCPDISCOVER, which we *can* work with.

	   Because we can't detect that a RENEWING client is on the
	   wrong wire, it's going to sit there trying to renew until
	   it gets to the REBIND state, when we *can* NAK it because
	   the packet will get to us through a BOOTP gateway.  We
	   shouldn't actually see DHCPREQUEST packets from RENEWING
	   clients on the wrong wire anyway, since their idea of their
	   local router will be wrong.  In any case, the protocol
	   doesn't really allow us to NAK a DHCPREQUEST from a
	   RENEWING client, so we can punt on this issue. */

	if (!packet -> shared_network ||
	    (packet -> raw -> ciaddr.s_addr &&
238
	     packet -> raw -> giaddr.s_addr) ||
239
	    (oc && !packet -> raw -> ciaddr.s_addr)) {
240 241 242 243 244
		
		/* If we don't know where it came from but we do know
		   where it claims to have come from, it didn't come
		   from there.   Fry it. */
		if (!packet -> shared_network) {
Ted Lemon's avatar
Ted Lemon committed
245
			if (subnet && subnet -> group -> authoritative) {
246
				log_info ("%s: wrong network.", msgbuf);
247 248 249
				nak_lease (packet, &cip);
				return;
			}
250
			/* Otherwise, ignore it. */
251
			log_info ("%s: ignored (not authoritative).", msgbuf);
252
			return;
253
		}
254

255 256
		/* If we do know where it came from and it asked for an
		   address that is not on that shared network, nak it. */
257 258
		subnet = find_grouped_subnet (packet -> shared_network, cip);
		if (!subnet) {
Ted Lemon's avatar
Ted Lemon committed
259 260
			if (packet -> shared_network -> group -> authoritative)
			{
261
				log_info ("%s: wrong network.", msgbuf);
Ted Lemon's avatar
Ted Lemon committed
262
				nak_lease (packet, &cip);
263
				return;
Ted Lemon's avatar
Ted Lemon committed
264
			}
265
			log_info ("%s: ignored (not authoritative).", msgbuf);
266 267 268
			return;
		}
	}
Ted Lemon's avatar
Ted Lemon committed
269

270 271 272
	/* If the address the client asked for is ours, but it wasn't
           available for the client, NAK it. */
	if (!lease && ours) {
273
		log_info ("%s: lease %s unavailable.", msgbuf, piaddr (cip));
274 275 276 277
		nak_lease (packet, &cip);
		return;
	}

Ted Lemon's avatar
Ted Lemon committed
278
	/* Otherwise, send the lease to the client if we found one. */
279
	if (lease) {
280
		ack_lease (packet, lease, DHCPACK, 0, msgbuf);
281
	} else
282
		log_info ("%s: unknown lease %s.", msgbuf, piaddr (cip));
Ted Lemon's avatar
Ted Lemon committed
283 284 285 286 287
}

void dhcprelease (packet)
	struct packet *packet;
{
288
	struct lease *lease;
Ted Lemon's avatar
Ted Lemon committed
289
	struct iaddr cip;
290 291
	struct option_cache *oc;
	struct data_string data;
292
	char *s;
293

294 295 296
	/* DHCPRELEASE must not specify address in requested-address
           option, but old protocol specs weren't explicit about this,
           so let it go. */
297
	if ((oc = lookup_option (&dhcp_universe, packet -> options,
298
				 DHO_DHCP_REQUESTED_ADDRESS))) {
299
		log_info ("DHCPRELEASE from %s specified requested-address.",
300 301 302
		      print_hw_addr (packet -> raw -> htype,
				     packet -> raw -> hlen,
				     packet -> raw -> chaddr));
303 304
	}

305
	oc = lookup_option (&dhcp_universe, packet -> options,
306 307 308
			    DHO_DHCP_CLIENT_IDENTIFIER);
	memset (&data, 0, sizeof data);
	if (oc &&
309 310
	    evaluate_option_cache (&data, packet, (struct lease *)0,
				   packet -> options, (struct option_state *)0,
311
				   &global_scope, oc, MDL)) {
312 313
		lease = find_lease_by_uid (data.data, data.len);
		data_string_forget (&data, "dhcprelease");
Ted Lemon's avatar
Ted Lemon committed
314

315 316 317 318 319 320 321 322
		/* See if we can find a lease that matches the IP address
		   the client is claiming. */
		for (; lease; lease = lease -> n_uid) {
			if (!memcmp (&packet -> raw -> ciaddr,
				     lease -> ip_addr.iabuf, 4)) {
				break;
			}
		}
323 324 325 326 327 328
	}

	/* The client is supposed to pass a valid client-identifier,
	   but the spec on this has changed historically, so try the
	   IP address in ciaddr if the client-identifier fails. */
	if (!lease) {
329 330 331
		cip.len = 4;
		memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
		lease = find_lease_by_ip_addr (cip);
332 333 334
	}


Ted Lemon's avatar
Ted Lemon committed
335 336 337 338 339 340 341 342 343 344 345
	if (lease && lease -> client_hostname &&
	    db_printable (lease -> client_hostname))
		s = lease -> client_hostname;
	else
		s = (char *)0;

	/* Say what we're doing... */
	log_info ("DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
		  inet_ntoa (packet -> raw -> ciaddr),
		  (packet -> raw -> htype
		   ? print_hw_addr (packet -> raw -> htype,
346
			     packet -> raw -> hlen,
347
			     packet -> raw -> chaddr)
Ted Lemon's avatar
Ted Lemon committed
348 349 350 351 352 353 354 355 356
		   : (lease
		      ? print_hex_1 (lease -> uid_len, lease -> uid, 
				     lease -> uid_len)
		      : "<no identifier>")),
		  s ? "(" : "", s ? s : "", s ? ") " : "",
		  packet -> raw -> giaddr.s_addr
		  ? inet_ntoa (packet -> raw -> giaddr)
		  : packet -> interface -> name,
		  lease ? "" : "not ");
Ted Lemon's avatar
Ted Lemon committed
357

358
	/* If we found a lease, release it. */
359
	if (lease && lease -> ends > cur_time)
360
		release_lease (lease, packet);
Ted Lemon's avatar
Ted Lemon committed
361 362
}

363 364 365
void dhcpdecline (packet)
	struct packet *packet;
{
366
	struct lease *lease;
Ted Lemon's avatar
Ted Lemon committed
367
	struct iaddr cip;
368 369
	struct option_cache *oc;
	struct data_string data;
370 371 372 373
	struct option_state *options = (struct option_state *)0;
	int ignorep;
	int i;
	const char *status;
Ted Lemon's avatar
Ted Lemon committed
374
	char *s;
Ted Lemon's avatar
Ted Lemon committed
375

376
	/* DHCPDECLINE must specify address. */
377
	if (!(oc = lookup_option (&dhcp_universe, packet -> options,
378 379 380
				  DHO_DHCP_REQUESTED_ADDRESS)))
		return;
	memset (&data, 0, sizeof data);
381 382 383
	if (!evaluate_option_cache (&data, packet, (struct lease *)0,
				    packet -> options,
				    (struct option_state *)0,
384
				    &global_scope, oc, MDL))
385
		return;
Ted Lemon's avatar
Ted Lemon committed
386

387
	cip.len = 4;
388 389
	memcpy (cip.iabuf, data.data, 4);
	data_string_forget (&data, "dhcpdecline");
390 391
	lease = find_lease_by_ip_addr (cip);

392
	option_state_allocate (&options, "dhcpdecline");
393

394 395 396
	/* Execute statements in scope starting with the subnet scope. */
	if (lease)
		execute_statements_in_scope (packet, (struct lease *)0,
397 398 399
					     packet -> options, options,
					     &global_scope,
					     lease -> subnet -> group,
400 401 402 403 404
					     (struct group *)0);

	/* Execute statements in the class scopes. */
	for (i = packet -> class_count; i > 0; i--) {
		execute_statements_in_scope
405 406
			(packet, (struct lease *)0, packet -> options, options,
			 &global_scope, packet -> classes [i - 1] -> group,
407
			 lease ? lease -> subnet -> group : (struct group *)0);
408
	}
409 410 411 412

	/* Drop the request if dhcpdeclines are being ignored. */
	oc = lookup_option (&server_universe, options, SV_DECLINES);
	if (!oc ||
413 414 415
	    evaluate_boolean_option_cache (&ignorep, packet, lease,
					   packet -> options, options,
					   &lease -> scope, oc, MDL)) {
416 417 418 419 420 421 422 423 424
		/* If we found a lease, mark it as unusable and complain. */
		if (lease) {
			abandon_lease (lease, "declined.");
			status = "";
		}
		status = " (not found)";
	} else
		status = " (ignored)";

Ted Lemon's avatar
Ted Lemon committed
425 426 427 428 429 430 431 432 433
	if (!ignorep) {
		char *s;
		if (lease && lease -> client_hostname &&
		    db_printable (lease -> client_hostname))
			s = lease -> client_hostname;
		else
			s = (char *)0;

		log_info ("DHCPDECLINE of %s from %s %s%s%svia %s %s",
434
			  piaddr (cip),
Ted Lemon's avatar
Ted Lemon committed
435 436 437
			  (packet -> raw -> htype
			   ? print_hw_addr (packet -> raw -> htype,
					    packet -> raw -> hlen,
438
					    packet -> raw -> chaddr)
Ted Lemon's avatar
Ted Lemon committed
439 440 441 442 443
			   : (lease
			      ? print_hex_1 (lease -> uid_len, lease -> uid, 
					     lease -> uid_len)
			      : "<no identifier>")),
			  s ? "(" : "", s ? s : "", s ? ") " : "",
444 445
			  packet -> raw -> giaddr.s_addr
			  ? inet_ntoa (packet -> raw -> giaddr)
Ted Lemon's avatar
Ted Lemon committed
446 447
			  : packet -> interface -> name,
			  status);
448
	}
449 450
		
	option_state_dereference (&options, "dhcpdecline");
451 452 453 454 455
}

void dhcpinform (packet)
	struct packet *packet;
{
Ted Lemon's avatar
Ted Lemon committed
456 457 458 459 460 461 462
	char msgbuf [1024];
	struct data_string d1, prl;
	struct option_cache *oc;
	struct expression *expr;
	struct option_state *options = (struct option_state *)0;
	struct dhcp_packet raw;
	struct packet outgoing;
463
	unsigned char dhcpack = DHCPACK;
Ted Lemon's avatar
Ted Lemon committed
464 465
	struct subnet *subnet;
	struct iaddr cip;
466 467
	unsigned i, j;
	int nulltp;
Ted Lemon's avatar
Ted Lemon committed
468 469 470
	struct sockaddr_in to;
	struct in_addr from;

471 472 473 474 475 476 477 478 479 480 481
	/* The client should set ciaddr to its IP address, but apparently
	   it's common for clients not to do this, so we'll use their IP
	   source address if they didn't set ciaddr. */
	if (!packet -> raw -> ciaddr.s_addr) {
		cip.len = 4;
		memcpy (cip.iabuf, &packet -> client_addr, 4);
	} else {
		cip.len = 4;
		memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
	}

Ted Lemon's avatar
Ted Lemon committed
482
	sprintf (msgbuf, "DHCPINFORM from %s via %s",
483 484 485 486
		 piaddr (cip), packet -> interface -> name);

	/* If the IP source address is zero, don't respond. */
	if (!memcmp (cip.iabuf, "\0\0\0", 4)) {
487
		log_info ("%s: ignored (null source address).", msgbuf);
488 489
		return;
	}
Ted Lemon's avatar
Ted Lemon committed
490 491 492 493 494 495 496 497 498 499 500

	/* Find the subnet that the client is on. */
	oc = (struct option_cache *)0;
	subnet = find_subnet (cip);

	/* Sourceless packets don't make sense here. */
	if (!subnet) {
		log_info ("%s: unknown subnet %s",
			  msgbuf, inet_ntoa (packet -> raw -> giaddr));
	}

501 502 503 504
	/* We don't respond to DHCPINFORM packets if we're not authoritative.
	   It would be nice if a per-host value could override this, but
	   there's overhead involved in checking this, so let's see how people
	   react first. */
Ted Lemon's avatar
Ted Lemon committed
505
	if (subnet && !subnet -> group -> authoritative) {
506 507 508 509 510
		log_info ("%s: not authoritative for subnet %s",
			  msgbuf, piaddr (subnet -> net));
		return;
	}

Ted Lemon's avatar
Ted Lemon committed
511 512 513 514 515 516 517
	memset (&d1, 0, sizeof d1);
	option_state_allocate (&options, "dhcpinform");
	memset (&outgoing, 0, sizeof outgoing);
	memset (&raw, 0, sizeof raw);
	outgoing.raw = &raw;

	/* Execute statements in scope starting with the subnet scope. */
518
	if (subnet)
519
		execute_statements_in_scope (packet, (struct lease *)0,
520 521
					     packet -> options, options,
					     &global_scope, subnet -> group,
522
					     (struct group *)0);
Ted Lemon's avatar
Ted Lemon committed
523 524 525 526

	/* Execute statements in the class scopes. */
	for (i = packet -> class_count; i > 0; i--) {
		execute_statements_in_scope
527 528
			(packet, (struct lease *)0, packet -> options, options,
			 &global_scope, packet -> classes [i - 1] -> group,
529
			 subnet ? subnet -> group : (struct group *)0);
Ted Lemon's avatar
Ted Lemon committed
530 531 532
	}

	/* Figure out the filename. */
533
	memset (&d1, 0, sizeof d1);
Ted Lemon's avatar
Ted Lemon committed
534
	oc = lookup_option (&server_universe, options, SV_FILENAME);
535 536 537
	if (oc &&
	    evaluate_option_cache (&d1, packet, (struct lease *)0,
				   packet -> options, (struct option_state *)0,
538
				   &global_scope, oc, MDL)) {
Ted Lemon's avatar
Ted Lemon committed
539 540 541 542 543 544
		i = d1.len;
		if (i > sizeof raw.file)
			i = sizeof raw.file;
		else
			raw.file [i] = 0;
		memcpy (raw.file, d1.data, i);
545
		data_string_forget (&d1, "dhcpinform");
Ted Lemon's avatar
Ted Lemon committed
546 547 548
	}

	/* Choose a server name as above. */
549
	oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
550 551 552
	if (oc &&
	    evaluate_option_cache (&d1, packet, (struct lease *)0, 
				   packet -> options, (struct option_state *)0,
553
				   &global_scope, oc, MDL)) {
Ted Lemon's avatar
Ted Lemon committed
554 555 556 557 558 559
		i = d1.len;
		if (i > sizeof raw.sname)
			i = sizeof raw.sname;
		else
			raw.sname [i] = 0;
		memcpy (raw.sname, d1.data, i);
560
		data_string_forget (&d1, "dhcpinform");
Ted Lemon's avatar
Ted Lemon committed
561 562 563 564 565 566 567
	}

	/* Set a flag if this client is a lame Microsoft client that NUL
	   terminates string options and expects us to do likewise. */
	nulltp = 0;
	if ((oc = lookup_option (&dhcp_universe, packet -> options,
				 DHO_HOST_NAME))) {
568 569
		if (evaluate_option_cache (&d1, packet, (struct lease *)0, 
					   packet -> options, options,
570
					   &global_scope, oc, MDL)) {
Ted Lemon's avatar
Ted Lemon committed
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
			if (d1.data [d1.len - 1] == '\0')
				nulltp = 1;
			data_string_forget (&d1, "dhcpinform");
		}
	}

	/* Put in DHCP-specific options. */
	i = DHO_DHCP_MESSAGE_TYPE;
	oc = (struct option_cache *)0;
	if (option_cache_allocate (&oc, "dhcpinform")) {
		if (make_const_data (&oc -> expression, &dhcpack, 1, 0, 0)) {
			oc -> option = dhcp_universe.options [i];
			save_option (&dhcp_universe, options, oc);
		}
		option_cache_dereference (&oc, "dhcpinform");
	}

	i = DHO_DHCP_SERVER_IDENTIFIER;
	if (!(oc = lookup_option (&dhcp_universe, options, i))) {
	      use_primary:
		oc = (struct option_cache *)0;
		if (option_cache_allocate (&oc, "dhcpinform")) {
			if (make_const_data
			    (&oc -> expression,
			     ((unsigned char *)
			      &packet -> interface -> primary_address),
			     sizeof packet -> interface -> primary_address,
			     0, 0)) {
				oc -> option =
					dhcp_universe.options [i];
				save_option (&dhcp_universe,
					     options, oc);
			}
			option_cache_dereference (&oc, "dhcpinform");
		}
		from = packet -> interface -> primary_address;
	} else {
608
		if (evaluate_option_cache (&d1, packet, (struct lease *)0,
609 610
					   packet -> options, options,
					   &global_scope, oc, MDL)) {
611 612
			if (!d1.len || d1.len != sizeof from) {
				data_string_forget (&d1, "dhcpinform");
Ted Lemon's avatar
Ted Lemon committed
613
				goto use_primary;
614
			}
Ted Lemon's avatar
Ted Lemon committed
615 616 617 618 619 620 621 622 623
			memcpy (&from, d1.data, sizeof from);
			data_string_forget (&d1, "dhcpinform");
		} else
			goto use_primary;
	}

	/* Use the subnet mask from the subnet declaration if no other
	   mask has been provided. */
	i = DHO_SUBNET_MASK;
624
	if (subnet && !lookup_option (&dhcp_universe, options, i)) {
625
		oc = (struct option_cache *)0;
Ted Lemon's avatar
Ted Lemon committed
626 627 628 629 630 631 632 633 634 635 636
		if (option_cache_allocate (&oc, "dhcpinform")) {
			if (make_const_data (&oc -> expression,
					     subnet -> netmask.iabuf,
					     subnet -> netmask.len, 0, 0)) {
				oc -> option = dhcp_universe.options [i];
				save_option (&dhcp_universe, options, oc);
			}
			option_cache_dereference (&oc, "dhcpinform");
		}
	}

637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
	/* Make an encapsulation for the NWIP suboptions if the client
	   asked for them. */
	i = DHO_NWIP_SUBOPTIONS;
	if (!lookup_option (&dhcp_universe, options, i)) {
		oc = (struct option_cache *)0;
		if (option_cache_allocate (&oc, "dhcpinform")) {
			memset (&d1, 0, sizeof d1);
			d1.data = "nwip";
			d1.len = 4;
			if (make_encapsulation (&oc -> expression, &d1)) {
				oc -> option = dhcp_universe.options [i];
				save_option (&dhcp_universe, options, oc);
			}
			option_cache_dereference (&oc, "dhcpinform");
		}
	}

Ted Lemon's avatar
Ted Lemon committed
654 655 656 657 658 659 660
	/* If we've been given a vendor option space, and there's something
	   in it, and we weren't given a vendor-encapsulated-options option,
	   then cons one up. */
	i = DHO_VENDOR_ENCAPSULATED_OPTIONS;
	j = SV_VENDOR_OPTION_SPACE;
	if (!lookup_option (&dhcp_universe, options, i) &&
	    (oc = lookup_option (&server_universe, options, j)) &&
661
	    evaluate_option_cache (&d1, packet, (struct lease *)0,
662 663
				   packet -> options, options,
				   &global_scope, oc, MDL)) {
Ted Lemon's avatar
Ted Lemon committed
664 665 666 667 668 669 670 671 672 673 674
		oc = (struct option_cache *)0;
		if (option_cache_allocate (&oc, "dhcpinform")) {
			if (make_encapsulation (&oc -> expression, &d1)) {
				oc -> option = dhcp_universe.options [i];
				save_option (&dhcp_universe, options, oc);
			}
			option_cache_dereference (&oc, "dhcpinform");
		}
		data_string_forget (&d1, "dhcpinform");
	}

675 676 677
	/* If a site option space has been specified, use that for
	   site option codes. */
	i = SV_SITE_OPTION_SPACE;
678
	if ((oc = lookup_option (&server_universe, options, i)) &&
679
	    evaluate_option_cache (&d1, packet, (struct lease *)0,
680 681
				   packet -> options, options,
				   &global_scope, oc, MDL)) {
682
		const struct universe *u;
683
		
684
		u = ((const struct universe *)
685 686 687
		     hash_lookup (&universe_hash, d1.data, d1.len));
		if (!u) {
			log_error ("unknown option space %s.", d1.data);
688
			option_state_dereference (&options, "dhcpinform");
689 690 691 692 693 694 695 696 697 698 699
			return;
		}

		options -> site_universe = u -> index;
		options -> site_code_min = 128; /* XXX */
		data_string_forget (&d1, "dhcpinform");
	} else {
		options -> site_universe = dhcp_universe.index;
		options -> site_code_min = 0; /* Trust me, it works. */
	}

700
	memset (&prl, 0, sizeof prl);
701 702 703

	/* Use the parameter list from the scope if there is one. */
	oc = lookup_option (&dhcp_universe, options,
Ted Lemon's avatar
Ted Lemon committed
704 705
			    DHO_DHCP_PARAMETER_REQUEST_LIST);

706 707 708 709
	/* Otherwise, if the client has provided a list of options
	   that it wishes returned, use it to prioritize.  Otherwise,
	   prioritize based on the default priority list. */

710
	if (!oc)
711
		oc = lookup_option (&dhcp_universe, packet -> options,
712 713 714
				    DHO_DHCP_PARAMETER_REQUEST_LIST);

	if (oc)
715
		evaluate_option_cache (&prl, packet, (struct lease *)0,
716 717
				       packet -> options, options,
				       &global_scope, oc, MDL);
Ted Lemon's avatar
Ted Lemon committed
718 719 720 721 722 723 724 725

#ifdef DEBUG_PACKET
	dump_packet (packet);
	dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
#endif

	log_info ("%s", msgbuf);

726 727 728 729
	/* Figure out the address of the boot file server. */
	raw.siaddr = from;
	if ((oc =
	     lookup_option (&server_universe, options, SV_NEXT_SERVER))) {
730
		if (evaluate_option_cache (&d1, packet, (struct lease *)0,
731 732
					   packet -> options, options,
					   &global_scope, oc, MDL)) {
733 734 735 736 737 738 739 740
			/* If there was more than one answer,
			   take the first. */
			if (d1.len >= 4 && d1.data)
				memcpy (&raw.siaddr, d1.data, 4);
			data_string_forget (&d1, "dhcpinform");
		}
	}

Ted Lemon's avatar
Ted Lemon committed
741 742
	/* Set up the option buffer... */
	outgoing.packet_length =
743
		cons_options (packet, outgoing.raw, (struct lease *)0,
744 745
			      0, packet -> options, options, &global_scope,
			      0, nulltp, 0,
Ted Lemon's avatar
Ted Lemon committed
746 747
			      prl.len ? &prl : (struct data_string *)0);
	option_state_dereference (&options, "dhcpinform");
748
	data_string_forget (&prl, "dhcpinform");
Ted Lemon's avatar
Ted Lemon committed
749 750 751 752 753 754

	/* Make sure that the packet is at least as big as a BOOTP packet. */
	if (outgoing.packet_length < BOOTP_MIN_LEN)
		outgoing.packet_length = BOOTP_MIN_LEN;

	raw.giaddr = packet -> raw -> giaddr;
755
	raw.ciaddr = packet -> raw -> ciaddr;
Ted Lemon's avatar
Ted Lemon committed
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
	raw.hlen = packet -> raw -> hlen;
	raw.htype = packet -> raw -> htype;

	raw.xid = packet -> raw -> xid;
	raw.secs = packet -> raw -> secs;
	raw.flags = packet -> raw -> flags;
	raw.hops = packet -> raw -> hops;
	raw.op = BOOTREPLY;

	/* Report what we're sending... */
	log_info ("DHCPACK to %s", inet_ntoa (raw.ciaddr));

#ifdef DEBUG_PACKET
	dump_packet (&outgoing);
	dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif

	/* Set up the common stuff... */
	to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
	to.sin_len = sizeof to;
#endif
	memset (to.sin_zero, 0, sizeof to.sin_zero);

781
	/* Use the IP address we derived for the client. */
782
	memcpy (&to.sin_addr, cip.iabuf, 4);
Ted Lemon's avatar
Ted Lemon committed
783 784 785 786 787 788 789
	to.sin_port = remote_port;

	errno = 0;
	send_packet ((fallback_interface
		      ? fallback_interface : packet -> interface),
		     &outgoing, &raw, outgoing.packet_length,
		     from, &to, (struct hardware *)0);
790 791
}

Ted Lemon's avatar
Ted Lemon committed
792
void nak_lease (packet, cip)
Ted Lemon's avatar
Ted Lemon committed
793
	struct packet *packet;
Ted Lemon's avatar
Ted Lemon committed
794
	struct iaddr *cip;
Ted Lemon's avatar
Ted Lemon committed
795 796
{
	struct sockaddr_in to;
797
	struct in_addr from;
Ted Lemon's avatar
Ted Lemon committed
798 799 800 801
	int result;
	struct dhcp_packet raw;
	unsigned char nak = DHCPNAK;
	struct packet outgoing;
802
	struct hardware hto;
803
	int i;
804
	struct data_string data;
805
	struct option_state *options = (struct option_state *)0;
806 807
	struct expression *expr;
	struct option_cache *oc = (struct option_cache *)0;
Ted Lemon's avatar
Ted Lemon committed
808

809
	option_state_allocate (&options, "nak_lease");
Ted Lemon's avatar
Ted Lemon committed
810 811 812 813 814
	memset (&outgoing, 0, sizeof outgoing);
	memset (&raw, 0, sizeof raw);
	outgoing.raw = &raw;

	/* Set DHCP_MESSAGE_TYPE to DHCPNAK */
815
	if (!option_cache_allocate (&oc, "nak_lease")) {
816
		log_error ("No memory for DHCPNAK message type.");
817
		option_state_dereference (&options, "nak_lease");
818 819 820
		return;
	}
	if (!make_const_data (&oc -> expression, &nak, sizeof nak, 0, 0)) {
821
		log_error ("No memory for expr_const expression.");
822
		option_cache_dereference (&oc, "nak_lease");
823
		option_state_dereference (&options, "nak_lease");
824 825 826
		return;
	}
	oc -> option = dhcp_universe.options [DHO_DHCP_MESSAGE_TYPE];
827
	save_option (&dhcp_universe, options, oc);
828 829
	option_cache_dereference (&oc, "nak_lease");
		     
830
	/* Set DHCP_MESSAGE to whatever the message is */
831
	if (!option_cache_allocate (&oc, "nak_lease")) {
832
		log_error ("No memory for DHCPNAK message type.");
833
		option_state_dereference (&options, "nak_lease");
834 835 836
		return;
	}
	if (!make_const_data (&oc -> expression,
Ted Lemon's avatar
Ted Lemon committed
837 838
			      (unsigned char *)dhcp_message,
			      strlen (dhcp_message), 1, 0)) {
839
		log_error ("No memory for expr_const expression.");
840
		option_cache_dereference (&oc, "nak_lease");
841
		option_state_dereference (&options, "nak_lease");
842 843 844
		return;
	}
	oc -> option = dhcp_universe.options [DHO_DHCP_MESSAGE];
845
	save_option (&dhcp_universe, options, oc);
846 847
	option_cache_dereference (&oc, "nak_lease");
		     
848
	/* Do not use the client's requested parameter list. */
849
	delete_option (&dhcp_universe, packet -> options,
850
		       DHO_DHCP_PARAMETER_REQUEST_LIST);
851

Ted Lemon's avatar
Ted Lemon committed
852
	/* Set up the option buffer... */
853
	outgoing.packet_length =
854
		cons_options (packet, outgoing.raw, (struct lease *)0,
855 856
			      0, packet -> options, options, &global_scope,
			      0, 0, 0, (struct data_string *)0);
857
	option_state_dereference (&options, "nak_lease");
Ted Lemon's avatar
Ted Lemon committed
858

859
/*	memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
860
	raw.siaddr = packet -> interface -> primary_address;
Ted Lemon's avatar
Ted Lemon committed
861
	raw.giaddr = packet -> raw -> giaddr;
862 863 864
	memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
	raw.hlen = packet -> raw -> hlen;
	raw.htype = packet -> raw -> htype;
Ted Lemon's avatar
Ted Lemon committed
865 866 867

	raw.xid = packet -> raw -> xid;
	raw.secs = packet -> raw -> secs;
868
	raw.flags = packet -> raw -> flags | htons (BOOTP_BROADCAST);
Ted Lemon's avatar
Ted Lemon committed
869 870 871
	raw.hops = packet -> raw -> hops;
	raw.op = BOOTREPLY;

872
	/* Report what we're sending... */
873
	log_info ("DHCPNAK on %s to %s via %s",
874 875 876
	      piaddr (*cip),
	      print_hw_addr (packet -> raw -> htype,
			     packet -> raw -> hlen,
877 878 879 880 881 882
			     packet -> raw -> chaddr),
	      packet -> raw -> giaddr.s_addr
	      ? inet_ntoa (packet -> raw -> giaddr)
	      : packet -> interface -> name);


883 884 885 886 887 888 889 890

#ifdef DEBUG_PACKET
	dump_packet (packet);
	dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
	dump_packet (&outgoing);
	dump_raw ((unsigned char *)&raw, outgoing.packet_length);
#endif

891
	hto.hbuf [0] = packet -> raw -> htype;
892
	hto.hlen = packet -> raw -> hlen;
893 894
	memcpy (&hto.hbuf [1], packet -> raw -> chaddr, hto.hlen);
	hto.hlen++;
895 896 897 898 899 900 901 902

	/* Set up the common stuff... */
	to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
	to.sin_len = sizeof to;
#endif
	memset (to.sin_zero, 0, sizeof to.sin_zero);

903
	from = packet -> interface -> primary_address;
904

905 906 907 908
	/* Make sure that the packet is at least as big as a BOOTP packet. */
	if (outgoing.packet_length < BOOTP_MIN_LEN)
		outgoing.packet_length = BOOTP_MIN_LEN;

909 910 911
	/* If this was gatewayed, send it back to the gateway.
	   Otherwise, broadcast it on the local network. */
	if (raw.giaddr.s_addr) {
Ted Lemon's avatar
Ted Lemon committed
912
		to.sin_addr = raw.giaddr;
913 914 915 916
		if (raw.giaddr.s_addr != INADDR_LOOPBACK)
			to.sin_port = local_port;
		else
			to.sin_port = remote_port; /* for testing. */
917

Ted Lemon's avatar
Ted Lemon committed
918 919 920 921 922 923 924
		if (fallback_interface) {
			result = send_packet (fallback_interface,
					      packet, &raw,
					      outgoing.packet_length,
					      from, &to, &hto);
			return;
		}
925
	} else {
926
		to.sin_addr = limited_broadcast;
927
		to.sin_port = remote_port;
928
	}
Ted Lemon's avatar
Ted Lemon committed
929 930

	errno = 0;
931 932
	result = send_packet (packet -> interface,
			      packet, &raw, outgoing.packet_length,
933
			      from, &to, (struct hardware *)0);
Ted Lemon's avatar
Ted Lemon committed
934 935
}

936
void ack_lease (packet, lease, offer, when, msg)
Ted Lemon's avatar
Ted Lemon committed
937 938
	struct packet *packet;
	struct lease *lease;
939
	unsigned int offer;
Ted Lemon's avatar
Ted Lemon committed
940
	TIME when;
941
	char *msg;
Ted Lemon's avatar
Ted Lemon committed
942 943
{
	struct lease lt;
944
	struct lease_state *state;
Ted Lemon's avatar
Ted Lemon committed
945
	TIME lease_time;
946