Print this page
7388 Support DHCP Client FQDN. Allow IAID/DUID for all v4.
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
|
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 + * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
24 25 *
25 26 * REQUESTING state of the client state machine.
26 27 */
27 28
28 29 #include <stdlib.h>
29 30 #include <string.h>
30 31 #include <search.h>
31 32 #include <sys/types.h>
32 33 #include <netinet/in.h>
33 34 #include <netinet/dhcp.h>
34 35 #include <netinet/udp.h>
35 36 #include <netinet/ip_var.h>
36 37 #include <netinet/udp_var.h>
37 38 #include <arpa/inet.h>
38 39 #include <dhcp_hostconf.h>
39 40 #include <dhcpagent_util.h>
40 41 #include <dhcpmsg.h>
41 42
42 43 #include "states.h"
43 44 #include "util.h"
44 45 #include "packet.h"
45 46 #include "interface.h"
46 47 #include "agent.h"
47 48
48 49 static PKT_LIST *select_best(dhcp_smach_t *);
49 50 static void request_failed(dhcp_smach_t *);
50 51 static stop_func_t stop_requesting;
51 52
52 53 /*
53 54 * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
54 55 * state. This is a separate function because a NoBinding
55 56 * response can also cause us to do this.
56 57 *
57 58 * input: dhcp_smach_t *: the state machine
58 59 * output: none
59 60 */
60 61
61 62 void
62 63 send_v6_request(dhcp_smach_t *dsmp)
63 64 {
64 65 dhcp_pkt_t *dpkt;
65 66 dhcpv6_ia_na_t d6in;
66 67
67 68 dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
68 69 (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
69 70 dsmp->dsm_serveridlen);
70 71
71 72 /* Add an IA_NA option for our controlling LIF */
|
↓ open down ↓ |
38 lines elided |
↑ open up ↑ |
72 73 d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
73 74 d6in.d6in_t1 = htonl(0);
74 75 d6in.d6in_t2 = htonl(0);
75 76 (void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
76 77 (dhcpv6_option_t *)&d6in + 1,
77 78 sizeof (d6in) - sizeof (dhcpv6_option_t));
78 79
79 80 /* Add required Option Request option */
80 81 (void) add_pkt_prl(dpkt, dsmp);
81 82
83 + /* Add FQDN if configured */
84 + (void) dhcp_add_fqdn_opt(dpkt, dsmp);
85 +
82 86 (void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
83 87 DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
84 88
85 89 /* For DHCPv6, state switch cannot fail */
86 90 (void) set_smach_state(dsmp, REQUESTING);
87 91 }
88 92
89 93 /*
90 94 * server_unicast_option(): determines the server address to use based on the
91 95 * DHCPv6 Server Unicast option present in the given
92 96 * packet.
93 97 *
94 98 * input: dhcp_smach_t *: the state machine
95 99 * PKT_LIST *: received packet (Advertisement or Reply)
96 100 * output: none
97 101 */
98 102
99 103 void
100 104 server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
101 105 {
102 106 const dhcpv6_option_t *d6o;
103 107 uint_t olen;
104 108
105 109 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
106 110 olen -= sizeof (*d6o);
107 111 /* LINTED: no consequent */
108 112 if (d6o == NULL) {
109 113 /* No Server Unicast option specified */
110 114 } else if (olen != sizeof (dsmp->dsm_server)) {
111 115 dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
112 116 "Unicast option with bad length",
113 117 pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
114 118 } else {
115 119 in6_addr_t addr;
116 120
117 121 (void) memcpy(&addr, d6o + 1, olen);
118 122 if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
119 123 dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
120 124 "to unspecified address ignored");
121 125 } else if (IN6_IS_ADDR_MULTICAST(&addr)) {
122 126 dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
123 127 "to multicast address ignored");
124 128 } else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
125 129 IN6_IS_ADDR_V4MAPPED(&addr)) {
126 130 dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
127 131 "to invalid address ignored");
128 132 } else {
129 133 dsmp->dsm_server = addr;
130 134 }
131 135 }
132 136 }
133 137
134 138 /*
135 139 * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
136 140 * if so, chooses the best one, sends a REQUEST to the
137 141 * server and registers an event handler to receive
138 142 * the ACK/NAK. This may be called by the offer timer or
139 143 * by any function that wants to check for offers after
140 144 * canceling that timer.
141 145 *
142 146 * input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
143 147 * void *: the state machine receiving OFFER packets
144 148 * output: void
145 149 */
146 150
147 151 void
148 152 dhcp_requesting(iu_tq_t *tqp, void *arg)
149 153 {
150 154 dhcp_smach_t *dsmp = arg;
151 155 dhcp_pkt_t *dpkt;
152 156 PKT_LIST *offer;
153 157 lease_t lease;
154 158 boolean_t isv6 = dsmp->dsm_isv6;
155 159
156 160 /*
157 161 * We assume here that if tqp is set, then this means we're being
158 162 * called back by the offer wait timer. If so, then drop our hold
159 163 * on the state machine. Otherwise, cancel the timer if it's running.
160 164 */
161 165 if (tqp != NULL) {
162 166 dhcpmsg(MSG_VERBOSE,
163 167 "dhcp_requesting: offer wait timer on v%d %s",
164 168 isv6 ? 6 : 4, dsmp->dsm_name);
165 169 dsmp->dsm_offer_timer = -1;
166 170 if (!verify_smach(dsmp))
167 171 return;
168 172 } else {
169 173 cancel_offer_timer(dsmp);
170 174 }
171 175
172 176 /*
173 177 * select the best OFFER; all others pitched.
174 178 */
175 179
176 180 offer = select_best(dsmp);
177 181 if (offer == NULL) {
178 182
179 183 dhcpmsg(MSG_VERBOSE,
180 184 "no OFFERs/Advertisements on %s, waiting...",
181 185 dsmp->dsm_name);
182 186
183 187 /*
184 188 * no acceptable OFFERs have come in. reschedule
185 189 * ourself for callback.
186 190 */
187 191
188 192 if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
189 193 dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
190 194
191 195 /*
192 196 * ugh. the best we can do at this point is
193 197 * revert back to INIT and wait for a user to
194 198 * restart us.
195 199 */
196 200
197 201 dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
198 202 "reschedule callback, reverting to INIT state on "
199 203 "%s", dsmp->dsm_name);
200 204
201 205 stop_pkt_retransmission(dsmp);
202 206 (void) set_smach_state(dsmp, INIT);
203 207 dsmp->dsm_dflags |= DHCP_IF_FAILED;
204 208 ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
205 209 } else {
206 210 hold_smach(dsmp);
207 211 }
208 212
209 213 return;
210 214 }
211 215
212 216 /*
213 217 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
214 218 * declines all other offers we've received. We can no longer use any
215 219 * cached offers, so we must discard them now. With DHCPv6, though,
216 220 * we're permitted to hang onto the advertisements (offers) and try
217 221 * them if the preferred one doesn't pan out.
218 222 */
219 223 if (!isv6)
220 224 free_pkt_list(&dsmp->dsm_recv_pkt_list);
221 225
222 226 /* stop collecting packets. */
223 227
224 228 stop_pkt_retransmission(dsmp);
225 229
226 230 /*
227 231 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
228 232 * If we got a BOOTP packet, go to the BOUND state now.
229 233 */
230 234 if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
231 235 free_pkt_list(&dsmp->dsm_recv_pkt_list);
232 236
233 237 if (!set_smach_state(dsmp, REQUESTING)) {
234 238 dhcp_restart(dsmp);
235 239 return;
236 240 }
237 241
238 242 if (!dhcp_bound(dsmp, offer)) {
239 243 dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
240 244 "failed for %s", dsmp->dsm_name);
241 245 dhcp_restart(dsmp);
242 246 return;
243 247 }
244 248
245 249 return;
246 250 }
247 251
248 252 if (isv6) {
249 253 const char *estr, *msg;
250 254 const dhcpv6_option_t *d6o;
251 255 uint_t olen, msglen;
252 256
253 257 /* If there's a Status Code option, print the message */
254 258 d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
255 259 &olen);
256 260 (void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
257 261 print_server_msg(dsmp, msg, msglen);
258 262
259 263 /* Copy in the Server ID (guaranteed to be present now) */
260 264 if (!save_server_id(dsmp, offer))
261 265 goto failure;
262 266
263 267 /*
264 268 * Determine how to send this message. If the Advertisement
265 269 * (offer) has the unicast option, then use the address
266 270 * specified in the option. Otherwise, send via multicast.
267 271 */
268 272 server_unicast_option(dsmp, offer);
269 273
270 274 send_v6_request(dsmp);
271 275 } else {
272 276 /* if we got a message from the server, display it. */
273 277 if (offer->opts[CD_MESSAGE] != NULL) {
274 278 print_server_msg(dsmp,
275 279 (char *)offer->opts[CD_MESSAGE]->value,
276 280 offer->opts[CD_MESSAGE]->len);
277 281 }
278 282
279 283 /*
280 284 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
281 285 * since we got here from the INIT state.
282 286 */
283 287
284 288 dpkt = init_pkt(dsmp, REQUEST);
285 289
286 290 /*
287 291 * Grab the lease out of the OFFER; we know it's valid because
288 292 * select_best() already checked. The max dhcp message size
289 293 * option is set to the interface max, minus the size of the
290 294 * udp and ip headers.
291 295 */
292 296
293 297 (void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
294 298 sizeof (lease_t));
295 299
296 300 (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
297 301 (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
298 302 htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
299 303 (void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
300 304 offer->pkt->yiaddr.s_addr);
301 305 (void) add_pkt_opt(dpkt, CD_SERVER_ID,
302 306 offer->opts[CD_SERVER_ID]->value,
303 307 offer->opts[CD_SERVER_ID]->len);
304 308
305 309 if (class_id_len != 0) {
|
↓ open down ↓ |
214 lines elided |
↑ open up ↑ |
306 310 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
307 311 class_id_len);
308 312 }
309 313 (void) add_pkt_prl(dpkt, dsmp);
310 314
311 315 /*
312 316 * dsm_reqhost was set for this state machine in
313 317 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
314 318 * host name was found
315 319 */
316 - if (dsmp->dsm_reqhost != NULL) {
320 + if (dhcp_add_fqdn_opt(dpkt, dsmp) != 0 &&
321 + dsmp->dsm_reqhost != NULL) {
317 322 (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
318 323 strlen(dsmp->dsm_reqhost));
319 324 }
320 325 (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
321 326
322 327 /*
323 328 * send out the REQUEST, trying retransmissions. either a NAK
324 329 * or too many REQUEST attempts will revert us to SELECTING.
325 330 */
326 331
327 332 if (!set_smach_state(dsmp, REQUESTING)) {
328 333 dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
329 334 "REQUESTING state; reverting to INIT on %s",
330 335 dsmp->dsm_name);
331 336 goto failure;
332 337 }
333 338
334 339 (void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
335 340 stop_requesting);
336 341 }
337 342
338 343 /* all done with the offer */
339 344 free_pkt_entry(offer);
340 345
341 346 return;
342 347
343 348 failure:
344 349 dsmp->dsm_dflags |= DHCP_IF_FAILED;
345 350 (void) set_smach_state(dsmp, INIT);
346 351 ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
347 352 free_pkt_list(&dsmp->dsm_recv_pkt_list);
348 353 }
349 354
350 355 /*
351 356 * compute_points_v6(): compute the number of "points" for a given v6
352 357 * advertisement.
353 358 *
354 359 * input: const PKT_LIST *: packet to inspect
355 360 * const dhcp_smach_t *: state machine that received the packet
356 361 * output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
357 362 */
358 363
359 364 static int
360 365 compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
361 366 {
362 367 char abuf[INET6_ADDRSTRLEN];
363 368 int points = 0;
364 369 const dhcpv6_option_t *d6o, *d6so;
365 370 uint_t olen, solen;
366 371 int i;
367 372 const char *estr, *msg;
368 373 uint_t msglen;
369 374
370 375 /*
371 376 * Look through the packet contents. Valid packets must have our
372 377 * client ID and a server ID, which has already been checked by
373 378 * dhcp_packet_lif. Bonus points for each option.
374 379 */
375 380
376 381 /* One point for having a valid message. */
377 382 points++;
378 383
379 384 /*
380 385 * Per RFC 3315, if the Advertise message says, "yes, we have no
381 386 * bananas today," then ignore the entire message. (Why it's just
382 387 * _this_ error and no other is a bit of a mystery, but a standard is a
383 388 * standard.)
384 389 */
385 390 d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
386 391 if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
387 392 DHCPV6_STAT_NOADDRS) {
388 393 dhcpmsg(MSG_INFO,
389 394 "discard advertisement from %s on %s: no address status",
390 395 inet_ntop(AF_INET6,
391 396 &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
392 397 abuf, sizeof (abuf)), dsmp->dsm_name);
393 398 return (-1);
394 399 }
395 400
396 401 /* Two points for each batch of offered IP addresses */
397 402 d6o = NULL;
398 403 while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
399 404 &olen)) != NULL) {
400 405
401 406 /*
402 407 * Note that it's possible to have "no bananas" on an
403 408 * individual IA. We must look for that here.
404 409 *
405 410 * RFC 3315 section 17.1.3 does not refer to the status code
406 411 * embedded in the IA itself. However, the TAHI test suite
407 412 * checks for this specific case. Because it's extremely
408 413 * unlikely that any usable server is going to report that it
409 414 * has no addresses on a network using DHCP for address
410 415 * assignment, we allow such messages to be dropped.
411 416 */
412 417 d6so = dhcpv6_find_option(
413 418 (const char *)d6o + sizeof (dhcpv6_ia_na_t),
414 419 olen - sizeof (dhcpv6_ia_na_t), NULL,
415 420 DHCPV6_OPT_STATUS_CODE, &solen);
416 421 if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
417 422 DHCPV6_STAT_NOADDRS)
418 423 return (-1);
419 424 points += 2;
420 425 }
421 426
422 427 /*
423 428 * Note that we drive on in the case where there are no addresses. The
424 429 * hope here is that we'll at least get some useful configuration
425 430 * information.
426 431 */
427 432
428 433 /* One point for each requested option */
429 434 for (i = 0; i < dsmp->dsm_prllen; i++) {
430 435 if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
431 436 NULL)
432 437 points++;
433 438 }
434 439
435 440 /*
436 441 * Ten points for each point of "preference." Note: the value 255 is
437 442 * special. It means "stop right now and select this server."
438 443 */
439 444 d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
440 445 if (d6o != NULL && olen == sizeof (*d6o) + 1) {
441 446 int pref = *(const uchar_t *)(d6o + 1);
442 447
443 448 if (pref == 255)
444 449 return (-2);
445 450 points += 10 * pref;
446 451 }
447 452
448 453 return (points);
449 454 }
450 455
451 456 /*
452 457 * compute_points_v4(): compute the number of "points" for a given v4 offer.
453 458 *
454 459 * input: const PKT_LIST *: packet to inspect
455 460 * const dhcp_smach_t *: state machine that received the packet
456 461 * output: int: -1 to discard, >=0 for preference.
457 462 */
458 463
459 464 static int
460 465 compute_points_v4(const PKT_LIST *pkt)
461 466 {
462 467 int points = 0;
463 468
464 469 if (pkt->opts[CD_DHCP_TYPE] == NULL) {
465 470 dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
466 471 goto valid_offer;
467 472 }
468 473
469 474 if (pkt->opts[CD_LEASE_TIME] == NULL) {
470 475 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
471 476 "time");
472 477 return (-1);
473 478 }
474 479
475 480 if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
476 481 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
477 482 "lease time");
478 483 return (-1);
479 484 }
480 485
481 486 if (pkt->opts[CD_SERVER_ID] == NULL) {
482 487 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
483 488 "id");
484 489 return (-1);
485 490 }
486 491
487 492 if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
488 493 dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
489 494 "server id");
490 495 return (-1);
491 496 }
492 497
493 498 /* valid DHCP OFFER. see if we got our parameters. */
494 499 dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
495 500 points += 30;
496 501
497 502 valid_offer:
498 503 if (pkt->rfc1048)
499 504 points += 5;
500 505
501 506 /*
502 507 * also could be faked, though more difficult because the encapsulation
503 508 * is hard to encode on a BOOTP server; plus there's not as much real
504 509 * estate in the packet for options, so it's likely this option would
505 510 * get dropped.
506 511 */
507 512
508 513 if (pkt->opts[CD_VENDOR_SPEC] != NULL)
509 514 points += 80;
510 515
511 516 if (pkt->opts[CD_SUBNETMASK] != NULL)
512 517 points++;
513 518
514 519 if (pkt->opts[CD_ROUTER] != NULL)
515 520 points++;
516 521
517 522 if (pkt->opts[CD_HOSTNAME] != NULL)
518 523 points += 5;
519 524
520 525 return (points);
521 526 }
522 527
523 528 /*
524 529 * select_best(): selects the best offer from a list of IPv4 OFFER packets or
525 530 * DHCPv6 Advertise packets.
526 531 *
527 532 * input: dhcp_smach_t *: state machine with enqueued offers
528 533 * output: PKT_LIST *: the best packet, or NULL if none are acceptable
529 534 */
530 535
531 536 static PKT_LIST *
532 537 select_best(dhcp_smach_t *dsmp)
533 538 {
534 539 PKT_LIST *current = dsmp->dsm_recv_pkt_list;
535 540 PKT_LIST *next, *best = NULL;
536 541 int points, best_points = -1;
537 542
538 543 /*
539 544 * pick out the best offer. point system.
540 545 * what's important for IPv4?
541 546 *
542 547 * 0) DHCP (30 points)
543 548 * 1) no option overload
544 549 * 2) encapsulated vendor option (80 points)
545 550 * 3) non-null sname and siaddr fields
546 551 * 4) non-null file field
547 552 * 5) hostname (5 points)
548 553 * 6) subnetmask (1 point)
549 554 * 7) router (1 point)
550 555 */
551 556
552 557 for (; current != NULL; current = next) {
553 558 next = current->next;
554 559
555 560 points = current->isv6 ?
556 561 compute_points_v6(current, dsmp) :
557 562 compute_points_v4(current);
558 563
559 564 /*
560 565 * Just discard any unacceptable entries we encounter.
561 566 */
562 567 if (points == -1) {
563 568 remque(current);
564 569 free_pkt_entry(current);
565 570 continue;
566 571 }
567 572
568 573 dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
569 574
570 575 /* Special case: stop now and select */
571 576 if (points == -2) {
572 577 best = current;
573 578 break;
574 579 }
575 580
576 581 if (points >= best_points) {
577 582 best_points = points;
578 583 best = current;
579 584 }
580 585 }
581 586
582 587 if (best != NULL) {
583 588 dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
584 589 remque(best);
585 590 } else {
586 591 dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
587 592 }
588 593
589 594 return (best);
590 595 }
591 596
592 597 /*
593 598 * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
594 599 * current state. If we're renewing or rebinding, the ACK
595 600 * must be for the same address and must have a new lease
596 601 * time. If it's a NAK, then our cache is garbage, and we
597 602 * must restart. Finally, call dhcp_bound on accepted
598 603 * ACKs.
599 604 *
600 605 * input: dhcp_smach_t *: the state machine to handle the ACK/NAK
601 606 * PKT_LIST *: the ACK/NAK message
602 607 * output: void
603 608 */
604 609
605 610 static void
606 611 accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
607 612 {
608 613 /* Account for received and processed messages */
609 614 dsmp->dsm_received++;
610 615
611 616 if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
612 617 if (dsmp->dsm_state != INFORM_SENT &&
613 618 dsmp->dsm_state != INFORMATION &&
614 619 (plp->opts[CD_LEASE_TIME] == NULL ||
615 620 plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
616 621 dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
617 622 "%s missing mandatory lease option, ignored",
618 623 dsmp->dsm_name);
619 624 dsmp->dsm_bad_offers++;
620 625 free_pkt_entry(plp);
621 626 return;
622 627 }
623 628 if ((dsmp->dsm_state == RENEWING ||
624 629 dsmp->dsm_state == REBINDING) &&
625 630 dsmp->dsm_leases->dl_lifs->lif_addr !=
626 631 plp->pkt->yiaddr.s_addr) {
627 632 dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
628 633 "packet has a different IP address (%s), ignored",
629 634 inet_ntoa(plp->pkt->yiaddr));
630 635 dsmp->dsm_bad_offers++;
631 636 free_pkt_entry(plp);
632 637 return;
633 638 }
634 639 }
635 640
636 641 /*
637 642 * looks good; cancel the retransmission timer and unregister
638 643 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
639 644 */
640 645
641 646 stop_pkt_retransmission(dsmp);
642 647
643 648 if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
644 649 dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
645 650 dsmp->dsm_name);
646 651 dsmp->dsm_bad_offers++;
647 652 free_pkt_entry(plp);
648 653 dhcp_restart(dsmp);
649 654
650 655 /*
651 656 * remove any bogus cached configuration we might have
652 657 * around (right now would only happen if we got here
653 658 * from INIT_REBOOT).
654 659 */
655 660
656 661 (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
657 662 return;
658 663 }
659 664
660 665 if (plp->opts[CD_SERVER_ID] == NULL ||
661 666 plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
662 667 dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
663 668 "server id on %s", dsmp->dsm_name);
664 669 dsmp->dsm_bad_offers++;
665 670 free_pkt_entry(plp);
666 671 dhcp_restart(dsmp);
667 672 return;
668 673 }
669 674
670 675 if (plp->opts[CD_MESSAGE] != NULL) {
671 676 print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
672 677 plp->opts[CD_MESSAGE]->len);
673 678 }
674 679
675 680 dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
676 681 if (!dhcp_bound(dsmp, plp)) {
677 682 dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
678 683 "for %s", dsmp->dsm_name);
679 684 dhcp_restart(dsmp);
680 685 }
681 686 }
682 687
683 688 /*
684 689 * accept_v6_message(): determine what to do with a DHCPv6 message based on the
685 690 * current state.
686 691 *
687 692 * input: dhcp_smach_t *: the state machine to handle the message
688 693 * PKT_LIST *: the DHCPv6 message
689 694 * const char *: type of message (for logging)
690 695 * uchar_t: type of message (extracted from packet)
691 696 * output: void
692 697 */
693 698
694 699 static void
695 700 accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
696 701 uchar_t recv_type)
697 702 {
698 703 const dhcpv6_option_t *d6o;
699 704 uint_t olen;
700 705 const char *estr, *msg;
701 706 uint_t msglen;
702 707 int status;
703 708
704 709 /* Account for received and processed messages */
705 710 dsmp->dsm_received++;
706 711
707 712 /* We don't yet support Reconfigure at all. */
708 713 if (recv_type == DHCPV6_MSG_RECONFIGURE) {
709 714 dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
710 715 "on %s", dsmp->dsm_name);
711 716 free_pkt_entry(plp);
712 717 return;
713 718 }
714 719
715 720 /*
716 721 * All valid DHCPv6 messages must have our Client ID specified.
717 722 */
718 723 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
719 724 olen -= sizeof (*d6o);
720 725 if (d6o == NULL || olen != dsmp->dsm_cidlen ||
721 726 memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
722 727 dhcpmsg(MSG_VERBOSE,
723 728 "accept_v6_message: discarded %s on %s: %s Client ID",
724 729 pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
725 730 free_pkt_entry(plp);
726 731 return;
727 732 }
728 733
729 734 /*
730 735 * All valid DHCPv6 messages must have a Server ID specified.
731 736 *
732 737 * If this is a Reply and it's not in response to Solicit, Confirm,
733 738 * Rebind, or Information-Request, then it must also match the Server
734 739 * ID we're expecting.
735 740 *
736 741 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
737 742 * cases, the Server ID needs to be saved. This is done inside of
738 743 * dhcp_bound().
739 744 */
740 745 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
741 746 if (d6o == NULL) {
742 747 dhcpmsg(MSG_DEBUG,
743 748 "accept_v6_message: discarded %s on %s: no Server ID",
744 749 pname, dsmp->dsm_name);
745 750 free_pkt_entry(plp);
746 751 return;
747 752 }
748 753 if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
749 754 dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
750 755 dsmp->dsm_state != INFORM_SENT) {
751 756 olen -= sizeof (*d6o);
752 757 if (olen != dsmp->dsm_serveridlen ||
753 758 memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
754 759 dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
755 760 "%s: wrong Server ID", pname, dsmp->dsm_name);
756 761 free_pkt_entry(plp);
757 762 return;
758 763 }
759 764 }
760 765
761 766 /*
762 767 * Break out of the switch if the input message needs to be discarded.
763 768 * Return from the function if the message has been enqueued or
764 769 * consumed.
765 770 */
766 771 switch (dsmp->dsm_state) {
767 772 case SELECTING:
768 773 /* A Reply message signifies a Rapid-Commit. */
769 774 if (recv_type == DHCPV6_MSG_REPLY) {
770 775 if (dhcpv6_pkt_option(plp, NULL,
771 776 DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
772 777 dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
773 778 "on %s lacks Rapid-Commit; ignoring",
774 779 dsmp->dsm_name);
775 780 break;
776 781 }
777 782 dhcpmsg(MSG_VERBOSE,
778 783 "accept_v6_message: rapid-commit Reply on %s",
779 784 dsmp->dsm_name);
780 785 cancel_offer_timer(dsmp);
781 786 goto rapid_commit;
782 787 }
783 788
784 789 /* Otherwise, we're looking for Advertisements. */
785 790 if (recv_type != DHCPV6_MSG_ADVERTISE)
786 791 break;
787 792
788 793 /*
789 794 * Special case: if this advertisement has preference 255, then
790 795 * we must stop right now and select this server.
791 796 */
792 797 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
793 798 &olen);
794 799 if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
795 800 *(const uchar_t *)(d6o + 1) == 255) {
796 801 pkt_smach_enqueue(dsmp, plp);
797 802 dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
798 803 " immediate Request on %s", dsmp->dsm_name);
799 804 dhcp_requesting(NULL, dsmp);
800 805 } else {
801 806 pkt_smach_enqueue(dsmp, plp);
802 807 }
803 808 return;
804 809
805 810 case PRE_BOUND:
806 811 case BOUND:
807 812 /*
808 813 * Not looking for anything in these states. (If we
809 814 * implemented reconfigure, that might go here.)
810 815 */
811 816 break;
812 817
813 818 case REQUESTING:
814 819 case INIT_REBOOT:
815 820 case RENEWING:
816 821 case REBINDING:
817 822 case INFORM_SENT:
818 823 /*
819 824 * We're looking for Reply messages.
820 825 */
821 826 if (recv_type != DHCPV6_MSG_REPLY)
822 827 break;
823 828 dhcpmsg(MSG_VERBOSE,
824 829 "accept_v6_message: received Reply message on %s",
825 830 dsmp->dsm_name);
826 831 rapid_commit:
827 832 /*
828 833 * Extract the status code option. If one is present and the
829 834 * request failed, then try to go to another advertisement in
830 835 * the list or restart the selection machinery.
831 836 */
832 837 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
833 838 &olen);
834 839 status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
835 840 /*
836 841 * Check for the UseMulticast status code. If this is present,
837 842 * and if we were actually using unicast, then drop back and
838 843 * try again. If we weren't using unicast, then just pretend
839 844 * we never saw this message -- the peer is confused. (TAHI
840 845 * does this.)
841 846 */
842 847 if (status == DHCPV6_STAT_USEMCAST) {
843 848 if (IN6_IS_ADDR_MULTICAST(
844 849 &dsmp->dsm_send_dest.v6.sin6_addr)) {
845 850 break;
846 851 } else {
847 852 free_pkt_entry(plp);
848 853 dsmp->dsm_send_dest.v6.sin6_addr =
849 854 ipv6_all_dhcp_relay_and_servers;
850 855 retransmit_now(dsmp);
851 856 return;
852 857 }
853 858 }
854 859 print_server_msg(dsmp, msg, msglen);
855 860 /*
856 861 * We treat NoBinding at the top level as "success." Granted,
857 862 * this doesn't make much sense, but the TAHI test suite does
858 863 * this. NoBinding really only makes sense in the context of a
859 864 * specific IA, as it refers to the GUID:IAID binding, so
860 865 * ignoring it at the top level is safe.
861 866 */
862 867 if (status == DHCPV6_STAT_SUCCESS ||
863 868 status == DHCPV6_STAT_NOBINDING) {
864 869 if (dhcp_bound(dsmp, plp)) {
865 870 /*
866 871 * dhcp_bound will stop retransmission on
867 872 * success, if that's called for.
868 873 */
869 874 server_unicast_option(dsmp, plp);
870 875 } else {
871 876 stop_pkt_retransmission(dsmp);
872 877 dhcpmsg(MSG_WARNING, "accept_v6_message: "
873 878 "dhcp_bound failed for %s", dsmp->dsm_name);
874 879 (void) remove_hostconf(dsmp->dsm_name,
875 880 dsmp->dsm_isv6);
876 881 dhcp_restart(dsmp);
877 882 }
878 883 } else {
879 884 dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
880 885 estr);
881 886 stop_pkt_retransmission(dsmp);
882 887 free_pkt_entry(plp);
883 888 if (dsmp->dsm_state == INFORM_SENT) {
884 889 (void) set_smach_state(dsmp, INIT);
885 890 ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
886 891 } else {
887 892 (void) remove_hostconf(dsmp->dsm_name,
888 893 dsmp->dsm_isv6);
889 894 request_failed(dsmp);
890 895 }
891 896 }
892 897 return;
893 898
894 899 case DECLINING:
895 900 /*
896 901 * We're looking for Reply messages.
897 902 */
898 903 if (recv_type != DHCPV6_MSG_REPLY)
899 904 break;
900 905 stop_pkt_retransmission(dsmp);
901 906 /*
902 907 * Extract the status code option. Note that it's not a
903 908 * failure if the server reports an error.
904 909 */
905 910 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
906 911 &olen);
907 912 if (dhcpv6_status_code(d6o, olen, &estr, &msg,
908 913 &msglen) == DHCPV6_STAT_SUCCESS) {
909 914 print_server_msg(dsmp, msg, msglen);
910 915 } else {
911 916 dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
912 917 estr);
913 918 }
914 919 free_pkt_entry(plp);
915 920 if (dsmp->dsm_leases == NULL) {
916 921 dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
917 922 "leases left", dsmp->dsm_name);
918 923 dhcp_restart(dsmp);
919 924 } else if (dsmp->dsm_lif_wait == 0) {
920 925 (void) set_smach_state(dsmp, BOUND);
921 926 } else {
922 927 (void) set_smach_state(dsmp, PRE_BOUND);
923 928 }
924 929 return;
925 930
926 931 case RELEASING:
927 932 /*
928 933 * We're looking for Reply messages.
929 934 */
930 935 if (recv_type != DHCPV6_MSG_REPLY)
931 936 break;
932 937 stop_pkt_retransmission(dsmp);
933 938 /*
934 939 * Extract the status code option.
935 940 */
936 941 d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
937 942 &olen);
938 943 if (dhcpv6_status_code(d6o, olen, &estr, &msg,
939 944 &msglen) == DHCPV6_STAT_SUCCESS) {
940 945 print_server_msg(dsmp, msg, msglen);
941 946 } else {
942 947 dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
943 948 estr);
944 949 }
945 950 free_pkt_entry(plp);
946 951 finished_smach(dsmp, DHCP_IPC_SUCCESS);
947 952 return;
948 953 }
949 954
950 955 /*
951 956 * Break from above switch means that the message must be discarded.
952 957 */
953 958 dhcpmsg(MSG_VERBOSE,
954 959 "accept_v6_message: discarded v6 %s on %s; state %s",
955 960 pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
956 961 free_pkt_entry(plp);
957 962 }
958 963
959 964 /*
960 965 * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
961 966 * global socket -- broadcast packets for IPv4, all
962 967 * packets for DHCPv6.
963 968 *
964 969 * input: iu_eh_t *: unused
965 970 * int: the global file descriptor the ACK/NAK arrived on
966 971 * short: unused
967 972 * iu_event_id_t: unused
968 973 * void *: unused
969 974 * output: void
970 975 */
971 976
972 977 /* ARGSUSED */
973 978 void
974 979 dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
975 980 void *arg)
976 981 {
977 982 PKT_LIST *plp;
978 983 dhcp_pif_t *pif;
979 984 uchar_t recv_type;
980 985 const char *pname;
981 986 uint_t xid;
982 987 dhcp_smach_t *dsmp;
983 988 boolean_t isv6 = (fd == v6_sock_fd);
984 989 struct sockaddr_in sin;
985 990 const char *reason;
986 991 size_t sinlen = sizeof (sin);
987 992 int sock;
988 993
989 994 plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
990 995 if (plp == NULL)
991 996 return;
992 997
993 998 recv_type = pkt_recv_type(plp);
994 999 pname = pkt_type_to_string(recv_type, isv6);
995 1000
996 1001 /*
997 1002 * Find the corresponding state machine and pif.
998 1003 *
999 1004 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
1000 1005 * any transaction, and thus we would need to search on transaction ID
1001 1006 * zero (all state machines) to find the match. However, Reconfigure
1002 1007 * is not yet supported.
1003 1008 */
1004 1009 xid = pkt_get_xid(plp->pkt, isv6);
1005 1010
1006 1011 for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
1007 1012 dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
1008 1013 pif = dsmp->dsm_lif->lif_pif;
1009 1014 if (pif->pif_index == plp->ifindex ||
1010 1015 pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex)
1011 1016 break;
1012 1017 }
1013 1018
1014 1019 if (dsmp == NULL) {
1015 1020 dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet"
1016 1021 " on ifindex %d: unknown state machine", isv6 ? 6 : 4,
1017 1022 pname, plp->ifindex);
1018 1023 free_pkt_entry(plp);
1019 1024 return;
1020 1025 }
1021 1026
1022 1027 if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1023 1028 reason = "not ACK or NAK";
1024 1029 goto drop;
1025 1030 }
1026 1031
1027 1032 /*
1028 1033 * For IPv4, most packets will be handled by dhcp_packet_lif(). The
1029 1034 * only exceptions are broadcast packets sent when lif_sock_ip_fd has
1030 1035 * bound to something other than INADDR_ANY.
1031 1036 */
1032 1037 if (!isv6) {
1033 1038 sock = dsmp->dsm_lif->lif_sock_ip_fd;
1034 1039
1035 1040 if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
1036 1041 sin.sin_addr.s_addr == INADDR_ANY) {
1037 1042 reason = "handled by lif_sock_ip_fd";
1038 1043 goto drop;
1039 1044 }
1040 1045 }
1041 1046
1042 1047 /*
1043 1048 * We've got a packet; make sure it's acceptable and cancel the REQUEST
1044 1049 * retransmissions.
1045 1050 */
1046 1051 if (isv6)
1047 1052 accept_v6_message(dsmp, plp, pname, recv_type);
1048 1053 else
1049 1054 accept_v4_acknak(dsmp, plp);
1050 1055 return;
1051 1056 drop:
1052 1057 dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
1053 1058 "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
1054 1059 reason);
1055 1060 free_pkt_entry(plp);
1056 1061 }
1057 1062
1058 1063 /*
1059 1064 * request_failed(): Attempt to request an address has failed. Take an
1060 1065 * appropriate action.
1061 1066 *
1062 1067 * input: dhcp_smach_t *: state machine that has failed
1063 1068 * output: void
1064 1069 */
1065 1070
1066 1071 static void
1067 1072 request_failed(dhcp_smach_t *dsmp)
1068 1073 {
1069 1074 PKT_LIST *offer;
1070 1075
1071 1076 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1072 1077 if ((offer = select_best(dsmp)) != NULL) {
1073 1078 insque(offer, &dsmp->dsm_recv_pkt_list);
1074 1079 dhcp_requesting(NULL, dsmp);
1075 1080 } else {
1076 1081 dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
1077 1082 dsmp->dsm_name);
1078 1083 dhcp_selecting(dsmp);
1079 1084 }
1080 1085 }
1081 1086
1082 1087 /*
1083 1088 * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
1084 1089 * a given logical interface for IPv4 (only).
1085 1090 *
1086 1091 * input: iu_eh_t *: unused
1087 1092 * int: the file descriptor the packet arrived on
1088 1093 * short: unused
1089 1094 * iu_event_id_t: the id of this event callback with the handler
1090 1095 * void *: pointer to logical interface receiving message
1091 1096 * output: void
1092 1097 */
1093 1098
1094 1099 /* ARGSUSED */
1095 1100 void
1096 1101 dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
1097 1102 void *arg)
1098 1103 {
1099 1104 dhcp_lif_t *lif = arg;
1100 1105 PKT_LIST *plp;
1101 1106 uchar_t recv_type;
1102 1107 const char *pname;
1103 1108 uint_t xid;
1104 1109 dhcp_smach_t *dsmp;
1105 1110
1106 1111 if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
1107 1112 return;
1108 1113
1109 1114 recv_type = pkt_recv_type(plp);
1110 1115 pname = pkt_type_to_string(recv_type, B_FALSE);
1111 1116
1112 1117 if (!pkt_v4_match(recv_type,
1113 1118 DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
1114 1119 dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
1115 1120 "received via LIF %s", pname, lif->lif_name);
1116 1121 free_pkt_entry(plp);
1117 1122 return;
1118 1123 }
1119 1124
1120 1125 /*
1121 1126 * Find the corresponding state machine.
1122 1127 */
1123 1128 xid = pkt_get_xid(plp->pkt, B_FALSE);
1124 1129 for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
1125 1130 dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
1126 1131 if (dsmp->dsm_lif == lif)
1127 1132 break;
1128 1133 }
1129 1134
1130 1135 if (dsmp == NULL)
1131 1136 goto drop;
1132 1137
1133 1138 if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1134 1139 /*
1135 1140 * We've got an ACK/NAK; make sure it's acceptable and cancel
1136 1141 * the REQUEST retransmissions.
1137 1142 */
1138 1143 accept_v4_acknak(dsmp, plp);
1139 1144 } else {
1140 1145 if (is_bound_state(dsmp->dsm_state))
1141 1146 goto drop;
1142 1147 /*
1143 1148 * Must be an OFFER or a BOOTP message: enqueue it for later
1144 1149 * processing by select_best().
1145 1150 */
1146 1151 pkt_smach_enqueue(dsmp, plp);
1147 1152 }
1148 1153 return;
1149 1154 drop:
1150 1155 dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
1151 1156 "%x received via LIF %s; %s", pname, xid, lif->lif_name,
1152 1157 dsmp == NULL ? "unknown state machine" : "bound");
1153 1158 free_pkt_entry(plp);
1154 1159 }
1155 1160
1156 1161 /*
1157 1162 * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only
1158 1163 * if we're leasing addresses. Doesn't restart for information-
1159 1164 * only interfaces.
1160 1165 *
1161 1166 * input: dhcp_smach_t *: the state machine to restart DHCP on
1162 1167 * output: void
1163 1168 */
1164 1169
1165 1170 void
1166 1171 dhcp_restart(dhcp_smach_t *dsmp)
1167 1172 {
1168 1173 if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
1169 1174 return;
1170 1175
1171 1176 /*
1172 1177 * As we're returning to INIT state, we need to discard any leases we
1173 1178 * may have, and (for v4) canonize the LIF. There's a bit of tension
1174 1179 * between keeping around a possibly still working address, and obeying
1175 1180 * the RFCs. A more elaborate design would be to mark the addresses as
1176 1181 * DEPRECATED, and then start a removal timer. Such a design would
1177 1182 * probably compromise testing.
1178 1183 */
1179 1184 deprecate_leases(dsmp);
1180 1185
1181 1186 if (!set_start_timer(dsmp)) {
1182 1187 dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
1183 1188 "reverting to INIT state on %s", dsmp->dsm_name);
1184 1189
1185 1190 (void) set_smach_state(dsmp, INIT);
1186 1191 dsmp->dsm_dflags |= DHCP_IF_FAILED;
1187 1192 ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
1188 1193 } else {
1189 1194 dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
1190 1195 dsmp->dsm_name);
1191 1196 }
1192 1197 }
1193 1198
1194 1199 /*
1195 1200 * stop_requesting(): decides when to stop retransmitting REQUESTs
1196 1201 *
1197 1202 * input: dhcp_smach_t *: the state machine REQUESTs are being sent from
1198 1203 * unsigned int: the number of REQUESTs sent so far
1199 1204 * output: boolean_t: B_TRUE if retransmissions should stop
1200 1205 */
1201 1206
1202 1207 static boolean_t
1203 1208 stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
1204 1209 {
1205 1210 uint_t maxreq;
1206 1211
1207 1212 maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
1208 1213 if (n_requests >= maxreq) {
1209 1214
1210 1215 dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
1211 1216 dsmp->dsm_name);
1212 1217
1213 1218 request_failed(dsmp);
1214 1219 return (B_TRUE);
1215 1220 } else {
1216 1221 return (B_FALSE);
1217 1222 }
1218 1223 }
|
↓ open down ↓ |
892 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX