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/packet.c
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.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
26 27 #include <string.h>
27 28 #include <sys/types.h>
28 29 #include <stdlib.h>
29 30 #include <dhcpmsg.h>
30 31 #include <stddef.h>
31 32 #include <assert.h>
32 33 #include <search.h>
33 34 #include <alloca.h>
34 35 #include <limits.h>
35 36 #include <stropts.h>
36 37 #include <netinet/dhcp6.h>
37 38 #include <arpa/inet.h>
38 39 #include <sys/sysmacros.h>
39 40 #include <sys/sockio.h>
40 41 #include <inet/ip6_asp.h>
41 42
42 43 #include "states.h"
43 44 #include "interface.h"
44 45 #include "agent.h"
45 46 #include "packet.h"
46 47 #include "util.h"
47 48
48 49 int v6_sock_fd = -1;
49 50 int v4_sock_fd = -1;
50 51
51 52 const in6_addr_t ipv6_all_dhcp_relay_and_servers = {
52 53 0xff, 0x02, 0x00, 0x00,
53 54 0x00, 0x00, 0x00, 0x00,
54 55 0x00, 0x00, 0x00, 0x00,
55 56 0x00, 0x01, 0x00, 0x02
56 57 };
57 58
58 59 /*
59 60 * We have our own version of this constant because dhcpagent is compiled with
60 61 * -lxnet.
61 62 */
62 63 const in6_addr_t my_in6addr_any = IN6ADDR_ANY_INIT;
63 64
64 65 static void retransmit(iu_tq_t *, void *);
65 66 static void next_retransmission(dhcp_smach_t *, boolean_t, boolean_t);
66 67 static boolean_t send_pkt_internal(dhcp_smach_t *);
67 68
68 69 /*
69 70 * pkt_send_type(): returns an integer representing the packet's type; only
70 71 * for use with outbound packets.
71 72 *
72 73 * input: dhcp_pkt_t *: the packet to examine
73 74 * output: uchar_t: the packet type (0 if unknown)
74 75 */
75 76
76 77 static uchar_t
77 78 pkt_send_type(const dhcp_pkt_t *dpkt)
78 79 {
79 80 const uchar_t *option;
80 81
81 82 if (dpkt->pkt_isv6)
82 83 return (((const dhcpv6_message_t *)dpkt->pkt)->d6m_msg_type);
83 84
84 85 /*
85 86 * this is a little dirty but it should get the job done.
86 87 * assumes that the type is in the statically allocated part
87 88 * of the options field.
88 89 */
89 90
90 91 option = dpkt->pkt->options;
91 92 for (;;) {
92 93 if (*option == CD_PAD) {
93 94 option++;
94 95 continue;
95 96 }
96 97 if (*option == CD_END ||
97 98 option + 2 - dpkt->pkt->options >=
98 99 sizeof (dpkt->pkt->options))
99 100 return (0);
100 101 if (*option == CD_DHCP_TYPE)
101 102 break;
102 103 option++;
103 104 option += *option + 1;
104 105 }
105 106
106 107 return (option[2]);
107 108 }
108 109
109 110 /*
110 111 * pkt_recv_type(): returns an integer representing the packet's type; only
111 112 * for use with inbound packets.
112 113 *
113 114 * input: dhcp_pkt_t *: the packet to examine
114 115 * output: uchar_t: the packet type (0 if unknown)
115 116 */
116 117
117 118 uchar_t
118 119 pkt_recv_type(const PKT_LIST *plp)
119 120 {
120 121 if (plp->isv6)
121 122 return (((const dhcpv6_message_t *)plp->pkt)->d6m_msg_type);
122 123 else if (plp->opts[CD_DHCP_TYPE] != NULL)
123 124 return (plp->opts[CD_DHCP_TYPE]->value[0]);
124 125 else
125 126 return (0);
126 127 }
127 128
128 129 /*
129 130 * pkt_get_xid(): returns transaction ID from a DHCP packet.
130 131 *
131 132 * input: const PKT *: the packet to examine
132 133 * output: uint_t: the transaction ID (0 if unknown)
133 134 */
134 135
135 136 uint_t
136 137 pkt_get_xid(const PKT *pkt, boolean_t isv6)
137 138 {
138 139 if (pkt == NULL)
139 140 return (0);
140 141 if (isv6)
141 142 return (DHCPV6_GET_TRANSID((const dhcpv6_message_t *)pkt));
142 143 else
143 144 return (pkt->xid);
144 145 }
145 146
146 147 /*
147 148 * init_pkt(): initializes and returns a packet of a given type
148 149 *
149 150 * input: dhcp_smach_t *: the state machine that will send the packet
150 151 * uchar_t: the packet type (DHCP message type)
151 152 * output: dhcp_pkt_t *: a pointer to the initialized packet; may be NULL
152 153 */
153 154
154 155 dhcp_pkt_t *
155 156 init_pkt(dhcp_smach_t *dsmp, uchar_t type)
156 157 {
157 158 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
158 159 dhcp_lif_t *lif = dsmp->dsm_lif;
159 160 dhcp_pif_t *pif = lif->lif_pif;
160 161 uint_t mtu = lif->lif_max;
161 162 uint32_t xid;
162 163 boolean_t isv6;
163 164
164 165 dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
165 166
166 167 /*
167 168 * Since multiple dhcp leases may be maintained over the same pif
168 169 * (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
169 170 *
170 171 * Note that transaction ID zero is intentionally never assigned.
171 172 * That's used to represent "no ID." Also note that transaction IDs
172 173 * are only 24 bits long in DHCPv6.
173 174 */
174 175
175 176 do {
176 177 xid = mrand48();
177 178 if (isv6)
178 179 xid &= 0xFFFFFF;
179 180 } while (xid == 0 ||
180 181 lookup_smach_by_xid(xid, NULL, dpkt->pkt_isv6) != NULL);
181 182
182 183 if (isv6) {
183 184 dhcpv6_message_t *v6;
184 185
185 186 if (mtu != dpkt->pkt_max_len &&
186 187 (v6 = realloc(dpkt->pkt, mtu)) != NULL) {
187 188 /* LINTED: alignment known to be correct */
188 189 dpkt->pkt = (PKT *)v6;
189 190 dpkt->pkt_max_len = mtu;
190 191 }
191 192
192 193 if (sizeof (*v6) > dpkt->pkt_max_len) {
193 194 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v6 pkt: %u",
194 195 mtu);
195 196 return (NULL);
196 197 }
197 198
198 199 v6 = (dhcpv6_message_t *)dpkt->pkt;
199 200 dpkt->pkt_cur_len = sizeof (*v6);
200 201
201 202 (void) memset(v6, 0, dpkt->pkt_max_len);
202 203
203 204 v6->d6m_msg_type = type;
204 205 DHCPV6_SET_TRANSID(v6, xid);
205 206
206 207 if (dsmp->dsm_cidlen > 0 &&
207 208 add_pkt_opt(dpkt, DHCPV6_OPT_CLIENTID, dsmp->dsm_cid,
208 209 dsmp->dsm_cidlen) == NULL) {
209 210 dhcpmsg(MSG_WARNING,
210 211 "init_pkt: cannot insert client ID");
211 212 return (NULL);
212 213 }
213 214
214 215 /* For v6, time starts with the creation of a transaction */
215 216 dsmp->dsm_neg_hrtime = gethrtime();
216 217 dsmp->dsm_newstart_monosec = monosec();
217 218 } else {
218 219 static uint8_t bootmagic[] = BOOTMAGIC;
219 220 PKT *v4;
220 221
221 222 if (mtu != dpkt->pkt_max_len &&
222 223 (v4 = realloc(dpkt->pkt, mtu)) != NULL) {
223 224 dpkt->pkt = v4;
224 225 dpkt->pkt_max_len = mtu;
225 226 }
226 227
227 228 if (offsetof(PKT, options) > dpkt->pkt_max_len) {
228 229 dhcpmsg(MSG_ERR, "init_pkt: cannot allocate v4 pkt: %u",
229 230 mtu);
230 231 return (NULL);
231 232 }
232 233
233 234 v4 = dpkt->pkt;
234 235 dpkt->pkt_cur_len = offsetof(PKT, options);
235 236
236 237 (void) memset(v4, 0, dpkt->pkt_max_len);
237 238 (void) memcpy(v4->cookie, bootmagic, sizeof (bootmagic));
238 239 if (pif->pif_hwlen <= sizeof (v4->chaddr)) {
239 240 v4->hlen = pif->pif_hwlen;
240 241 (void) memcpy(v4->chaddr, pif->pif_hwaddr,
241 242 pif->pif_hwlen);
242 243 } else {
243 244 /*
244 245 * The mac address does not fit in the chaddr
245 246 * field, thus it can not be sent to the server,
246 247 * thus server can not unicast the reply. Per
247 248 * RFC 2131 4.4.1, client can set this bit in
248 249 * DISCOVER/REQUEST. If the client is already
249 250 * in a bound state, do not set this bit, as it
250 251 * can respond to unicast responses from server
251 252 * using the 'ciaddr' address.
252 253 */
253 254 if (type == DISCOVER || (type == REQUEST &&
254 255 !is_bound_state(dsmp->dsm_state)))
255 256 v4->flags = htons(BCAST_MASK);
256 257 }
257 258
258 259 v4->xid = xid;
259 260 v4->op = BOOTREQUEST;
260 261 v4->htype = pif->pif_hwtype;
261 262
262 263 if (add_pkt_opt(dpkt, CD_DHCP_TYPE, &type, 1) == NULL) {
263 264 dhcpmsg(MSG_WARNING,
264 265 "init_pkt: cannot set DHCP packet type");
265 266 return (NULL);
266 267 }
267 268
268 269 if (dsmp->dsm_cidlen > 0 &&
269 270 add_pkt_opt(dpkt, CD_CLIENT_ID, dsmp->dsm_cid,
270 271 dsmp->dsm_cidlen) == NULL) {
271 272 dhcpmsg(MSG_WARNING,
272 273 "init_pkt: cannot insert client ID");
273 274 return (NULL);
274 275 }
275 276 }
276 277
277 278 return (dpkt);
278 279 }
279 280
280 281 /*
281 282 * remove_pkt_opt(): removes the first instance of an option from a dhcp_pkt_t
282 283 *
283 284 * input: dhcp_pkt_t *: the packet to remove the option from
284 285 * uint_t: the type of option being added
285 286 * output: boolean_t: B_TRUE on success, B_FALSE on failure
286 287 * note: currently does not work with DHCPv6 suboptions, or to remove
287 288 * arbitrary option instances.
288 289 */
289 290
290 291 boolean_t
291 292 remove_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type)
292 293 {
293 294 uchar_t *raw_pkt, *raw_end, *next;
294 295 uint_t len;
295 296
296 297 raw_pkt = (uchar_t *)dpkt->pkt;
297 298 raw_end = raw_pkt + dpkt->pkt_cur_len;
298 299 if (dpkt->pkt_isv6) {
299 300 dhcpv6_option_t d6o;
300 301
301 302 raw_pkt += sizeof (dhcpv6_message_t);
302 303
303 304 opt_type = htons(opt_type);
304 305 while (raw_pkt + sizeof (d6o) <= raw_end) {
305 306 (void) memcpy(&d6o, raw_pkt, sizeof (d6o));
306 307 len = ntohs(d6o.d6o_len) + sizeof (d6o);
307 308 if (len > raw_end - raw_pkt)
308 309 break;
309 310 next = raw_pkt + len;
310 311 if (d6o.d6o_code == opt_type) {
311 312 if (next < raw_end) {
312 313 (void) memmove(raw_pkt, next,
313 314 raw_end - next);
314 315 }
315 316 dpkt->pkt_cur_len -= len;
316 317 return (B_TRUE);
317 318 }
318 319 raw_pkt = next;
319 320 }
320 321 } else {
321 322 uchar_t *pstart, *padrun;
322 323
323 324 raw_pkt += offsetof(PKT, options);
324 325 pstart = raw_pkt;
325 326
326 327 if (opt_type == CD_END || opt_type == CD_PAD)
327 328 return (B_FALSE);
328 329
329 330 padrun = NULL;
330 331 while (raw_pkt + 1 <= raw_end) {
331 332 if (*raw_pkt == CD_END)
332 333 break;
333 334 if (*raw_pkt == CD_PAD) {
334 335 if (padrun == NULL)
335 336 padrun = raw_pkt;
336 337 raw_pkt++;
337 338 continue;
338 339 }
339 340 if (raw_pkt + 2 > raw_end)
340 341 break;
341 342 len = raw_pkt[1];
342 343 if (len > raw_end - raw_pkt || len < 2)
343 344 break;
344 345 next = raw_pkt + len;
345 346 if (*raw_pkt == opt_type) {
346 347 if (next < raw_end) {
347 348 int toadd = (4 + ((next-pstart)&3) -
348 349 ((raw_pkt-pstart)&3)) & 3;
349 350 int torem = 4 - toadd;
350 351
351 352 if (torem != 4 && padrun != NULL &&
352 353 (raw_pkt - padrun) >= torem) {
353 354 raw_pkt -= torem;
354 355 dpkt->pkt_cur_len -= torem;
355 356 } else if (toadd > 0) {
356 357 (void) memset(raw_pkt, CD_PAD,
357 358 toadd);
358 359 raw_pkt += toadd;
359 360 /* max is not an issue here */
360 361 dpkt->pkt_cur_len += toadd;
361 362 }
362 363 if (raw_pkt != next) {
363 364 (void) memmove(raw_pkt, next,
364 365 raw_end - next);
365 366 }
366 367 }
367 368 dpkt->pkt_cur_len -= len;
368 369 return (B_TRUE);
369 370 }
370 371 padrun = NULL;
371 372 raw_pkt = next;
372 373 }
373 374 }
374 375 return (B_FALSE);
375 376 }
376 377
377 378 /*
378 379 * update_v6opt_len(): updates the length field of a DHCPv6 option.
379 380 *
380 381 * input: dhcpv6_option_t *: option to be updated
381 382 * int: number of octets to add or subtract
382 383 * output: boolean_t: B_TRUE on success, B_FALSE on failure
383 384 */
384 385
385 386 boolean_t
386 387 update_v6opt_len(dhcpv6_option_t *opt, int adjust)
387 388 {
388 389 dhcpv6_option_t optval;
389 390
390 391 (void) memcpy(&optval, opt, sizeof (optval));
391 392 adjust += ntohs(optval.d6o_len);
392 393 if (adjust < 0 || adjust > UINT16_MAX) {
393 394 return (B_FALSE);
394 395 } else {
395 396 optval.d6o_len = htons(adjust);
396 397 (void) memcpy(opt, &optval, sizeof (optval));
397 398 return (B_TRUE);
398 399 }
399 400 }
400 401
401 402 /*
402 403 * add_pkt_opt(): adds an option to a dhcp_pkt_t
403 404 *
404 405 * input: dhcp_pkt_t *: the packet to add the option to
405 406 * uint_t: the type of option being added
|
↓ open down ↓ |
372 lines elided |
↑ open up ↑ |
406 407 * const void *: the value of that option
407 408 * uint_t: the length of the value of the option
408 409 * output: void *: pointer to the option that was added, or NULL on failure.
409 410 */
410 411
411 412 void *
412 413 add_pkt_opt(dhcp_pkt_t *dpkt, uint_t opt_type, const void *opt_val,
413 414 uint_t opt_len)
414 415 {
415 416 uchar_t *raw_pkt;
416 - int req_len;
417 + size_t req_len;
417 418 void *optr;
418 419
419 420 raw_pkt = (uchar_t *)dpkt->pkt;
420 421 optr = raw_pkt + dpkt->pkt_cur_len;
421 422 if (dpkt->pkt_isv6) {
422 - dhcpv6_option_t d6o;
423 + req_len = opt_len + sizeof (dhcpv6_option_t);
423 424
424 - req_len = opt_len + sizeof (d6o);
425 -
426 425 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
427 426 dhcpmsg(MSG_WARNING,
428 427 "add_pkt_opt: not enough room for v6 option %u in "
429 428 "packet (%u + %u > %u)", opt_type,
430 429 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
431 430 return (NULL);
432 431 }
433 - d6o.d6o_code = htons(opt_type);
434 - d6o.d6o_len = htons(opt_len);
435 - (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], &d6o, sizeof (d6o));
436 - dpkt->pkt_cur_len += sizeof (d6o);
437 - if (opt_len > 0) {
438 - (void) memcpy(&raw_pkt[dpkt->pkt_cur_len], opt_val,
439 - opt_len);
440 - dpkt->pkt_cur_len += opt_len;
441 - }
442 432 } else {
443 - req_len = opt_len + 2; /* + 2 for code & length bytes */
433 + req_len = opt_len + DHCP_OPT_META_LEN;
444 434
445 435 /* CD_END and CD_PAD options don't have a length field */
446 436 if (opt_type == CD_END || opt_type == CD_PAD) {
447 437 req_len = 1;
448 438 } else if (opt_val == NULL) {
449 439 dhcpmsg(MSG_ERROR, "add_pkt_opt: option type %d is "
450 440 "missing required value", opt_type);
451 441 return (NULL);
452 442 }
453 443
454 444 if ((dpkt->pkt_cur_len + req_len) > dpkt->pkt_max_len) {
455 445 dhcpmsg(MSG_WARNING,
456 446 "add_pkt_opt: not enough room for v4 option %u in "
457 447 "packet", opt_type);
458 448 return (NULL);
459 449 }
450 + }
460 451
461 - raw_pkt[dpkt->pkt_cur_len++] = opt_type;
452 + req_len = encode_dhcp_opt(&raw_pkt[dpkt->pkt_cur_len], dpkt->pkt_isv6,
453 + opt_type, opt_val, opt_len);
454 + dpkt->pkt_cur_len += req_len;
462 455
463 - if (req_len > 1) {
464 - raw_pkt[dpkt->pkt_cur_len++] = opt_len;
465 - if (opt_len > 0) {
466 - (void) memcpy(&raw_pkt[dpkt->pkt_cur_len],
467 - opt_val, opt_len);
468 - dpkt->pkt_cur_len += opt_len;
469 - }
456 + return (optr);
457 +}
458 +
459 +/*
460 + * encode_dhcp_opt(): sets the fields of an allocated DHCP option buffer
461 + *
462 + * input: void *: the buffer allocated for enough space for
463 + * (DHCPv6) dhcpv6_option_t and value, or for (DHCPv4) opt_type +
464 + * length + value (length/value are skipped for CD_END or
465 + * CD_PAD);
466 + * boolean_t: a value indicating whether DHCPv6 or not;
467 + * uint_t: the type of option being added;
468 + * const void *: the value of that option;
469 + * uint_t: the length of the value of the option
470 + * output: size_t: the number of bytes written starting at opt.
471 + */
472 +
473 +size_t
474 +encode_dhcp_opt(void *dopt, boolean_t isv6, uint_t opt_type,
475 + const void *opt_val, uint_t opt_len)
476 +{
477 + boolean_t do_copy_value = B_FALSE;
478 + size_t res_len = 0;
479 + uint8_t *pval;
480 +
481 + if (isv6) {
482 + dhcpv6_option_t d6o;
483 + d6o.d6o_code = htons(opt_type);
484 + d6o.d6o_len = htons(opt_len);
485 + (void) memcpy(dopt, &d6o, sizeof (d6o));
486 + res_len += sizeof (d6o);
487 +
488 + do_copy_value = B_TRUE;
489 + } else {
490 + pval = (uint8_t *)dopt;
491 + pval[res_len++] = opt_type;
492 +
493 + if (opt_type != CD_END && opt_type != CD_PAD) {
494 + pval[res_len++] = opt_len;
495 + do_copy_value = B_TRUE;
470 496 }
471 497 }
472 - return (optr);
498 +
499 + pval = (uint8_t *)dopt + res_len;
500 + if (do_copy_value && opt_len > 0) {
501 + (void) memcpy(pval, opt_val, opt_len);
502 + res_len += opt_len;
503 + }
504 +
505 + return (res_len);
473 506 }
474 507
475 508 /*
476 509 * add_pkt_subopt(): adds an option to a dhcp_pkt_t option. DHCPv6-specific,
477 510 * but could be extended to IPv4 DHCP if necessary. Assumes
478 511 * that if the parent isn't a top-level option, the caller
479 512 * will adjust any upper-level options recursively using
480 513 * update_v6opt_len.
481 514 *
482 515 * input: dhcp_pkt_t *: the packet to add the suboption to
483 516 * dhcpv6_option_t *: the start of the option to that should contain
484 517 * it (parent)
485 518 * uint_t: the type of suboption being added
486 519 * const void *: the value of that option
487 520 * uint_t: the length of the value of the option
488 521 * output: void *: pointer to the suboption that was added, or NULL on
489 522 * failure.
490 523 */
491 524
492 525 void *
493 526 add_pkt_subopt(dhcp_pkt_t *dpkt, dhcpv6_option_t *parentopt, uint_t opt_type,
494 527 const void *opt_val, uint_t opt_len)
495 528 {
496 529 uchar_t *raw_pkt;
497 530 int req_len;
498 531 void *optr;
499 532 dhcpv6_option_t d6o;
500 533 uchar_t *optend;
501 534 int olen;
502 535
503 536 if (!dpkt->pkt_isv6)
504 537 return (NULL);
505 538
506 539 raw_pkt = (uchar_t *)dpkt->pkt;
507 540 req_len = opt_len + sizeof (d6o);
508 541
509 542 if (dpkt->pkt_cur_len + req_len > dpkt->pkt_max_len) {
510 543 dhcpmsg(MSG_WARNING,
511 544 "add_pkt_subopt: not enough room for v6 suboption %u in "
512 545 "packet (%u + %u > %u)", opt_type,
513 546 dpkt->pkt_cur_len, req_len, dpkt->pkt_max_len);
514 547 return (NULL);
515 548 }
516 549
517 550 /*
518 551 * Update the parent option to include room for this option,
519 552 * and compute the insertion point.
520 553 */
521 554 (void) memcpy(&d6o, parentopt, sizeof (d6o));
522 555 olen = ntohs(d6o.d6o_len);
523 556 optend = (uchar_t *)(parentopt + 1) + olen;
524 557 olen += req_len;
525 558 d6o.d6o_len = htons(olen);
526 559 (void) memcpy(parentopt, &d6o, sizeof (d6o));
527 560
528 561 /*
529 562 * If there's anything at the end to move, then move it. Also bump up
530 563 * the packet size.
531 564 */
532 565 if (optend < raw_pkt + dpkt->pkt_cur_len) {
533 566 (void) memmove(optend + req_len, optend,
534 567 (raw_pkt + dpkt->pkt_cur_len) - optend);
535 568 }
536 569 dpkt->pkt_cur_len += req_len;
537 570
538 571 /*
539 572 * Now format the suboption and add it in.
540 573 */
541 574 optr = optend;
542 575 d6o.d6o_code = htons(opt_type);
543 576 d6o.d6o_len = htons(opt_len);
544 577 (void) memcpy(optend, &d6o, sizeof (d6o));
545 578 if (opt_len > 0)
546 579 (void) memcpy(optend + sizeof (d6o), opt_val, opt_len);
547 580 return (optr);
548 581 }
549 582
550 583 /*
551 584 * add_pkt_opt16(): adds an option with a 16-bit value to a dhcp_pkt_t
552 585 *
553 586 * input: dhcp_pkt_t *: the packet to add the option to
554 587 * uint_t: the type of option being added
555 588 * uint16_t: the value of that option
556 589 * output: void *: pointer to the option that was added, or NULL on failure.
557 590 */
558 591
559 592 void *
560 593 add_pkt_opt16(dhcp_pkt_t *dpkt, uint_t opt_type, uint16_t opt_value)
561 594 {
562 595 return (add_pkt_opt(dpkt, opt_type, &opt_value, 2));
563 596 }
564 597
565 598 /*
566 599 * add_pkt_opt32(): adds an option with a 32-bit value to a dhcp_pkt_t
567 600 *
568 601 * input: dhcp_pkt_t *: the packet to add the option to
569 602 * uint_t: the type of option being added
570 603 * uint32_t: the value of that option
571 604 * output: void *: pointer to the option that was added, or NULL on failure.
572 605 */
573 606
574 607 void *
575 608 add_pkt_opt32(dhcp_pkt_t *dpkt, uint_t opt_type, uint32_t opt_value)
576 609 {
577 610 return (add_pkt_opt(dpkt, opt_type, &opt_value, 4));
578 611 }
579 612
580 613 /*
581 614 * add_pkt_prl(): adds the parameter request option to the packet
582 615 *
583 616 * input: dhcp_pkt_t *: the packet to add the option to
584 617 * dhcp_smach_t *: state machine with request option
585 618 * output: void *: pointer to the option that was added, or NULL on failure.
586 619 */
587 620
588 621 void *
589 622 add_pkt_prl(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
590 623 {
591 624 uint_t len;
592 625
593 626 if (dsmp->dsm_prllen == 0)
594 627 return (0);
595 628
596 629 if (dpkt->pkt_isv6) {
597 630 uint16_t *prl;
598 631
599 632 /*
600 633 * RFC 3315 requires that we include the option, even if we
601 634 * have nothing to request.
602 635 */
603 636 if (dsmp->dsm_prllen == 0)
604 637 prl = NULL;
605 638 else
606 639 prl = alloca(dsmp->dsm_prllen * sizeof (uint16_t));
607 640
608 641 for (len = 0; len < dsmp->dsm_prllen; len++)
609 642 prl[len] = htons(dsmp->dsm_prl[len]);
610 643 return (add_pkt_opt(dpkt, DHCPV6_OPT_ORO, prl,
611 644 len * sizeof (uint16_t)));
612 645 } else {
613 646 uint8_t *prl = alloca(dsmp->dsm_prllen);
614 647
615 648 for (len = 0; len < dsmp->dsm_prllen; len++)
616 649 prl[len] = dsmp->dsm_prl[len];
617 650 return (add_pkt_opt(dpkt, CD_REQUEST_LIST, prl, len));
618 651 }
619 652 }
620 653
621 654 /*
622 655 * add_pkt_lif(): Adds CD_REQUESTED_IP_ADDR (IPv4 DHCP) or IA_NA and IAADDR
623 656 * (DHCPv6) options to the packet to represent the given LIF.
624 657 *
625 658 * input: dhcp_pkt_t *: the packet to add the options to
626 659 * dhcp_lif_t *: the logical interface to represent
627 660 * int: status code (unused for IPv4 DHCP)
628 661 * const char *: message to include with status option, or NULL
629 662 * output: boolean_t: B_TRUE on success, B_FALSE on failure
630 663 */
631 664
632 665 boolean_t
633 666 add_pkt_lif(dhcp_pkt_t *dpkt, dhcp_lif_t *lif, int status, const char *msg)
634 667 {
635 668 if (lif->lif_pif->pif_isv6) {
636 669 dhcp_smach_t *dsmp;
637 670 dhcpv6_message_t *d6m;
638 671 dhcpv6_ia_na_t d6in;
639 672 dhcpv6_iaaddr_t d6ia;
640 673 uint32_t iaid;
641 674 uint16_t *statusopt;
642 675 dhcpv6_option_t *d6o, *d6so;
643 676 uint_t olen;
644 677
645 678 /*
646 679 * Currently, we support just one IAID related to the primary
647 680 * LIF on the state machine.
648 681 */
649 682 dsmp = lif->lif_lease->dl_smach;
650 683 iaid = dsmp->dsm_lif->lif_iaid;
651 684 iaid = htonl(iaid);
652 685
653 686 d6m = (dhcpv6_message_t *)dpkt->pkt;
654 687
655 688 /*
656 689 * Find or create the IA_NA needed for this LIF. If we
657 690 * supported IA_TA, we'd check the IFF_TEMPORARY bit here.
658 691 */
659 692 d6o = NULL;
660 693 while ((d6o = dhcpv6_find_option(d6m + 1,
661 694 dpkt->pkt_cur_len - sizeof (*d6m), d6o, DHCPV6_OPT_IA_NA,
662 695 &olen)) != NULL) {
663 696 if (olen < sizeof (d6in))
664 697 continue;
665 698 (void) memcpy(&d6in, d6o, sizeof (d6in));
666 699 if (d6in.d6in_iaid == iaid)
667 700 break;
668 701 }
669 702 if (d6o == NULL) {
670 703 d6in.d6in_iaid = iaid;
671 704 d6in.d6in_t1 = 0;
672 705 d6in.d6in_t2 = 0;
673 706 d6o = add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
674 707 (dhcpv6_option_t *)&d6in + 1,
675 708 sizeof (d6in) - sizeof (*d6o));
676 709 if (d6o == NULL)
677 710 return (B_FALSE);
678 711 }
679 712
680 713 /*
681 714 * Now add the IAADDR suboption for this LIF. No need to
682 715 * search here, as we know that this is unique.
683 716 */
684 717 d6ia.d6ia_addr = lif->lif_v6addr;
685 718
686 719 /*
687 720 * For Release and Decline, we zero out the lifetime. For
688 721 * Renew and Rebind, we report the original time as the
689 722 * preferred and valid lifetimes.
690 723 */
691 724 if (d6m->d6m_msg_type == DHCPV6_MSG_RELEASE ||
692 725 d6m->d6m_msg_type == DHCPV6_MSG_DECLINE) {
693 726 d6ia.d6ia_preflife = 0;
694 727 d6ia.d6ia_vallife = 0;
695 728 } else {
696 729 d6ia.d6ia_preflife = htonl(lif->lif_preferred.dt_start);
697 730 d6ia.d6ia_vallife = htonl(lif->lif_expire.dt_start);
698 731 }
699 732 d6so = add_pkt_subopt(dpkt, d6o, DHCPV6_OPT_IAADDR,
700 733 (dhcpv6_option_t *)&d6ia + 1,
701 734 sizeof (d6ia) - sizeof (*d6o));
702 735 if (d6so == NULL)
703 736 return (B_FALSE);
704 737
705 738 /*
706 739 * Add a status code suboption to the IAADDR to tell the server
707 740 * why we're declining the address. Note that we must manually
708 741 * update the enclosing IA_NA, as add_pkt_subopt doesn't know
709 742 * how to do that.
710 743 */
711 744 if (status != DHCPV6_STAT_SUCCESS || msg != NULL) {
712 745 olen = sizeof (*statusopt) +
713 746 (msg == NULL ? 0 : strlen(msg));
714 747 statusopt = alloca(olen);
715 748 *statusopt = htons(status);
716 749 if (msg != NULL) {
717 750 (void) memcpy((char *)(statusopt + 1), msg,
718 751 olen - sizeof (*statusopt));
719 752 }
720 753 d6so = add_pkt_subopt(dpkt, d6so,
721 754 DHCPV6_OPT_STATUS_CODE, statusopt, olen);
722 755 if (d6so != NULL) {
723 756 /*
724 757 * Update for length of suboption header and
725 758 * suboption contents.
726 759 */
727 760 (void) update_v6opt_len(d6o, sizeof (*d6so) +
728 761 olen);
729 762 }
730 763 }
731 764 } else {
732 765 /*
733 766 * For DECLINE, we need to add the CD_REQUESTED_IP_ADDR option.
734 767 * In all other cases (RELEASE and REQUEST), we need to set
735 768 * ciadr.
736 769 */
737 770 if (pkt_send_type(dpkt) == DECLINE) {
738 771 if (!add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
739 772 lif->lif_addr))
740 773 return (B_FALSE);
741 774 } else {
742 775 dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
743 776 }
744 777
745 778 /*
746 779 * It's not too worrisome if the message fails to fit in the
747 780 * packet. The result will still be valid.
748 781 */
749 782 if (msg != NULL)
750 783 (void) add_pkt_opt(dpkt, CD_MESSAGE, msg,
751 784 strlen(msg) + 1);
752 785 }
753 786 return (B_TRUE);
754 787 }
755 788
756 789 /*
757 790 * free_pkt_entry(): frees a packet list list entry
758 791 *
759 792 * input: PKT_LIST *: the packet list entry to free
760 793 * output: void
761 794 */
762 795 void
763 796 free_pkt_entry(PKT_LIST *plp)
764 797 {
765 798 if (plp != NULL) {
766 799 free(plp->pkt);
767 800 free(plp);
768 801 }
769 802 }
770 803
771 804 /*
772 805 * free_pkt_list(): frees an entire packet list
773 806 *
774 807 * input: PKT_LIST **: the packet list to free
775 808 * output: void
776 809 */
777 810
778 811 void
779 812 free_pkt_list(PKT_LIST **head)
780 813 {
781 814 PKT_LIST *plp;
782 815
783 816 while ((plp = *head) != NULL) {
784 817 remque(plp);
785 818 free_pkt_entry(plp);
786 819 }
787 820 }
788 821
789 822 /*
790 823 * send_pkt_internal(): sends a packet out on an interface
791 824 *
792 825 * input: dhcp_smach_t *: the state machine with a packet to send
793 826 * output: boolean_t: B_TRUE if the packet is sent, B_FALSE otherwise
794 827 */
795 828
796 829 static boolean_t
797 830 send_pkt_internal(dhcp_smach_t *dsmp)
798 831 {
799 832 ssize_t n_bytes;
800 833 dhcp_lif_t *lif = dsmp->dsm_lif;
801 834 dhcp_pkt_t *dpkt = &dsmp->dsm_send_pkt;
802 835 uchar_t ptype = pkt_send_type(dpkt);
803 836 const char *pkt_name;
804 837 struct iovec iov;
805 838 struct msghdr msg;
806 839 struct cmsghdr *cmsg;
807 840 struct in6_pktinfo *ipi6;
808 841 boolean_t ismcast;
809 842 int msgtype;
810 843
811 844 /*
812 845 * Timer should not be running at the point we go to send a packet.
813 846 */
814 847 if (dsmp->dsm_retrans_timer != -1) {
815 848 dhcpmsg(MSG_CRIT, "send_pkt_internal: unexpected retransmit "
816 849 "timer on %s", dsmp->dsm_name);
817 850 stop_pkt_retransmission(dsmp);
818 851 }
819 852
820 853 pkt_name = pkt_type_to_string(ptype, dpkt->pkt_isv6);
821 854
822 855 /*
823 856 * if needed, schedule a retransmission timer, then attempt to
824 857 * send the packet. if we fail, then log the error. our
825 858 * return value should indicate whether or not we were
826 859 * successful in sending the request, independent of whether
827 860 * we could schedule a timer.
828 861 */
829 862
830 863 if (dsmp->dsm_send_timeout != 0) {
831 864 if ((dsmp->dsm_retrans_timer = iu_schedule_timer_ms(tq,
832 865 dsmp->dsm_send_timeout, retransmit, dsmp)) == -1)
833 866 dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot "
834 867 "schedule retransmit timer for %s packet",
835 868 pkt_name);
836 869 else
837 870 hold_smach(dsmp);
838 871 }
839 872
840 873 if (dpkt->pkt_isv6) {
841 874 hrtime_t delta;
842 875
843 876 /*
844 877 * Convert current time into centiseconds since transaction
845 878 * started. This is what DHCPv6 expects to see in the Elapsed
846 879 * Time option.
847 880 */
848 881 delta = (gethrtime() - dsmp->dsm_neg_hrtime) /
849 882 (NANOSEC / 100);
850 883 if (delta > DHCPV6_FOREVER)
851 884 delta = DHCPV6_FOREVER;
852 885 (void) remove_pkt_opt(dpkt, DHCPV6_OPT_ELAPSED_TIME);
853 886 (void) add_pkt_opt16(dpkt, DHCPV6_OPT_ELAPSED_TIME,
854 887 htons(delta));
855 888 } else {
856 889 /*
857 890 * set the `pkt->secs' field depending on the type of packet.
858 891 * it should be zero, except in the following cases:
859 892 *
860 893 * DISCOVER: set to the number of seconds since we started
861 894 * trying to obtain a lease.
862 895 *
863 896 * INFORM: set to the number of seconds since we started
864 897 * trying to get configuration parameters.
865 898 *
866 899 * REQUEST: if in the REQUESTING state, then same value as
867 900 * DISCOVER, otherwise the number of seconds
868 901 * since we started trying to obtain a lease.
869 902 *
870 903 * we also set `dsm_newstart_monosec', to the time we sent a
871 904 * REQUEST or DISCOVER packet, so we know the lease start
872 905 * time (the DISCOVER case is for handling BOOTP servers).
873 906 */
874 907
875 908 switch (ptype) {
876 909
877 910 case DISCOVER:
878 911 dsmp->dsm_newstart_monosec = monosec();
879 912 dsmp->dsm_disc_secs = dsmp->dsm_newstart_monosec -
880 913 hrtime_to_monosec(dsmp->dsm_neg_hrtime);
881 914 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
882 915 break;
883 916
884 917 case INFORM:
885 918 dpkt->pkt->secs = htons(monosec() -
886 919 hrtime_to_monosec(dsmp->dsm_neg_hrtime));
887 920 break;
888 921
889 922 case REQUEST:
890 923 dsmp->dsm_newstart_monosec = monosec();
891 924
892 925 if (dsmp->dsm_state == REQUESTING) {
893 926 dpkt->pkt->secs = htons(dsmp->dsm_disc_secs);
894 927 break;
895 928 }
896 929
897 930 dpkt->pkt->secs = htons(monosec() -
898 931 hrtime_to_monosec(dsmp->dsm_neg_hrtime));
899 932 break;
900 933
901 934 default:
902 935 dpkt->pkt->secs = htons(0);
903 936 break;
904 937 }
905 938 }
906 939
907 940 if (dpkt->pkt_isv6) {
908 941 struct sockaddr_in6 sin6;
909 942
910 943 (void) memset(&iov, 0, sizeof (iov));
911 944 iov.iov_base = dpkt->pkt;
912 945 iov.iov_len = dpkt->pkt_cur_len;
913 946
914 947 (void) memset(&msg, 0, sizeof (msg));
915 948 msg.msg_name = &dsmp->dsm_send_dest.v6;
916 949 msg.msg_namelen = sizeof (struct sockaddr_in6);
917 950 msg.msg_iov = &iov;
918 951 msg.msg_iovlen = 1;
919 952
920 953 /*
921 954 * If the address that's requested cannot be reached, then fall
922 955 * back to the multcast address.
923 956 */
924 957 if (IN6_IS_ADDR_MULTICAST(&dsmp->dsm_send_dest.v6.sin6_addr)) {
925 958 ismcast = B_TRUE;
926 959 } else {
927 960 struct dstinforeq dinfo;
928 961 struct strioctl str;
929 962
930 963 ismcast = B_FALSE;
931 964 (void) memset(&dinfo, 0, sizeof (dinfo));
932 965 dinfo.dir_daddr = dsmp->dsm_send_dest.v6.sin6_addr;
933 966 str.ic_cmd = SIOCGDSTINFO;
934 967 str.ic_timout = 0;
935 968 str.ic_len = sizeof (dinfo);
936 969 str.ic_dp = (char *)&dinfo;
937 970 if (ioctl(v6_sock_fd, I_STR, &str) == -1) {
938 971 dhcpmsg(MSG_ERR,
939 972 "send_pkt_internal: ioctl SIOCGDSTINFO");
940 973 } else if (!dinfo.dir_dreachable) {
941 974 char abuf[INET6_ADDRSTRLEN];
942 975
943 976 dhcpmsg(MSG_DEBUG, "send_pkt_internal: %s is "
944 977 "not reachable; using multicast instead",
945 978 inet_ntop(AF_INET6, &dinfo.dir_daddr, abuf,
946 979 sizeof (abuf)));
947 980 sin6 = dsmp->dsm_send_dest.v6;
948 981 sin6.sin6_addr =
949 982 ipv6_all_dhcp_relay_and_servers;
950 983 msg.msg_name = &sin6;
951 984 ismcast = B_TRUE;
952 985 }
953 986 }
954 987
955 988 /*
956 989 * Make room for our ancillary data option as well as a dummy
957 990 * option used by CMSG_NXTHDR.
958 991 */
959 992 msg.msg_controllen = sizeof (*cmsg) + _MAX_ALIGNMENT +
960 993 sizeof (*ipi6) + _MAX_ALIGNMENT + sizeof (*cmsg);
961 994 msg.msg_control = alloca(msg.msg_controllen);
962 995 cmsg = CMSG_FIRSTHDR(&msg);
963 996 cmsg->cmsg_level = IPPROTO_IPV6;
964 997 cmsg->cmsg_type = IPV6_PKTINFO;
965 998 /* LINTED: alignment */
966 999 ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
967 1000 if (ismcast)
968 1001 ipi6->ipi6_addr = lif->lif_v6addr;
969 1002 else
970 1003 ipi6->ipi6_addr = my_in6addr_any;
971 1004 if (lif->lif_pif->pif_under_ipmp)
972 1005 ipi6->ipi6_ifindex = lif->lif_pif->pif_grindex;
973 1006 else
974 1007 ipi6->ipi6_ifindex = lif->lif_pif->pif_index;
975 1008 cmsg->cmsg_len = (char *)(ipi6 + 1) - (char *)cmsg;
976 1009
977 1010 /*
978 1011 * Now correct the control message length.
979 1012 */
980 1013 cmsg = CMSG_NXTHDR(&msg, cmsg);
981 1014 msg.msg_controllen = (char *)cmsg - (char *)msg.msg_control;
982 1015
983 1016 n_bytes = sendmsg(v6_sock_fd, &msg, 0);
984 1017 } else {
985 1018 n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
986 1019 dpkt->pkt_cur_len, 0,
987 1020 (struct sockaddr *)&dsmp->dsm_send_dest.v4,
988 1021 sizeof (struct sockaddr_in));
989 1022 }
990 1023
991 1024 if (n_bytes != dpkt->pkt_cur_len) {
992 1025 msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING;
993 1026 if (dsmp->dsm_retrans_timer == -1)
994 1027 dhcpmsg(msgtype, "send_pkt_internal: cannot send "
995 1028 "%s packet to server", pkt_name);
996 1029 else
997 1030 dhcpmsg(msgtype, "send_pkt_internal: cannot send "
998 1031 "%s packet to server (will retry in %u seconds)",
999 1032 pkt_name, dsmp->dsm_send_timeout / MILLISEC);
1000 1033 return (B_FALSE);
1001 1034 }
1002 1035
1003 1036 dhcpmsg(MSG_VERBOSE, "sent %s xid %x packet out %s", pkt_name,
1004 1037 pkt_get_xid(dpkt->pkt, dpkt->pkt_isv6), dsmp->dsm_name);
1005 1038
1006 1039 dsmp->dsm_packet_sent++;
1007 1040 dsmp->dsm_sent++;
1008 1041 return (B_TRUE);
1009 1042 }
1010 1043
1011 1044 /*
1012 1045 * send_pkt(): sends a packet out
1013 1046 *
1014 1047 * input: dhcp_smach_t *: the state machine sending the packet
1015 1048 * dhcp_pkt_t *: the packet to send out
1016 1049 * in_addr_t: the destination IP address for the packet
1017 1050 * stop_func_t *: a pointer to function to indicate when to stop
1018 1051 * retransmitting the packet (if NULL, packet is
1019 1052 * not retransmitted)
1020 1053 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1021 1054 */
1022 1055
1023 1056 boolean_t
1024 1057 send_pkt(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in_addr_t dest,
1025 1058 stop_func_t *stop)
1026 1059 {
1027 1060 /*
1028 1061 * packets must be at least sizeof (PKT) or they may be dropped
1029 1062 * by routers. pad out the packet in this case.
1030 1063 */
1031 1064
1032 1065 dpkt->pkt_cur_len = MAX(dpkt->pkt_cur_len, sizeof (PKT));
1033 1066
1034 1067 dsmp->dsm_packet_sent = 0;
1035 1068
1036 1069 (void) memset(&dsmp->dsm_send_dest.v4, 0,
1037 1070 sizeof (dsmp->dsm_send_dest.v4));
1038 1071 dsmp->dsm_send_dest.v4.sin_addr.s_addr = dest;
1039 1072 dsmp->dsm_send_dest.v4.sin_family = AF_INET;
1040 1073 dsmp->dsm_send_dest.v4.sin_port = htons(IPPORT_BOOTPS);
1041 1074 dsmp->dsm_send_stop_func = stop;
1042 1075
1043 1076 /*
1044 1077 * TODO: dispose of this gruesome assumption (there's no real
1045 1078 * technical gain from doing so, but it would be cleaner)
1046 1079 */
1047 1080
1048 1081 assert(dpkt == &dsmp->dsm_send_pkt);
1049 1082
1050 1083 /*
1051 1084 * clear out any packets which had been previously received
1052 1085 * but not pulled off of the recv_packet queue.
1053 1086 */
1054 1087
1055 1088 free_pkt_list(&dsmp->dsm_recv_pkt_list);
1056 1089
1057 1090 if (stop == NULL)
1058 1091 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1059 1092 else
1060 1093 next_retransmission(dsmp, B_TRUE, B_FALSE);
1061 1094
1062 1095 return (send_pkt_internal(dsmp));
1063 1096 }
1064 1097
1065 1098 /*
1066 1099 * send_pkt_v6(): sends a DHCPv6 packet out
1067 1100 *
1068 1101 * input: dhcp_smach_t *: the state machine sending the packet
1069 1102 * dhcp_pkt_t *: the packet to send out
1070 1103 * in6_addr_t: the destination IPv6 address for the packet
1071 1104 * stop_func_t *: a pointer to function to indicate when to stop
1072 1105 * retransmitting the packet (if NULL, packet is
1073 1106 * not retransmitted)
1074 1107 * uint_t: Initial Retransmit Timer value
1075 1108 * uint_t: Maximum Retransmit Timer value, zero if none
1076 1109 * output: boolean_t: B_TRUE if the packet was sent, B_FALSE otherwise
1077 1110 */
1078 1111
1079 1112 boolean_t
1080 1113 send_pkt_v6(dhcp_smach_t *dsmp, dhcp_pkt_t *dpkt, in6_addr_t dest,
1081 1114 stop_func_t *stop, uint_t irt, uint_t mrt)
1082 1115 {
1083 1116 dsmp->dsm_packet_sent = 0;
1084 1117
1085 1118 (void) memset(&dsmp->dsm_send_dest.v6, 0,
1086 1119 sizeof (dsmp->dsm_send_dest.v6));
1087 1120 dsmp->dsm_send_dest.v6.sin6_addr = dest;
1088 1121 dsmp->dsm_send_dest.v6.sin6_family = AF_INET6;
1089 1122 dsmp->dsm_send_dest.v6.sin6_port = htons(IPPORT_DHCPV6S);
1090 1123 dsmp->dsm_send_stop_func = stop;
1091 1124
1092 1125 /*
1093 1126 * TODO: dispose of this gruesome assumption (there's no real
1094 1127 * technical gain from doing so, but it would be cleaner)
1095 1128 */
1096 1129
1097 1130 assert(dpkt == &dsmp->dsm_send_pkt);
1098 1131
1099 1132 /*
1100 1133 * clear out any packets which had been previously received
1101 1134 * but not pulled off of the recv_packet queue.
1102 1135 */
1103 1136
1104 1137 free_pkt_list(&dsmp->dsm_recv_pkt_list);
1105 1138
1106 1139 if (stop == NULL) {
1107 1140 dsmp->dsm_send_timeout = 0; /* prevents retransmissions */
1108 1141 } else {
1109 1142 dsmp->dsm_send_timeout = irt;
1110 1143 dsmp->dsm_send_tcenter = mrt;
1111 1144 /*
1112 1145 * This is quite ugly, but RFC 3315 section 17.1.2 requires
1113 1146 * that the RAND value for the very first retransmission of a
1114 1147 * Solicit message is strictly greater than zero.
1115 1148 */
1116 1149 next_retransmission(dsmp, B_TRUE,
1117 1150 pkt_send_type(dpkt) == DHCPV6_MSG_SOLICIT);
1118 1151 }
1119 1152
1120 1153 return (send_pkt_internal(dsmp));
1121 1154 }
1122 1155
1123 1156 /*
1124 1157 * retransmit(): retransmits the current packet on an interface
1125 1158 *
1126 1159 * input: iu_tq_t *: unused
1127 1160 * void *: the dhcp_smach_t * (state machine) sending a packet
1128 1161 * output: void
1129 1162 */
1130 1163
1131 1164 /* ARGSUSED */
1132 1165 static void
1133 1166 retransmit(iu_tq_t *tqp, void *arg)
1134 1167 {
1135 1168 dhcp_smach_t *dsmp = arg;
1136 1169
1137 1170 dsmp->dsm_retrans_timer = -1;
1138 1171
1139 1172 if (!verify_smach(dsmp))
1140 1173 return;
1141 1174
1142 1175 /*
1143 1176 * Check the callback to see if we should keep sending retransmissions.
1144 1177 * Compute the next retransmission time first, so that the callback can
1145 1178 * cap the value if need be. (Required for DHCPv6 Confirm messages.)
1146 1179 *
1147 1180 * Hold the state machine across the callback so that the called
1148 1181 * function can remove the state machine from the system without
1149 1182 * disturbing the string used subsequently for verbose logging. The
1150 1183 * Release function destroys the state machine when the retry count
1151 1184 * expires.
1152 1185 */
1153 1186
1154 1187 next_retransmission(dsmp, B_FALSE, B_FALSE);
1155 1188 hold_smach(dsmp);
1156 1189 if (dsmp->dsm_send_stop_func(dsmp, dsmp->dsm_packet_sent)) {
1157 1190 dhcpmsg(MSG_VERBOSE, "retransmit: time to stop on %s",
1158 1191 dsmp->dsm_name);
1159 1192 } else {
1160 1193 dhcpmsg(MSG_VERBOSE, "retransmit: sending another on %s",
1161 1194 dsmp->dsm_name);
1162 1195 (void) send_pkt_internal(dsmp);
1163 1196 }
1164 1197 release_smach(dsmp);
1165 1198 }
1166 1199
1167 1200 /*
1168 1201 * stop_pkt_retransmission(): stops retransmission of last sent packet
1169 1202 *
1170 1203 * input: dhcp_smach_t *: the state machine to stop retransmission on
1171 1204 * output: void
1172 1205 */
1173 1206
1174 1207 void
1175 1208 stop_pkt_retransmission(dhcp_smach_t *dsmp)
1176 1209 {
1177 1210 if (dsmp->dsm_retrans_timer != -1 &&
1178 1211 iu_cancel_timer(tq, dsmp->dsm_retrans_timer, NULL) == 1) {
1179 1212 dhcpmsg(MSG_VERBOSE, "stop_pkt_retransmission: stopped on %s",
1180 1213 dsmp->dsm_name);
1181 1214 dsmp->dsm_retrans_timer = -1;
1182 1215 release_smach(dsmp);
1183 1216 }
1184 1217 }
1185 1218
1186 1219 /*
1187 1220 * retransmit_now(): force a packet retransmission right now. Used only with
1188 1221 * the DHCPv6 UseMulticast status code. Use with caution;
1189 1222 * triggered retransmissions can cause packet storms.
1190 1223 *
1191 1224 * input: dhcp_smach_t *: the state machine to force retransmission on
1192 1225 * output: void
1193 1226 */
1194 1227
1195 1228 void
1196 1229 retransmit_now(dhcp_smach_t *dsmp)
1197 1230 {
1198 1231 stop_pkt_retransmission(dsmp);
1199 1232 (void) send_pkt_internal(dsmp);
1200 1233 }
1201 1234
1202 1235 /*
1203 1236 * alloc_pkt_entry(): Allocates a packet list entry with a given data area
1204 1237 * size.
1205 1238 *
1206 1239 * input: size_t: size of data area for packet
1207 1240 * boolean_t: B_TRUE for IPv6
1208 1241 * output: PKT_LIST *: allocated packet list entry
1209 1242 */
1210 1243
1211 1244 PKT_LIST *
1212 1245 alloc_pkt_entry(size_t psize, boolean_t isv6)
1213 1246 {
1214 1247 PKT_LIST *plp;
1215 1248
1216 1249 if ((plp = calloc(1, sizeof (*plp))) == NULL ||
1217 1250 (plp->pkt = malloc(psize)) == NULL) {
1218 1251 free(plp);
1219 1252 plp = NULL;
1220 1253 } else {
1221 1254 plp->len = psize;
1222 1255 plp->isv6 = isv6;
1223 1256 }
1224 1257
1225 1258 return (plp);
1226 1259 }
1227 1260
1228 1261 /*
1229 1262 * sock_recvpkt(): read from the given socket into an allocated buffer and
1230 1263 * handles any ancillary data options.
1231 1264 *
1232 1265 * input: int: file descriptor to read
1233 1266 * PKT_LIST *: allocated buffer
1234 1267 * output: ssize_t: number of bytes read, or -1 on error
1235 1268 */
1236 1269
1237 1270 static ssize_t
1238 1271 sock_recvpkt(int fd, PKT_LIST *plp)
1239 1272 {
1240 1273 struct iovec iov;
1241 1274 struct msghdr msg;
1242 1275 int64_t ctrl[8192 / sizeof (int64_t)];
1243 1276 ssize_t msglen;
1244 1277
1245 1278 (void) memset(&iov, 0, sizeof (iov));
1246 1279 iov.iov_base = (caddr_t)plp->pkt;
1247 1280 iov.iov_len = plp->len;
1248 1281
1249 1282 (void) memset(&msg, 0, sizeof (msg));
1250 1283 msg.msg_name = &plp->pktfrom;
1251 1284 msg.msg_namelen = sizeof (plp->pktfrom);
1252 1285 msg.msg_iov = &iov;
1253 1286 msg.msg_iovlen = 1;
1254 1287 msg.msg_control = ctrl;
1255 1288 msg.msg_controllen = sizeof (ctrl);
1256 1289
1257 1290 if ((msglen = recvmsg(fd, &msg, 0)) != -1) {
1258 1291 struct cmsghdr *cmsg;
1259 1292
1260 1293 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
1261 1294 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1262 1295 struct sockaddr_in *sinp;
1263 1296 struct sockaddr_in6 *sin6;
1264 1297 struct in6_pktinfo *ipi6;
1265 1298
1266 1299 switch (cmsg->cmsg_level) {
1267 1300 case IPPROTO_IP:
1268 1301 switch (cmsg->cmsg_type) {
1269 1302 case IP_RECVDSTADDR:
1270 1303 sinp = (struct sockaddr_in *)
1271 1304 &plp->pktto;
1272 1305 sinp->sin_family = AF_INET;
1273 1306 (void) memcpy(&sinp->sin_addr.s_addr,
1274 1307 CMSG_DATA(cmsg),
1275 1308 sizeof (ipaddr_t));
1276 1309 break;
1277 1310
1278 1311 case IP_RECVIF:
1279 1312 (void) memcpy(&plp->ifindex,
1280 1313 CMSG_DATA(cmsg), sizeof (uint_t));
1281 1314 break;
1282 1315 }
1283 1316 break;
1284 1317
1285 1318 case IPPROTO_IPV6:
1286 1319 switch (cmsg->cmsg_type) {
1287 1320 case IPV6_PKTINFO:
1288 1321 /* LINTED: alignment */
1289 1322 ipi6 = (struct in6_pktinfo *)
1290 1323 CMSG_DATA(cmsg);
1291 1324 sin6 = (struct sockaddr_in6 *)
1292 1325 &plp->pktto;
1293 1326 sin6->sin6_family = AF_INET6;
1294 1327 (void) memcpy(&sin6->sin6_addr,
1295 1328 &ipi6->ipi6_addr,
1296 1329 sizeof (ipi6->ipi6_addr));
1297 1330 (void) memcpy(&plp->ifindex,
1298 1331 &ipi6->ipi6_ifindex,
1299 1332 sizeof (uint_t));
1300 1333 break;
1301 1334 }
1302 1335 }
1303 1336 }
1304 1337 }
1305 1338 return (msglen);
1306 1339 }
1307 1340
1308 1341 /*
1309 1342 * recv_pkt(): receives a single DHCP packet on a given file descriptor.
1310 1343 *
1311 1344 * input: int: the file descriptor to receive the packet from
1312 1345 * int: the maximum packet size to allow
1313 1346 * boolean_t: B_TRUE for IPv6
1314 1347 * output: PKT_LIST *: the received packet
1315 1348 */
1316 1349
1317 1350 PKT_LIST *
1318 1351 recv_pkt(int fd, int mtu, boolean_t isv6)
1319 1352 {
1320 1353 PKT_LIST *plp;
1321 1354 ssize_t retval;
1322 1355
1323 1356 if ((plp = alloc_pkt_entry(mtu, isv6)) == NULL) {
1324 1357 dhcpmsg(MSG_ERROR,
1325 1358 "recv_pkt: allocation failure; dropped packet");
1326 1359 return (NULL);
1327 1360 }
1328 1361
1329 1362 retval = sock_recvpkt(fd, plp);
1330 1363 if (retval == -1) {
1331 1364 dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped",
1332 1365 isv6 ? 6 : 4);
1333 1366 goto failure;
1334 1367 }
1335 1368
1336 1369 plp->len = retval;
1337 1370
1338 1371 if (isv6) {
1339 1372 if (retval < sizeof (dhcpv6_message_t)) {
1340 1373 dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
1341 1374 goto failure;
1342 1375 }
1343 1376 } else {
1344 1377 switch (dhcp_options_scan(plp, B_TRUE)) {
1345 1378
1346 1379 case DHCP_WRONG_MSG_TYPE:
1347 1380 dhcpmsg(MSG_WARNING,
1348 1381 "recv_pkt: unexpected DHCP message");
1349 1382 goto failure;
1350 1383
1351 1384 case DHCP_GARBLED_MSG_TYPE:
1352 1385 dhcpmsg(MSG_WARNING,
1353 1386 "recv_pkt: garbled DHCP message type");
1354 1387 goto failure;
1355 1388
1356 1389 case DHCP_BAD_OPT_OVLD:
1357 1390 dhcpmsg(MSG_WARNING, "recv_pkt: bad option overload");
1358 1391 goto failure;
1359 1392
1360 1393 case 0:
1361 1394 break;
1362 1395
1363 1396 default:
1364 1397 dhcpmsg(MSG_WARNING,
1365 1398 "recv_pkt: packet corrupted, dropped");
1366 1399 goto failure;
1367 1400 }
1368 1401 }
1369 1402 return (plp);
1370 1403
1371 1404 failure:
1372 1405 free_pkt_entry(plp);
1373 1406 return (NULL);
1374 1407 }
1375 1408
1376 1409 /*
1377 1410 * pkt_v4_match(): check if a given DHCPv4 message type is in a given set
1378 1411 *
1379 1412 * input: uchar_t: packet type
1380 1413 * dhcp_message_type_t: bit-wise OR of DHCP_P* values.
1381 1414 * output: boolean_t: B_TRUE if packet type is in the set
1382 1415 */
1383 1416
1384 1417 boolean_t
1385 1418 pkt_v4_match(uchar_t type, dhcp_message_type_t match_type)
1386 1419 {
1387 1420 /*
1388 1421 * note: the ordering here allows direct indexing of the table
1389 1422 * based on the RFC2131 packet type value passed in.
1390 1423 */
1391 1424
1392 1425 static dhcp_message_type_t type_map[] = {
1393 1426 DHCP_PUNTYPED, DHCP_PDISCOVER, DHCP_POFFER, DHCP_PREQUEST,
1394 1427 DHCP_PDECLINE, DHCP_PACK, DHCP_PNAK, DHCP_PRELEASE,
1395 1428 DHCP_PINFORM
1396 1429 };
1397 1430
1398 1431 if (type < (sizeof (type_map) / sizeof (*type_map)))
1399 1432 return ((type_map[type] & match_type) ? B_TRUE : B_FALSE);
1400 1433 else
1401 1434 return (B_FALSE);
1402 1435 }
1403 1436
1404 1437 /*
1405 1438 * pkt_smach_enqueue(): enqueue a packet on a given state machine
1406 1439 *
1407 1440 * input: dhcp_smach_t: state machine
1408 1441 * PKT_LIST *: packet to enqueue
1409 1442 * output: none
1410 1443 */
1411 1444
1412 1445 void
1413 1446 pkt_smach_enqueue(dhcp_smach_t *dsmp, PKT_LIST *plp)
1414 1447 {
1415 1448 dhcpmsg(MSG_VERBOSE, "pkt_smach_enqueue: received %s %s packet on %s",
1416 1449 pkt_type_to_string(pkt_recv_type(plp), dsmp->dsm_isv6),
1417 1450 dsmp->dsm_isv6 ? "v6" : "v4", dsmp->dsm_name);
1418 1451
1419 1452 /* add to front of list */
1420 1453 insque(plp, &dsmp->dsm_recv_pkt_list);
1421 1454 }
1422 1455
1423 1456 /*
1424 1457 * next_retransmission(): computes the number of seconds until the next
1425 1458 * retransmission, based on the algorithms in RFCs 2131
1426 1459 * 3315.
1427 1460 *
1428 1461 * input: dhcp_smach_t *: state machine that needs a new timer
1429 1462 * boolean_t: B_TRUE if this is the first time sending the message
1430 1463 * boolean_t: B_TRUE for positive RAND values only (RFC 3315 17.1.2)
1431 1464 * output: none
1432 1465 */
1433 1466
1434 1467 static void
1435 1468 next_retransmission(dhcp_smach_t *dsmp, boolean_t first_send,
1436 1469 boolean_t positive_only)
1437 1470 {
1438 1471 uint32_t timeout_ms;
1439 1472
1440 1473 if (dsmp->dsm_isv6) {
1441 1474 double randval;
1442 1475
1443 1476 /*
1444 1477 * The RFC specifies 0 to 10% jitter for the initial
1445 1478 * solicitation, and plus or minus 10% jitter for all others.
1446 1479 * This works out to 100 milliseconds on the shortest timer we
1447 1480 * use.
1448 1481 */
1449 1482 if (positive_only)
1450 1483 randval = drand48() / 10.0;
1451 1484 else
1452 1485 randval = (drand48() - 0.5) / 5.0;
1453 1486
1454 1487 /* The RFC specifies doubling *after* the first transmission */
1455 1488 timeout_ms = dsmp->dsm_send_timeout;
1456 1489 if (!first_send)
1457 1490 timeout_ms *= 2;
1458 1491 timeout_ms += (int)(randval * dsmp->dsm_send_timeout);
1459 1492
1460 1493 /* This checks the MRT (maximum retransmission time) */
1461 1494 if (dsmp->dsm_send_tcenter != 0 &&
1462 1495 timeout_ms > dsmp->dsm_send_tcenter) {
1463 1496 timeout_ms = dsmp->dsm_send_tcenter +
1464 1497 (uint_t)(randval * dsmp->dsm_send_tcenter);
1465 1498 }
1466 1499
1467 1500 dsmp->dsm_send_timeout = timeout_ms;
1468 1501 } else {
1469 1502 if (dsmp->dsm_state == RENEWING ||
1470 1503 dsmp->dsm_state == REBINDING) {
1471 1504 monosec_t mono;
1472 1505
1473 1506 timeout_ms = dsmp->dsm_state == RENEWING ?
1474 1507 dsmp->dsm_leases->dl_t2.dt_start :
1475 1508 dsmp->dsm_leases->dl_lifs->lif_expire.dt_start;
1476 1509 timeout_ms += dsmp->dsm_curstart_monosec;
1477 1510 mono = monosec();
1478 1511 if (mono > timeout_ms)
1479 1512 timeout_ms = 0;
1480 1513 else
1481 1514 timeout_ms -= mono;
1482 1515 timeout_ms *= MILLISEC / 2;
1483 1516 } else {
1484 1517 /*
1485 1518 * Start at 4, and increase by a factor of 2 up to 64.
1486 1519 */
1487 1520 if (first_send) {
1488 1521 timeout_ms = 4 * MILLISEC;
1489 1522 } else {
1490 1523 timeout_ms = MIN(dsmp->dsm_send_tcenter << 1,
1491 1524 64 * MILLISEC);
1492 1525 }
1493 1526 }
1494 1527
1495 1528 dsmp->dsm_send_tcenter = timeout_ms;
1496 1529
1497 1530 /*
1498 1531 * At each iteration, jitter the timeout by some fraction of a
1499 1532 * second.
1500 1533 */
1501 1534 dsmp->dsm_send_timeout = timeout_ms +
1502 1535 ((lrand48() % (2 * MILLISEC)) - MILLISEC);
1503 1536 }
1504 1537 }
1505 1538
1506 1539 /*
1507 1540 * dhcp_ip_default(): open and bind the default IP sockets used for I/O and
1508 1541 * interface control.
1509 1542 *
1510 1543 * input: none
1511 1544 * output: B_TRUE on success
1512 1545 */
1513 1546
1514 1547 boolean_t
1515 1548 dhcp_ip_default(void)
1516 1549 {
1517 1550 int on = 1;
1518 1551
1519 1552 if ((v4_sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1520 1553 dhcpmsg(MSG_ERR,
1521 1554 "dhcp_ip_default: unable to create IPv4 socket");
1522 1555 return (B_FALSE);
1523 1556 }
1524 1557
1525 1558 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
1526 1559 sizeof (on)) == -1) {
1527 1560 dhcpmsg(MSG_ERR,
1528 1561 "dhcp_ip_default: unable to enable IP_RECVDSTADDR");
1529 1562 return (B_FALSE);
1530 1563 }
1531 1564
1532 1565 if (setsockopt(v4_sock_fd, IPPROTO_IP, IP_RECVIF, &on, sizeof (on)) ==
1533 1566 -1) {
1534 1567 dhcpmsg(MSG_ERR,
1535 1568 "dhcp_ip_default: unable to enable IP_RECVIF");
1536 1569 return (B_FALSE);
1537 1570 }
1538 1571
1539 1572 if (!bind_sock(v4_sock_fd, IPPORT_BOOTPC, INADDR_ANY)) {
1540 1573 dhcpmsg(MSG_ERROR,
1541 1574 "dhcp_ip_default: unable to bind IPv4 socket to port %d",
1542 1575 IPPORT_BOOTPC);
1543 1576 return (B_FALSE);
1544 1577 }
1545 1578
1546 1579 if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global,
1547 1580 NULL) == -1) {
1548 1581 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1549 1582 "receive IPv4 broadcasts");
1550 1583 return (B_FALSE);
1551 1584 }
1552 1585
1553 1586 if ((v6_sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
1554 1587 dhcpmsg(MSG_ERR,
1555 1588 "dhcp_ip_default: unable to create IPv6 socket");
1556 1589 return (B_FALSE);
1557 1590 }
1558 1591
1559 1592 if (setsockopt(v6_sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1560 1593 sizeof (on)) == -1) {
1561 1594 dhcpmsg(MSG_ERR,
1562 1595 "dhcp_ip_default: unable to enable IPV6_RECVPKTINFO");
1563 1596 return (B_FALSE);
1564 1597 }
1565 1598
1566 1599 if (!bind_sock_v6(v6_sock_fd, IPPORT_DHCPV6C, NULL)) {
1567 1600 dhcpmsg(MSG_ERROR,
1568 1601 "dhcp_ip_default: unable to bind IPv6 socket to port %d",
1569 1602 IPPORT_DHCPV6C);
1570 1603 return (B_FALSE);
1571 1604 }
1572 1605
1573 1606 if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global,
1574 1607 NULL) == -1) {
1575 1608 dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
1576 1609 "receive IPv6 packets");
1577 1610 return (B_FALSE);
1578 1611 }
1579 1612
1580 1613 return (B_TRUE);
1581 1614 }
|
↓ open down ↓ |
1099 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX