1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <netinet/in.h> /* struct in_addr */
30 #include <netinet/dhcp.h>
31 #include <signal.h>
32 #include <sys/socket.h>
33 #include <net/route.h>
34 #include <net/if_arp.h>
35 #include <string.h>
36 #include <dhcpmsg.h>
37 #include <ctype.h>
38 #include <netdb.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <dhcp_hostconf.h>
42
43 #include "states.h"
44 #include "agent.h"
45 #include "interface.h"
46 #include "util.h"
47 #include "packet.h"
48
49 /*
50 * this file contains utility functions that have no real better home
51 * of their own. they can largely be broken into six categories:
52 *
53 * o conversion functions -- functions to turn integers into strings,
54 * or to convert between units of a similar measure.
55 *
56 * o time and timer functions -- functions to handle time measurement
57 * and events.
58 *
59 * o ipc-related functions -- functions to simplify the generation of
60 * ipc messages to the agent's clients.
61 *
62 * o signal-related functions -- functions to clean up the agent when
63 * it receives a signal.
64 *
65 * o routing table manipulation functions
66 *
67 * o true miscellany -- anything else
68 */
69
70 /*
71 * pkt_type_to_string(): stringifies a packet type
72 *
73 * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
74 * boolean_t: B_TRUE if IPv6
75 * output: const char *: the stringified packet type
76 */
77
78 const char *
pkt_type_to_string(uchar_t type,boolean_t isv6)79 pkt_type_to_string(uchar_t type, boolean_t isv6)
80 {
81 /*
82 * note: the ordering in these arrays allows direct indexing of the
83 * table based on the RFC packet type value passed in.
84 */
85
86 static const char *v4types[] = {
87 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
88 "ACK", "NAK", "RELEASE", "INFORM"
89 };
90 static const char *v6types[] = {
91 NULL, "SOLICIT", "ADVERTISE", "REQUEST",
92 "CONFIRM", "RENEW", "REBIND", "REPLY",
93 "RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
94 "RELAY-FORW", "RELAY-REPL"
95 };
96
97 if (isv6) {
98 if (type >= sizeof (v6types) / sizeof (*v6types) ||
99 v6types[type] == NULL)
100 return ("<unknown>");
101 else
102 return (v6types[type]);
103 } else {
104 if (type >= sizeof (v4types) / sizeof (*v4types) ||
105 v4types[type] == NULL)
106 return ("<unknown>");
107 else
108 return (v4types[type]);
109 }
110 }
111
112 /*
113 * monosec_to_string(): converts a monosec_t into a date string
114 *
115 * input: monosec_t: the monosec_t to convert
116 * output: const char *: the corresponding date string
117 */
118
119 const char *
monosec_to_string(monosec_t monosec)120 monosec_to_string(monosec_t monosec)
121 {
122 time_t time = monosec_to_time(monosec);
123 char *time_string = ctime(&time);
124
125 /* strip off the newline -- ugh, why, why, why.. */
126 time_string[strlen(time_string) - 1] = '\0';
127 return (time_string);
128 }
129
130 /*
131 * monosec(): returns a monotonically increasing time in seconds that
132 * is not affected by stime(2) or adjtime(2).
133 *
134 * input: void
135 * output: monosec_t: the number of seconds since some time in the past
136 */
137
138 monosec_t
monosec(void)139 monosec(void)
140 {
141 return (gethrtime() / NANOSEC);
142 }
143
144 /*
145 * monosec_to_time(): converts a monosec_t into real wall time
146 *
147 * input: monosec_t: the absolute monosec_t to convert
148 * output: time_t: the absolute time that monosec_t represents in wall time
149 */
150
151 time_t
monosec_to_time(monosec_t abs_monosec)152 monosec_to_time(monosec_t abs_monosec)
153 {
154 return (abs_monosec - monosec()) + time(NULL);
155 }
156
157 /*
158 * hrtime_to_monosec(): converts a hrtime_t to monosec_t
159 *
160 * input: hrtime_t: the time to convert
161 * output: monosec_t: the time in monosec_t
162 */
163
164 monosec_t
hrtime_to_monosec(hrtime_t hrtime)165 hrtime_to_monosec(hrtime_t hrtime)
166 {
167 return (hrtime / NANOSEC);
168 }
169
170 /*
171 * print_server_msg(): prints a message from a DHCP server
172 *
173 * input: dhcp_smach_t *: the state machine the message is associated with
174 * const char *: the string to display
175 * uint_t: length of string
176 * output: void
177 */
178
179 void
print_server_msg(dhcp_smach_t * dsmp,const char * msg,uint_t msglen)180 print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
181 {
182 if (msglen > 0) {
183 dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
184 dsmp->dsm_name, msglen, msg);
185 }
186 }
187
188 /*
189 * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
190 *
191 * input: int: signal the handler was called with.
192 *
193 * output: void
194 */
195
196 static void
alrm_exit(int sig)197 alrm_exit(int sig)
198 {
199 int exitval;
200
201 if (sig == SIGALRM && grandparent != 0)
202 exitval = EXIT_SUCCESS;
203 else
204 exitval = EXIT_FAILURE;
205
206 _exit(exitval);
207 }
208
209 /*
210 * daemonize(): daemonizes the process
211 *
212 * input: void
213 * output: int: 1 on success, 0 on failure
214 */
215
216 int
daemonize(void)217 daemonize(void)
218 {
219 /*
220 * We've found that adoption takes sufficiently long that
221 * a dhcpinfo run after dhcpagent -a is started may occur
222 * before the agent is ready to process the request.
223 * The result is an error message and an unhappy user.
224 *
225 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
226 * unless interrupted by a SIGALRM, in which case it
227 * exits immediately. This has the effect that the
228 * grandparent doesn't exit until the dhcpagent is ready
229 * to process requests. This defers the the balance of
230 * the system start-up script processing until the
231 * dhcpagent is ready to field requests.
232 *
233 * grandparent is only set for the adopt case; other
234 * cases do not require the wait.
235 */
236
237 if (grandparent != 0)
238 (void) signal(SIGALRM, alrm_exit);
239
240 switch (fork()) {
241
242 case -1:
243 return (0);
244
245 case 0:
246 if (grandparent != 0)
247 (void) signal(SIGALRM, SIG_DFL);
248
249 /*
250 * setsid() makes us lose our controlling terminal,
251 * and become both a session leader and a process
252 * group leader.
253 */
254
255 (void) setsid();
256
257 /*
258 * under POSIX, a session leader can accidentally
259 * (through open(2)) acquire a controlling terminal if
260 * it does not have one. just to be safe, fork again
261 * so we are not a session leader.
262 */
263
264 switch (fork()) {
265
266 case -1:
267 return (0);
268
269 case 0:
270 (void) signal(SIGHUP, SIG_IGN);
271 (void) chdir("/");
272 (void) umask(022);
273 closefrom(0);
274 break;
275
276 default:
277 _exit(EXIT_SUCCESS);
278 }
279 break;
280
281 default:
282 if (grandparent != 0) {
283 (void) signal(SIGCHLD, SIG_IGN);
284 /*
285 * Note that we're not the agent here, so the DHCP
286 * logging subsystem hasn't been configured yet.
287 */
288 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
289 "waiting for adoption to complete.");
290 if (sleep(DHCP_ADOPT_SLEEP) == 0) {
291 syslog(LOG_WARNING | LOG_DAEMON,
292 "dhcpagent: daemonize: timed out awaiting "
293 "adoption.");
294 }
295 syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
296 "wait finished");
297 }
298 _exit(EXIT_SUCCESS);
299 }
300
301 return (1);
302 }
303
304 /*
305 * update_default_route(): update the interface's default route
306 *
307 * input: int: the type of message; either RTM_ADD or RTM_DELETE
308 * struct in_addr: the default gateway to use
309 * const char *: the interface associated with the route
310 * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
311 * output: boolean_t: B_TRUE on success, B_FALSE on failure
312 */
313
314 static boolean_t
update_default_route(uint32_t ifindex,int type,struct in_addr * gateway_nbo,int flags)315 update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo,
316 int flags)
317 {
318 struct {
319 struct rt_msghdr rm_mh;
320 struct sockaddr_in rm_dst;
321 struct sockaddr_in rm_gw;
322 struct sockaddr_in rm_mask;
323 struct sockaddr_dl rm_ifp;
324 } rtmsg;
325
326 (void) memset(&rtmsg, 0, sizeof (rtmsg));
327 rtmsg.rm_mh.rtm_version = RTM_VERSION;
328 rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg);
329 rtmsg.rm_mh.rtm_type = type;
330 rtmsg.rm_mh.rtm_pid = getpid();
331 rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags;
332 rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
333
334 rtmsg.rm_gw.sin_family = AF_INET;
335 rtmsg.rm_gw.sin_addr = *gateway_nbo;
336
337 rtmsg.rm_dst.sin_family = AF_INET;
338 rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
339
340 rtmsg.rm_mask.sin_family = AF_INET;
341 rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
342
343 rtmsg.rm_ifp.sdl_family = AF_LINK;
344 rtmsg.rm_ifp.sdl_index = ifindex;
345
346 return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
347 }
348
349 /*
350 * add_default_route(): add the default route to the given gateway
351 *
352 * input: const char *: the name of the interface associated with the route
353 * struct in_addr: the default gateway to add
354 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
355 */
356
357 boolean_t
add_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)358 add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
359 {
360 return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP));
361 }
362
363 /*
364 * del_default_route(): deletes the default route to the given gateway
365 *
366 * input: const char *: the name of the interface associated with the route
367 * struct in_addr: if not INADDR_ANY, the default gateway to remove
368 * output: boolean_t: B_TRUE on success, B_FALSE on failure
369 */
370
371 boolean_t
del_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)372 del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
373 {
374 if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
375 return (B_TRUE);
376
377 return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0));
378 }
379
380 /*
381 * inactivity_shutdown(): shuts down agent if there are no state machines left
382 * to manage
383 *
384 * input: iu_tq_t *: unused
385 * void *: unused
386 * output: void
387 */
388
389 /* ARGSUSED */
390 void
inactivity_shutdown(iu_tq_t * tqp,void * arg)391 inactivity_shutdown(iu_tq_t *tqp, void *arg)
392 {
393 if (smach_count() > 0) /* shouldn't happen, but... */
394 return;
395
396 dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
397
398 iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
399 }
400
401 /*
402 * graceful_shutdown(): shuts down the agent gracefully
403 *
404 * input: int: the signal that caused graceful_shutdown to be called
405 * output: void
406 */
407
408 void
graceful_shutdown(int sig)409 graceful_shutdown(int sig)
410 {
411 iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
412 DHCP_REASON_SIGNAL), drain_script, NULL);
413 }
414
415 /*
416 * bind_sock(): binds a socket to a given IP address and port number
417 *
418 * input: int: the socket to bind
419 * in_port_t: the port number to bind to, host byte order
420 * in_addr_t: the address to bind to, host byte order
421 * output: boolean_t: B_TRUE on success, B_FALSE on failure
422 */
423
424 boolean_t
bind_sock(int fd,in_port_t port_hbo,in_addr_t addr_hbo)425 bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
426 {
427 struct sockaddr_in sin;
428 int on = 1;
429
430 (void) memset(&sin, 0, sizeof (struct sockaddr_in));
431 sin.sin_family = AF_INET;
432 sin.sin_port = htons(port_hbo);
433 sin.sin_addr.s_addr = htonl(addr_hbo);
434
435 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
436
437 return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
438 }
439
440 /*
441 * bind_sock_v6(): binds a socket to a given IP address and port number
442 *
443 * input: int: the socket to bind
444 * in_port_t: the port number to bind to, host byte order
445 * in6_addr_t: the address to bind to, network byte order
446 * output: boolean_t: B_TRUE on success, B_FALSE on failure
447 */
448
449 boolean_t
bind_sock_v6(int fd,in_port_t port_hbo,const in6_addr_t * addr_nbo)450 bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
451 {
452 struct sockaddr_in6 sin6;
453 int on = 1;
454
455 (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
456 sin6.sin6_family = AF_INET6;
457 sin6.sin6_port = htons(port_hbo);
458 if (addr_nbo != NULL) {
459 (void) memcpy(&sin6.sin6_addr, addr_nbo,
460 sizeof (sin6.sin6_addr));
461 }
462
463 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
464
465 return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
466 }
467
468 /*
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
valid_hostname(const char * hostname)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 * iffile_to_hostname(): return the hostname contained on a line of the form
499 *
500 * [ ^I]*inet[ ^I]+hostname[\n]*\0
501 *
502 * in the file located at the specified path
503 *
504 * input: const char *: the path of the file to look in for the hostname
505 * output: const char *: the hostname at that path, or NULL on failure
506 */
507
508 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
509
510 const char *
iffile_to_hostname(const char * path)511 iffile_to_hostname(const char *path)
512 {
513 FILE *fp;
514 static char ifline[IFLINE_MAX];
515
516 fp = fopen(path, "r");
517 if (fp == NULL)
518 return (NULL);
519
520 /*
521 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
522 * such command is on a separate line (see the "while read ifcmds" code
523 * in /etc/init.d/inetinit). Thus we will read the file a line at a
524 * time, searching for a line of the form
525 *
526 * [ ^I]*inet[ ^I]+hostname[\n]*\0
527 *
528 * extract the host name from it, and check it for validity.
529 */
530 while (fgets(ifline, sizeof (ifline), fp) != NULL) {
531 char *p;
532
533 if ((p = strstr(ifline, "inet")) != NULL) {
534 if ((p != ifline) && !isspace(p[-1])) {
535 (void) fclose(fp);
536 return (NULL);
537 }
538 p += 4; /* skip over "inet" and expect spaces or tabs */
539 if ((*p == '\n') || (*p == '\0')) {
540 (void) fclose(fp);
541 return (NULL);
542 }
543 if (isspace(*p)) {
544 char *nlptr;
545
546 /* no need to read more of the file */
547 (void) fclose(fp);
548
549 while (isspace(*p))
550 p++;
551 if ((nlptr = strrchr(p, '\n')) != NULL)
552 *nlptr = '\0';
553 if (strlen(p) > MAXHOSTNAMELEN) {
554 dhcpmsg(MSG_WARNING,
555 "iffile_to_hostname:"
556 " host name too long");
557 return (NULL);
558 }
559 if (valid_hostname(p)) {
560 return (p);
561 } else {
562 dhcpmsg(MSG_WARNING,
563 "iffile_to_hostname:"
564 " host name not valid");
565 return (NULL);
566 }
567 } else {
568 (void) fclose(fp);
569 return (NULL);
570 }
571 }
572 }
573
574 (void) fclose(fp);
575 return (NULL);
576 }
577
578 /*
579 * init_timer(): set up a DHCP timer
580 *
581 * input: dhcp_timer_t *: the timer to set up
582 * output: void
583 */
584
585 void
init_timer(dhcp_timer_t * dt,lease_t startval)586 init_timer(dhcp_timer_t *dt, lease_t startval)
587 {
588 dt->dt_id = -1;
589 dt->dt_start = startval;
590 }
591
592 /*
593 * cancel_timer(): cancel a DHCP timer
594 *
595 * input: dhcp_timer_t *: the timer to cancel
596 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
597 */
598
599 boolean_t
cancel_timer(dhcp_timer_t * dt)600 cancel_timer(dhcp_timer_t *dt)
601 {
602 if (dt->dt_id == -1)
603 return (B_TRUE);
604
605 if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
606 dt->dt_id = -1;
607 return (B_TRUE);
608 }
609
610 return (B_FALSE);
611 }
612
613 /*
614 * schedule_timer(): schedule a DHCP timer. Note that it must not be already
615 * running, and that we can't cancel here. If it were, and
616 * we did, we'd leak a reference to the callback argument.
617 *
618 * input: dhcp_timer_t *: the timer to schedule
619 * output: boolean_t: B_TRUE on success, B_FALSE otherwise
620 */
621
622 boolean_t
schedule_timer(dhcp_timer_t * dt,iu_tq_callback_t * cbfunc,void * arg)623 schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
624 {
625 if (dt->dt_id != -1)
626 return (B_FALSE);
627 dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
628 return (dt->dt_id != -1);
629 }
630
631 /*
632 * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
633 * buffer.
634 *
635 * input: const dhcpv6_option_t *: pointer to option
636 * uint_t: option length
637 * const char **: error string (nul-terminated)
638 * const char **: message from server (unterminated)
639 * uint_t *: length of server message
640 * output: int: -1 on error, or >= 0 for a DHCPv6 status code
641 */
642
643 int
dhcpv6_status_code(const dhcpv6_option_t * d6o,uint_t olen,const char ** estr,const char ** msg,uint_t * msglenp)644 dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
645 const char **msg, uint_t *msglenp)
646 {
647 uint16_t status;
648 static const char *v6_status[] = {
649 NULL,
650 "Unknown reason",
651 "Server has no addresses available",
652 "Client record unavailable",
653 "Prefix inappropriate for link",
654 "Client must use multicast",
655 "No prefix available"
656 };
657 static char sbuf[32];
658
659 *estr = "";
660 *msg = "";
661 *msglenp = 0;
662 if (d6o == NULL)
663 return (0);
664 olen -= sizeof (*d6o);
665 if (olen < 2) {
666 *estr = "garbled status code";
667 return (-1);
668 }
669
670 *msg = (const char *)(d6o + 1) + 2;
671 *msglenp = olen - 2;
672
673 (void) memcpy(&status, d6o + 1, sizeof (status));
674 status = ntohs(status);
675 if (status > 0) {
676 if (status > DHCPV6_STAT_NOPREFIX) {
677 (void) snprintf(sbuf, sizeof (sbuf), "status %u",
678 status);
679 *estr = sbuf;
680 } else {
681 *estr = v6_status[status];
682 }
683 }
684 return (status);
685 }
686
687 void
write_lease_to_hostconf(dhcp_smach_t * dsmp)688 write_lease_to_hostconf(dhcp_smach_t *dsmp)
689 {
690 PKT_LIST *plp[2];
691 const char *hcfile;
692
693 hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
694 plp[0] = dsmp->dsm_ack;
695 plp[1] = dsmp->dsm_orig_ack;
696 if (write_hostconf(dsmp->dsm_name, plp, 2,
697 monosec_to_time(dsmp->dsm_curstart_monosec),
698 dsmp->dsm_isv6) != -1) {
699 dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
700 } else if (errno == EROFS) {
701 dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
702 "system; not saving lease", hcfile);
703 } else {
704 dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
705 "not use cached configuration)", hcfile);
706 }
707 }
708