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/util.c
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.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 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 + * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
23 24 */
24 25
25 26 #include <unistd.h>
26 27 #include <sys/types.h>
27 28 #include <sys/stat.h>
29 +#include <sys/utsname.h>
28 30 #include <stdlib.h>
29 31 #include <netinet/in.h> /* struct in_addr */
30 32 #include <netinet/dhcp.h>
31 33 #include <signal.h>
32 34 #include <sys/socket.h>
33 35 #include <net/route.h>
34 36 #include <net/if_arp.h>
35 37 #include <string.h>
36 38 #include <dhcpmsg.h>
37 39 #include <ctype.h>
40 +#include <arpa/inet.h>
41 +#include <arpa/nameser.h>
42 +#include <resolv.h>
38 43 #include <netdb.h>
39 44 #include <fcntl.h>
40 45 #include <stdio.h>
41 46 #include <dhcp_hostconf.h>
47 +#include <limits.h>
48 +#include <strings.h>
49 +#include <libipadm.h>
42 50
43 51 #include "states.h"
44 52 #include "agent.h"
45 53 #include "interface.h"
46 54 #include "util.h"
47 55 #include "packet.h"
56 +#include "defaults.h"
48 57
49 58 /*
50 59 * this file contains utility functions that have no real better home
51 60 * of their own. they can largely be broken into six categories:
52 61 *
53 62 * o conversion functions -- functions to turn integers into strings,
54 63 * or to convert between units of a similar measure.
55 64 *
56 65 * o time and timer functions -- functions to handle time measurement
57 66 * and events.
58 67 *
59 68 * o ipc-related functions -- functions to simplify the generation of
|
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
60 69 * ipc messages to the agent's clients.
61 70 *
62 71 * o signal-related functions -- functions to clean up the agent when
63 72 * it receives a signal.
64 73 *
65 74 * o routing table manipulation functions
66 75 *
67 76 * o true miscellany -- anything else
68 77 */
69 78
79 +#define ETCNODENAME "/etc/nodename"
80 +#define ETCDEFAULTDOMAIN "/etc/defaultdomain"
81 +
82 +static boolean_t is_fqdn(const char *);
83 +static int dhcp_assemble_fqdn(dhcp_smach_t *dsmp);
84 +
70 85 /*
71 86 * pkt_type_to_string(): stringifies a packet type
72 87 *
73 88 * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
74 89 * boolean_t: B_TRUE if IPv6
75 90 * output: const char *: the stringified packet type
76 91 */
77 92
78 93 const char *
79 94 pkt_type_to_string(uchar_t type, boolean_t isv6)
80 95 {
81 96 /*
82 97 * note: the ordering in these arrays allows direct indexing of the
83 98 * table based on the RFC packet type value passed in.
84 99 */
85 100
86 101 static const char *v4types[] = {
87 102 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
88 103 "ACK", "NAK", "RELEASE", "INFORM"
89 104 };
90 105 static const char *v6types[] = {
91 106 NULL, "SOLICIT", "ADVERTISE", "REQUEST",
92 107 "CONFIRM", "RENEW", "REBIND", "REPLY",
93 108 "RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
94 109 "RELAY-FORW", "RELAY-REPL"
95 110 };
96 111
97 112 if (isv6) {
98 113 if (type >= sizeof (v6types) / sizeof (*v6types) ||
99 114 v6types[type] == NULL)
100 115 return ("<unknown>");
101 116 else
102 117 return (v6types[type]);
103 118 } else {
104 119 if (type >= sizeof (v4types) / sizeof (*v4types) ||
105 120 v4types[type] == NULL)
106 121 return ("<unknown>");
107 122 else
108 123 return (v4types[type]);
109 124 }
110 125 }
111 126
112 127 /*
113 128 * monosec_to_string(): converts a monosec_t into a date string
114 129 *
115 130 * input: monosec_t: the monosec_t to convert
116 131 * output: const char *: the corresponding date string
117 132 */
118 133
119 134 const char *
120 135 monosec_to_string(monosec_t monosec)
121 136 {
122 137 time_t time = monosec_to_time(monosec);
123 138 char *time_string = ctime(&time);
124 139
125 140 /* strip off the newline -- ugh, why, why, why.. */
126 141 time_string[strlen(time_string) - 1] = '\0';
127 142 return (time_string);
128 143 }
129 144
130 145 /*
131 146 * monosec(): returns a monotonically increasing time in seconds that
132 147 * is not affected by stime(2) or adjtime(2).
133 148 *
134 149 * input: void
135 150 * output: monosec_t: the number of seconds since some time in the past
136 151 */
137 152
138 153 monosec_t
139 154 monosec(void)
140 155 {
141 156 return (gethrtime() / NANOSEC);
142 157 }
143 158
144 159 /*
145 160 * monosec_to_time(): converts a monosec_t into real wall time
146 161 *
147 162 * input: monosec_t: the absolute monosec_t to convert
148 163 * output: time_t: the absolute time that monosec_t represents in wall time
149 164 */
150 165
151 166 time_t
152 167 monosec_to_time(monosec_t abs_monosec)
153 168 {
154 169 return (abs_monosec - monosec()) + time(NULL);
155 170 }
156 171
157 172 /*
158 173 * hrtime_to_monosec(): converts a hrtime_t to monosec_t
159 174 *
160 175 * input: hrtime_t: the time to convert
161 176 * output: monosec_t: the time in monosec_t
162 177 */
163 178
164 179 monosec_t
165 180 hrtime_to_monosec(hrtime_t hrtime)
166 181 {
167 182 return (hrtime / NANOSEC);
168 183 }
169 184
170 185 /*
171 186 * print_server_msg(): prints a message from a DHCP server
172 187 *
173 188 * input: dhcp_smach_t *: the state machine the message is associated with
174 189 * const char *: the string to display
175 190 * uint_t: length of string
176 191 * output: void
177 192 */
178 193
179 194 void
180 195 print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
181 196 {
182 197 if (msglen > 0) {
183 198 dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
184 199 dsmp->dsm_name, msglen, msg);
185 200 }
186 201 }
187 202
188 203 /*
189 204 * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
190 205 *
191 206 * input: int: signal the handler was called with.
192 207 *
193 208 * output: void
194 209 */
195 210
196 211 static void
197 212 alrm_exit(int sig)
198 213 {
199 214 int exitval;
200 215
201 216 if (sig == SIGALRM && grandparent != 0)
202 217 exitval = EXIT_SUCCESS;
203 218 else
204 219 exitval = EXIT_FAILURE;
205 220
206 221 _exit(exitval);
207 222 }
208 223
209 224 /*
210 225 * daemonize(): daemonizes the process
211 226 *
212 227 * input: void
213 228 * output: int: 1 on success, 0 on failure
214 229 */
215 230
216 231 int
217 232 daemonize(void)
218 233 {
219 234 /*
220 235 * We've found that adoption takes sufficiently long that
221 236 * a dhcpinfo run after dhcpagent -a is started may occur
222 237 * before the agent is ready to process the request.
223 238 * The result is an error message and an unhappy user.
224 239 *
225 240 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
226 241 * unless interrupted by a SIGALRM, in which case it
227 242 * exits immediately. This has the effect that the
228 243 * grandparent doesn't exit until the dhcpagent is ready
229 244 * to process requests. This defers the the balance of
230 245 * the system start-up script processing until the
231 246 * dhcpagent is ready to field requests.
232 247 *
233 248 * grandparent is only set for the adopt case; other
234 249 * cases do not require the wait.
235 250 */
236 251
237 252 if (grandparent != 0)
238 253 (void) signal(SIGALRM, alrm_exit);
239 254
240 255 switch (fork()) {
241 256
242 257 case -1:
243 258 return (0);
244 259
245 260 case 0:
246 261 if (grandparent != 0)
247 262 (void) signal(SIGALRM, SIG_DFL);
248 263
249 264 /*
250 265 * setsid() makes us lose our controlling terminal,
251 266 * and become both a session leader and a process
252 267 * group leader.
253 268 */
254 269
255 270 (void) setsid();
256 271
257 272 /*
258 273 * under POSIX, a session leader can accidentally
259 274 * (through open(2)) acquire a controlling terminal if
260 275 * it does not have one. just to be safe, fork again
261 276 * so we are not a session leader.
262 277 */
263 278
264 279 switch (fork()) {
265 280
266 281 case -1:
267 282 return (0);
268 283
269 284 case 0:
270 285 (void) signal(SIGHUP, SIG_IGN);
271 286 (void) chdir("/");
272 287 (void) umask(022);
273 288 closefrom(0);
274 289 break;
275 290
276 291 default:
277 292 _exit(EXIT_SUCCESS);
278 293 }
279 294 break;
280 295
281 296 default:
282 297 if (grandparent != 0) {
283 298 (void) signal(SIGCHLD, SIG_IGN);
284 299 /*
285 300 * Note that we're not the agent here, so the DHCP
286 301 * logging subsystem hasn't been configured yet.
287 302 */
288 303 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
289 304 "waiting for adoption to complete.");
290 305 if (sleep(DHCP_ADOPT_SLEEP) == 0) {
291 306 syslog(LOG_WARNING | LOG_DAEMON,
292 307 "dhcpagent: daemonize: timed out awaiting "
293 308 "adoption.");
294 309 }
295 310 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
296 311 "wait finished");
297 312 }
298 313 _exit(EXIT_SUCCESS);
299 314 }
300 315
301 316 return (1);
302 317 }
303 318
304 319 /*
305 320 * update_default_route(): update the interface's default route
306 321 *
307 322 * input: int: the type of message; either RTM_ADD or RTM_DELETE
308 323 * struct in_addr: the default gateway to use
309 324 * const char *: the interface associated with the route
310 325 * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
311 326 * output: boolean_t: B_TRUE on success, B_FALSE on failure
312 327 */
313 328
314 329 static boolean_t
315 330 update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo,
316 331 int flags)
317 332 {
318 333 struct {
319 334 struct rt_msghdr rm_mh;
320 335 struct sockaddr_in rm_dst;
321 336 struct sockaddr_in rm_gw;
322 337 struct sockaddr_in rm_mask;
323 338 struct sockaddr_dl rm_ifp;
324 339 } rtmsg;
325 340
326 341 (void) memset(&rtmsg, 0, sizeof (rtmsg));
327 342 rtmsg.rm_mh.rtm_version = RTM_VERSION;
328 343 rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg);
329 344 rtmsg.rm_mh.rtm_type = type;
330 345 rtmsg.rm_mh.rtm_pid = getpid();
331 346 rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags;
332 347 rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
333 348
334 349 rtmsg.rm_gw.sin_family = AF_INET;
335 350 rtmsg.rm_gw.sin_addr = *gateway_nbo;
336 351
337 352 rtmsg.rm_dst.sin_family = AF_INET;
338 353 rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
339 354
340 355 rtmsg.rm_mask.sin_family = AF_INET;
341 356 rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
342 357
343 358 rtmsg.rm_ifp.sdl_family = AF_LINK;
344 359 rtmsg.rm_ifp.sdl_index = ifindex;
345 360
346 361 return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
347 362 }
348 363
349 364 /*
350 365 * add_default_route(): add the default route to the given gateway
351 366 *
352 367 * input: const char *: the name of the interface associated with the route
353 368 * struct in_addr: the default gateway to add
354 369 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
355 370 */
356 371
357 372 boolean_t
358 373 add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
359 374 {
360 375 return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP));
361 376 }
362 377
363 378 /*
364 379 * del_default_route(): deletes the default route to the given gateway
365 380 *
366 381 * input: const char *: the name of the interface associated with the route
367 382 * struct in_addr: if not INADDR_ANY, the default gateway to remove
368 383 * output: boolean_t: B_TRUE on success, B_FALSE on failure
369 384 */
370 385
371 386 boolean_t
372 387 del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
373 388 {
374 389 if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
375 390 return (B_TRUE);
376 391
377 392 return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0));
378 393 }
379 394
380 395 /*
381 396 * inactivity_shutdown(): shuts down agent if there are no state machines left
382 397 * to manage
383 398 *
384 399 * input: iu_tq_t *: unused
385 400 * void *: unused
386 401 * output: void
387 402 */
388 403
389 404 /* ARGSUSED */
390 405 void
391 406 inactivity_shutdown(iu_tq_t *tqp, void *arg)
392 407 {
393 408 if (smach_count() > 0) /* shouldn't happen, but... */
394 409 return;
395 410
396 411 dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
397 412
398 413 iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
399 414 }
400 415
401 416 /*
402 417 * graceful_shutdown(): shuts down the agent gracefully
403 418 *
404 419 * input: int: the signal that caused graceful_shutdown to be called
405 420 * output: void
406 421 */
407 422
408 423 void
409 424 graceful_shutdown(int sig)
410 425 {
411 426 iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
412 427 DHCP_REASON_SIGNAL), drain_script, NULL);
413 428 }
414 429
415 430 /*
416 431 * bind_sock(): binds a socket to a given IP address and port number
417 432 *
418 433 * input: int: the socket to bind
419 434 * in_port_t: the port number to bind to, host byte order
420 435 * in_addr_t: the address to bind to, host byte order
421 436 * output: boolean_t: B_TRUE on success, B_FALSE on failure
422 437 */
423 438
424 439 boolean_t
425 440 bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
426 441 {
427 442 struct sockaddr_in sin;
428 443 int on = 1;
429 444
430 445 (void) memset(&sin, 0, sizeof (struct sockaddr_in));
431 446 sin.sin_family = AF_INET;
432 447 sin.sin_port = htons(port_hbo);
433 448 sin.sin_addr.s_addr = htonl(addr_hbo);
434 449
435 450 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
436 451
437 452 return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
438 453 }
439 454
440 455 /*
441 456 * bind_sock_v6(): binds a socket to a given IP address and port number
442 457 *
443 458 * input: int: the socket to bind
444 459 * in_port_t: the port number to bind to, host byte order
445 460 * in6_addr_t: the address to bind to, network byte order
446 461 * output: boolean_t: B_TRUE on success, B_FALSE on failure
447 462 */
448 463
449 464 boolean_t
450 465 bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
451 466 {
452 467 struct sockaddr_in6 sin6;
453 468 int on = 1;
454 469
455 470 (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
456 471 sin6.sin6_family = AF_INET6;
457 472 sin6.sin6_port = htons(port_hbo);
458 473 if (addr_nbo != NULL) {
|
↓ open down ↓ |
379 lines elided |
↑ open up ↑ |
459 474 (void) memcpy(&sin6.sin6_addr, addr_nbo,
460 475 sizeof (sin6.sin6_addr));
461 476 }
462 477
463 478 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
464 479
465 480 return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
466 481 }
467 482
468 483 /*
469 - * valid_hostname(): check whether a string is a valid hostname
470 - *
471 - * input: const char *: the string to verify as a hostname
472 - * output: boolean_t: B_TRUE if the string is a valid hostname
473 - *
474 - * Note that we accept both host names beginning with a digit and
475 - * those containing hyphens. Neither is strictly legal according
476 - * to the RFCs, but both are in common practice, so we endeavour
477 - * to not break what customers are using.
478 - */
479 -
480 -static boolean_t
481 -valid_hostname(const char *hostname)
482 -{
483 - unsigned int i;
484 -
485 - for (i = 0; hostname[i] != '\0'; i++) {
486 -
487 - if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
488 - (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
489 - continue;
490 -
491 - return (B_FALSE);
492 - }
493 -
494 - return (i > 0);
495 -}
496 -
497 -/*
498 484 * iffile_to_hostname(): return the hostname contained on a line of the form
499 485 *
500 486 * [ ^I]*inet[ ^I]+hostname[\n]*\0
501 487 *
502 488 * in the file located at the specified path
503 489 *
504 490 * input: const char *: the path of the file to look in for the hostname
505 491 * output: const char *: the hostname at that path, or NULL on failure
506 492 */
507 493
508 494 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
509 495
510 496 const char *
511 497 iffile_to_hostname(const char *path)
512 498 {
513 499 FILE *fp;
514 500 static char ifline[IFLINE_MAX];
515 501
516 502 fp = fopen(path, "r");
517 503 if (fp == NULL)
518 504 return (NULL);
519 505
520 506 /*
521 507 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
522 508 * such command is on a separate line (see the "while read ifcmds" code
523 509 * in /etc/init.d/inetinit). Thus we will read the file a line at a
524 510 * time, searching for a line of the form
525 511 *
526 512 * [ ^I]*inet[ ^I]+hostname[\n]*\0
527 513 *
528 514 * extract the host name from it, and check it for validity.
529 515 */
530 516 while (fgets(ifline, sizeof (ifline), fp) != NULL) {
531 517 char *p;
532 518
533 519 if ((p = strstr(ifline, "inet")) != NULL) {
534 520 if ((p != ifline) && !isspace(p[-1])) {
535 521 (void) fclose(fp);
536 522 return (NULL);
537 523 }
538 524 p += 4; /* skip over "inet" and expect spaces or tabs */
539 525 if ((*p == '\n') || (*p == '\0')) {
540 526 (void) fclose(fp);
541 527 return (NULL);
542 528 }
543 529 if (isspace(*p)) {
544 530 char *nlptr;
545 531
546 532 /* no need to read more of the file */
547 533 (void) fclose(fp);
548 534
|
↓ open down ↓ |
41 lines elided |
↑ open up ↑ |
549 535 while (isspace(*p))
550 536 p++;
551 537 if ((nlptr = strrchr(p, '\n')) != NULL)
552 538 *nlptr = '\0';
553 539 if (strlen(p) > MAXHOSTNAMELEN) {
554 540 dhcpmsg(MSG_WARNING,
555 541 "iffile_to_hostname:"
556 542 " host name too long");
557 543 return (NULL);
558 544 }
559 - if (valid_hostname(p)) {
545 + if (ipadm_is_valid_hostname(p)) {
560 546 return (p);
561 547 } else {
562 548 dhcpmsg(MSG_WARNING,
563 549 "iffile_to_hostname:"
564 550 " host name not valid");
565 551 return (NULL);
566 552 }
567 553 } else {
568 554 (void) fclose(fp);
569 555 return (NULL);
570 556 }
571 557 }
572 558 }
573 559
574 560 (void) fclose(fp);
575 561 return (NULL);
576 562 }
577 563
578 564 /*
579 565 * init_timer(): set up a DHCP timer
580 566 *
581 567 * input: dhcp_timer_t *: the timer to set up
582 568 * output: void
583 569 */
584 570
585 571 void
586 572 init_timer(dhcp_timer_t *dt, lease_t startval)
587 573 {
588 574 dt->dt_id = -1;
589 575 dt->dt_start = startval;
590 576 }
591 577
592 578 /*
593 579 * cancel_timer(): cancel a DHCP timer
594 580 *
595 581 * input: dhcp_timer_t *: the timer to cancel
596 582 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
597 583 */
598 584
599 585 boolean_t
600 586 cancel_timer(dhcp_timer_t *dt)
601 587 {
602 588 if (dt->dt_id == -1)
603 589 return (B_TRUE);
604 590
605 591 if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
606 592 dt->dt_id = -1;
607 593 return (B_TRUE);
608 594 }
609 595
610 596 return (B_FALSE);
611 597 }
612 598
613 599 /*
614 600 * schedule_timer(): schedule a DHCP timer. Note that it must not be already
615 601 * running, and that we can't cancel here. If it were, and
616 602 * we did, we'd leak a reference to the callback argument.
617 603 *
618 604 * input: dhcp_timer_t *: the timer to schedule
619 605 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
620 606 */
621 607
622 608 boolean_t
623 609 schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
624 610 {
625 611 if (dt->dt_id != -1)
626 612 return (B_FALSE);
627 613 dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
628 614 return (dt->dt_id != -1);
629 615 }
630 616
631 617 /*
632 618 * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
633 619 * buffer.
634 620 *
635 621 * input: const dhcpv6_option_t *: pointer to option
636 622 * uint_t: option length
637 623 * const char **: error string (nul-terminated)
638 624 * const char **: message from server (unterminated)
639 625 * uint_t *: length of server message
640 626 * output: int: -1 on error, or >= 0 for a DHCPv6 status code
641 627 */
642 628
643 629 int
644 630 dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
645 631 const char **msg, uint_t *msglenp)
646 632 {
647 633 uint16_t status;
648 634 static const char *v6_status[] = {
649 635 NULL,
650 636 "Unknown reason",
651 637 "Server has no addresses available",
652 638 "Client record unavailable",
653 639 "Prefix inappropriate for link",
654 640 "Client must use multicast",
655 641 "No prefix available"
656 642 };
657 643 static char sbuf[32];
658 644
659 645 *estr = "";
660 646 *msg = "";
661 647 *msglenp = 0;
662 648 if (d6o == NULL)
663 649 return (0);
664 650 olen -= sizeof (*d6o);
665 651 if (olen < 2) {
666 652 *estr = "garbled status code";
667 653 return (-1);
668 654 }
669 655
670 656 *msg = (const char *)(d6o + 1) + 2;
671 657 *msglenp = olen - 2;
672 658
673 659 (void) memcpy(&status, d6o + 1, sizeof (status));
674 660 status = ntohs(status);
675 661 if (status > 0) {
676 662 if (status > DHCPV6_STAT_NOPREFIX) {
677 663 (void) snprintf(sbuf, sizeof (sbuf), "status %u",
678 664 status);
679 665 *estr = sbuf;
680 666 } else {
681 667 *estr = v6_status[status];
682 668 }
683 669 }
684 670 return (status);
685 671 }
686 672
687 673 void
688 674 write_lease_to_hostconf(dhcp_smach_t *dsmp)
689 675 {
690 676 PKT_LIST *plp[2];
691 677 const char *hcfile;
692 678
693 679 hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
694 680 plp[0] = dsmp->dsm_ack;
695 681 plp[1] = dsmp->dsm_orig_ack;
696 682 if (write_hostconf(dsmp->dsm_name, plp, 2,
|
↓ open down ↓ |
127 lines elided |
↑ open up ↑ |
697 683 monosec_to_time(dsmp->dsm_curstart_monosec),
698 684 dsmp->dsm_isv6) != -1) {
699 685 dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
700 686 } else if (errno == EROFS) {
701 687 dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
702 688 "system; not saving lease", hcfile);
703 689 } else {
704 690 dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
705 691 "not use cached configuration)", hcfile);
706 692 }
693 +}
694 +
695 +/*
696 + * Try to get a string from the first line of a file, up to but not
697 + * including any space (0x20) or newline.
698 + */
699 +
700 +static int
701 +dhcp_get_oneline(const char *filename, char *buf, size_t buflen)
702 +{
703 + char value[SYS_NMLN], *c;
704 + int fd, i;
705 +
706 + if ((fd = open(filename, O_RDONLY)) <= 0) {
707 + dhcpmsg(MSG_DEBUG, "dhcp_get_oneline: could not open %s",
708 + filename);
709 + *buf = '\0';
710 + } else {
711 + if ((i = read(fd, value, SYS_NMLN - 1)) <= 0) {
712 + dhcpmsg(MSG_WARNING, "dhcp_get_oneline: no line in %s",
713 + filename);
714 + *buf = '\0';
715 + } else {
716 + value[i] = '\0';
717 + if ((c = strchr(value, '\n')) != NULL)
718 + *c = '\0';
719 + if ((c = strchr(value, ' ')) != NULL)
720 + *c = '\0';
721 +
722 + if (strlcpy(buf, value, buflen) >= buflen) {
723 + dhcpmsg(MSG_WARNING, "dhcp_get_oneline: too long value, %s",
724 + value);
725 + *buf = '\0';
726 + }
727 + }
728 + (void) close(fd);
729 + }
730 +
731 + return (*buf != '\0' ? 0 : -1);
732 +}
733 +
734 +/*
735 + * Try to get the hostname from the /etc/nodename file. uname(2) cannot
736 + * be used, because that is initialized after DHCP has solicited, in order
737 + * to allow for the possibility that utsname.nodename can be set from
738 + * DHCP Hostname. Here, though, we want to send a value specified
739 + * advance of DHCP, so read /etc/nodename directly.
740 + */
741 +
742 +static int
743 +dhcp_get_nodename(char *buf, size_t buflen)
744 +{
745 + return dhcp_get_oneline(ETCNODENAME, buf, buflen);
746 +}
747 +
748 +/*
749 + * Try to get the value from the /etc/defaultdomain file. libnsl's
750 + * domainname() cannot be used, because that is initialized after DHCP has
751 + * solicited. Here, though, we want to send a value specified in advance
752 + * of DHCP, so read /etc/defaultdomain directly.
753 + */
754 +
755 +static int
756 +dhcp_get_defaultdomain(char *buf, size_t buflen)
757 +{
758 + return dhcp_get_oneline(ETCDEFAULTDOMAIN, buf, buflen);
759 +}
760 +
761 +/*
762 + * dhcp_add_hostname_opt(): Set CD_HOSTNAME option if REQUEST_HOSTNAME is
763 + * affirmative and if 1) dsm_msg_reqhost is available; or
764 + * 2) hostname is read from an extant /etc/hostname.<ifname>
765 + * file; or 3) interface is primary and nodename(4) is defined.
766 + *
767 + * input: dhcp_pkt_t *: pointer to DHCP message being constructed;
768 + * dhcp_smach_t *: pointer to interface DHCP state machine;
769 + */
770 +
771 +int
772 +dhcp_add_hostname_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
773 +{
774 + const char *reqhost;
775 + char nodename[MAXNAMELEN];
776 +
777 + if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME))
778 + return (0);
779 +
780 + dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: DF_REQUEST_HOSTNAME");
781 +
782 + if (dsmp->dsm_msg_reqhost != NULL
783 + && ipadm_is_valid_hostname(dsmp->dsm_msg_reqhost)) {
784 + reqhost = dsmp->dsm_msg_reqhost;
785 + } else {
786 + char hostfile[PATH_MAX + 1];
787 +
788 + (void) snprintf(hostfile, sizeof (hostfile),
789 + "/etc/hostname.%s", dsmp->dsm_name);
790 + reqhost = iffile_to_hostname(hostfile);
791 + }
792 +
793 + if (reqhost == NULL && (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
794 + dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
795 + reqhost = nodename;
796 + }
797 +
798 + if (reqhost != NULL) {
799 + free(dsmp->dsm_reqhost);
800 + if ((dsmp->dsm_reqhost = strdup(reqhost)) == NULL)
801 + dhcpmsg(MSG_WARNING, "dhcp_add_hostname_opt: cannot allocate "
802 + "memory for host name option");
803 + }
804 +
805 + if (dsmp->dsm_reqhost != NULL) {
806 + dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: host %s for %s",
807 + dsmp->dsm_reqhost, dsmp->dsm_name);
808 + (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
809 + strlen(dsmp->dsm_reqhost));
810 + return 1;
811 + }
812 + else {
813 + dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: no hostname for %s",
814 + dsmp->dsm_name);
815 + }
816 +
817 + return (0);
818 +}
819 +
820 +/*
821 + * dhcp_add_fqdn_opt(): Set CD_CLIENTFQDN option if dsm_reqfqdn is not NULL
822 + * or if dhcp_assemble_fqdn() initializes it. If dsm_reqfqdn
823 + * cannot be set, no option will be added.
824 + *
825 + * input: dhcp_pkt_t *: pointer to DHCP message being constructed;
826 + * dhcp_smach_t *: pointer to interface DHCP state machine;
827 + * output: 0 if a CLIENT_FQDN was added; non-zero if otherwise;
828 + */
829 +
830 +int
831 +dhcp_add_fqdn_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
832 +{
833 + /*
834 + * RFC 4702 section 2:
835 + *
836 + * The format of the Client FQDN option is:
837 + *
838 + * Code Len Flags RCODE1 RCODE2 Domain Name
839 + * +------+------+------+------+------+------+--
840 + * | 81 | n | | | | ...
841 + * +------+------+------+------+------+------+--
842 + *
843 + * Code and Len are distinct, and the remainder is in a single buffer,
844 + * opt81, for Flags + (unused) RCODE1 and RCODE2 (all octets) and a
845 + * potentially maximum-length domain name.
846 + *
847 + * The format of the Flags field is:
848 + *
849 + * 0 1 2 3 4 5 6 7
850 + * +-+-+-+-+-+-+-+-+
851 + * | MBZ |N|E|O|S|
852 + * +-+-+-+-+-+-+-+-+
853 + *
854 + * where MBZ is ignored and NEOS are:
855 + *
856 + * S = 1 to request that "the server SHOULD perform the A RR (FQDN-to-
857 + * address) DNS updates;
858 + *
859 + * O = 0, for a server-only response bit;
860 + *
861 + * E = 1 to indicate the domain name is in "canonical wire format,
862 + * without compression (i.e., ns_name_pton2) .... This encoding SHOULD
863 + * be used by clients ....";
864 + *
865 + * N = 0 to request that "the server SHALL perform DNS updates [of the
866 + * PTR RR]." (1 would request SHALL NOT update).
867 + *
868 + * The format of the DHCPv6 Client FQDN option is shown below:
869 + *
870 + * 0 1 2 3
871 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
872 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
873 + * | OPTION_FQDN | option-len |
874 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
875 + * | flags | |
876 + * +-+-+-+-+-+-+-+-+ |
877 + * . .
878 + * . domain-name .
879 + * . .
880 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
881 + *
882 + * option-code OPTION_CLIENT_FQDN (39)
883 + *
884 + * option-len 1 + length of domain name
885 + *
886 + * flags flag bits used between client and server to
887 + * negotiate who performs which updates
888 + *
889 + * domain-name the partial or fully qualified domain name
890 + * (with length option-len - 1)
891 + *
892 + * The DHCPv6 format of the Flags field is:
893 + *
894 + * 0 1 2 3 4 5 6 7
895 + * +-+-+-+-+-+-+-+-+
896 + * | MBZ |N|O|S|
897 + * +-+-+-+-+-+-+-+-+
898 + */
899 +
900 + const uint8_t S_BITPOS = 7 - 7;
901 + const uint8_t E_BITPOS = 7 - 5;
902 + const size_t OPT_FQDN_METALEN = 3, OPT_V6_FQDN_METALEN = 1;
903 + uint_t fqdncode;
904 + u_char enc_fqdnbuf[MAXNAMELEN];
905 + uint8_t fqdnopt[MAXNAMELEN + OPT_FQDN_METALEN];
906 + size_t len, metalen;
907 +
908 + if (dsmp->dsm_reqfqdn == NULL && dhcp_assemble_fqdn(dsmp) != 0)
909 + return (-1);
910 +
911 + /* encode the FQDN in canonical wire format */
912 +
913 + if (ns_name_pton2(dsmp->dsm_reqfqdn, enc_fqdnbuf, sizeof (enc_fqdnbuf),
914 + &len) < 0) {
915 + dhcpmsg(MSG_WARNING, "dhcp_add_fqdn_opt: error encoding domain"
916 + " name %s", dsmp->dsm_reqfqdn);
917 + return (-1);
918 + }
919 +
920 + dhcpmsg(MSG_DEBUG, "dhcp_add_fqdn_opt: interface FQDN is %s"
921 + " for %s", dsmp->dsm_reqfqdn, dsmp->dsm_name);
922 +
923 + bzero(fqdnopt, sizeof (fqdnopt));
924 + if (dsmp->dsm_isv6) {
925 + fqdncode = DHCPV6_OPT_CLIENT_FQDN;
926 + metalen = OPT_V6_FQDN_METALEN;
927 + *fqdnopt = (uint8_t)(1 << S_BITPOS);
928 + } else {
929 + fqdncode = CD_CLIENTFQDN;
930 + metalen = OPT_FQDN_METALEN;
931 + *fqdnopt = (uint8_t)((1 << S_BITPOS) | (1 << E_BITPOS));
932 + }
933 + (void) memcpy(fqdnopt + metalen, enc_fqdnbuf, len);
934 + (void) add_pkt_opt(dpkt, fqdncode, fqdnopt, metalen + len);
935 +
936 + return (0);
937 +}
938 +
939 +/*
940 + * dhcp_assemble_fqdn(): Set dsm_reqfqdn if REQUEST_FQDN is set and
941 + * either a host name was sent in the IPC message (e.g., from
942 + * ipadm(1M) -h,--reqhost) or the interface is primary and a
943 + * nodename(4) is defined. If the host name is not already fully
944 + * qualified per is_fqdn(), then a value from
945 + * dhcp_get_defaultdomain() or from resolv.conf(4)--if defined--
946 + * is used to construct an FQDN.
947 + *
948 + * If no FQDN can be determined, dsm_reqfqdn will be NULL.
949 + *
950 + * input: dhcp_smach_t *: pointer to interface DHCP state machine;
951 + * output: 0 if dsm_reqfqdn was assigned; non-zero if otherwise;
952 + */
953 +
954 +static int
955 +dhcp_assemble_fqdn(dhcp_smach_t *dsmp)
956 +{
957 + char fqdnbuf[MAXNAMELEN], nodename[MAXNAMELEN], *reqhost;
958 + size_t pos, len;
959 +
960 + if (dsmp->dsm_reqfqdn != NULL) {
961 + free(dsmp->dsm_reqfqdn);
962 + dsmp->dsm_reqfqdn = NULL;
963 + }
964 +
965 + if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_FQDN))
966 + return (-1);
967 +
968 + dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: DF_REQUEST_FQDN");
969 +
970 + bzero(fqdnbuf, sizeof (fqdnbuf));
971 +
972 + reqhost = dsmp->dsm_msg_reqhost;
973 + if (ipadm_is_nil_hostname(reqhost) &&
974 + (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
975 + dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
976 + reqhost = nodename;
977 + }
978 +
979 + if (ipadm_is_nil_hostname(reqhost)) {
980 + dhcpmsg(MSG_DEBUG,
981 + "dhcp_assemble_fqdn: no interface reqhost for %s",
982 + dsmp->dsm_name);
983 + return (-1);
984 + }
985 +
986 + if ((pos = strlcpy(fqdnbuf, reqhost, sizeof (fqdnbuf))) >=
987 + sizeof (fqdnbuf)) {
988 + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long reqhost %s"
989 + " for %s", reqhost, dsmp->dsm_name);
990 + return (-1);
991 + }
992 +
993 + if (!is_fqdn(reqhost)) {
994 + char dnamebuf[MAXHOSTNAMELEN];
995 + size_t needdots;
996 + int lasterrno;
997 + char *domainname = NULL;
998 +
999 + /*
1000 + * determine FQDN domain name, if possible
1001 + */
1002 +
1003 + if (dhcp_get_defaultdomain(dnamebuf, sizeof (dnamebuf)) == 0 &&
1004 + !ipadm_is_nil_hostname(dnamebuf)) {
1005 + domainname = dnamebuf;
1006 + } else {
1007 + /*
1008 + * fall back to resolv's "default domain (deprecated)"
1009 + */
1010 +
1011 + struct __res_state res_state;
1012 + bzero(&res_state, sizeof (struct __res_state));
1013 +
1014 + /* initialize resolver or warn */
1015 + if ((lasterrno = res_ninit(&res_state)) != 0) {
1016 + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: error %d"
1017 + " initializing resolver", lasterrno);
1018 + }
1019 + else {
1020 + if (*res_state.defdname != '\0') {
1021 + (void) strlcpy(dnamebuf, res_state.defdname,
1022 + sizeof (dnamebuf));
1023 + domainname = dnamebuf;
1024 + }
1025 + res_ndestroy(&res_state);
1026 + }
1027 + }
1028 +
1029 + if (ipadm_is_nil_hostname(domainname)) {
1030 + dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: no domain name for %s",
1031 + dsmp->dsm_name);
1032 + return (-1);
1033 + }
1034 +
1035 + /*
1036 + * Finish constructing FQDN. Account for space needed to hold a
1037 + * separator '.' and a terminating '.'.
1038 + */
1039 + len = strlen(domainname);
1040 + needdots = 1 + (domainname[len - 1] != '.');
1041 +
1042 + if (pos + len + needdots >= sizeof (fqdnbuf)) {
1043 + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long FQDN %s.%s"
1044 + " for %s", fqdnbuf, domainname, dsmp->dsm_name);
1045 + return (-1);
1046 + }
1047 +
1048 + /* add separator and then domain name */
1049 + fqdnbuf[pos++] = '.';
1050 + (void) strlcpy(fqdnbuf + pos, domainname, sizeof (fqdnbuf) - pos);
1051 + pos += len;
1052 +
1053 + /* ensure the final character is '.' */
1054 + if (needdots > 1)
1055 + fqdnbuf[pos++] = '.'; /* following is already zeroed */
1056 + }
1057 +
1058 + if (!ipadm_is_valid_hostname(fqdnbuf)) {
1059 + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: invalid FQDN %s for %s",
1060 + fqdnbuf, dsmp->dsm_name);
1061 + return (-1);
1062 + }
1063 +
1064 + free(dsmp->dsm_reqfqdn);
1065 + if ((dsmp->dsm_reqfqdn = strdup(fqdnbuf)) == NULL) {
1066 + dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: cannot allocate memory");
1067 + return (-1);
1068 + }
1069 + return (0);
1070 +}
1071 +
1072 +/*
1073 + * is_fqdn() : Determine if the `hostname' can be considered as a Fully
1074 + * Qualified Domain Name by being "rooted" (i.e., ending in '.')
1075 + * or by containing at least three DNS labels (e.g.,
1076 + * srv.example.com).
1077 + *
1078 + * input: const char *: the hostname to inspect;
1079 + * output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
1080 + * criteria above; otherwise, B_FALSE;
1081 + */
1082 +
1083 +boolean_t
1084 +is_fqdn(const char *hostname)
1085 +{
1086 + const char *c;
1087 + size_t i;
1088 +
1089 + if (hostname == NULL)
1090 + return (B_FALSE);
1091 +
1092 + i = strlen(hostname);
1093 + if (i > 0 && hostname[i - 1] == '.')
1094 + return (B_TRUE);
1095 +
1096 + c = hostname;
1097 + i = 0;
1098 + while ((c = strchr(c, '.')) != NULL) {
1099 + ++i;
1100 + ++c;
1101 + }
1102 +
1103 + /* at least two separators is inferred to be fully-qualified */
1104 + return (i >= 2);
707 1105 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX