17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5842620c7Smeem * Common Development and Distribution License (the "License").
6842620c7Smeem * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22*0a3e1f6cSVasumathi Sundaram * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate */
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate #include <unistd.h>
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/stat.h>
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <netinet/in.h> /* struct in_addr */
307c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
317c478bd9Sstevel@tonic-gate #include <signal.h>
327c478bd9Sstevel@tonic-gate #include <sys/socket.h>
337c478bd9Sstevel@tonic-gate #include <net/route.h>
347c478bd9Sstevel@tonic-gate #include <net/if_arp.h>
357c478bd9Sstevel@tonic-gate #include <string.h>
367c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
377c478bd9Sstevel@tonic-gate #include <ctype.h>
387c478bd9Sstevel@tonic-gate #include <netdb.h>
397c478bd9Sstevel@tonic-gate #include <fcntl.h>
407c478bd9Sstevel@tonic-gate #include <stdio.h>
41*0a3e1f6cSVasumathi Sundaram #include <dhcp_hostconf.h>
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate #include "states.h"
447c478bd9Sstevel@tonic-gate #include "agent.h"
457c478bd9Sstevel@tonic-gate #include "interface.h"
467c478bd9Sstevel@tonic-gate #include "util.h"
477c478bd9Sstevel@tonic-gate #include "packet.h"
487c478bd9Sstevel@tonic-gate
497c478bd9Sstevel@tonic-gate /*
507c478bd9Sstevel@tonic-gate * this file contains utility functions that have no real better home
517c478bd9Sstevel@tonic-gate * of their own. they can largely be broken into six categories:
527c478bd9Sstevel@tonic-gate *
537c478bd9Sstevel@tonic-gate * o conversion functions -- functions to turn integers into strings,
547c478bd9Sstevel@tonic-gate * or to convert between units of a similar measure.
557c478bd9Sstevel@tonic-gate *
56d04ccbb3Scarlsonj * o time and timer functions -- functions to handle time measurement
57d04ccbb3Scarlsonj * and events.
58d04ccbb3Scarlsonj *
597c478bd9Sstevel@tonic-gate * o ipc-related functions -- functions to simplify the generation of
607c478bd9Sstevel@tonic-gate * ipc messages to the agent's clients.
617c478bd9Sstevel@tonic-gate *
627c478bd9Sstevel@tonic-gate * o signal-related functions -- functions to clean up the agent when
637c478bd9Sstevel@tonic-gate * it receives a signal.
647c478bd9Sstevel@tonic-gate *
657c478bd9Sstevel@tonic-gate * o routing table manipulation functions
667c478bd9Sstevel@tonic-gate *
677c478bd9Sstevel@tonic-gate * o true miscellany -- anything else
687c478bd9Sstevel@tonic-gate */
697c478bd9Sstevel@tonic-gate
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate * pkt_type_to_string(): stringifies a packet type
727c478bd9Sstevel@tonic-gate *
73d04ccbb3Scarlsonj * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
74d04ccbb3Scarlsonj * boolean_t: B_TRUE if IPv6
757c478bd9Sstevel@tonic-gate * output: const char *: the stringified packet type
767c478bd9Sstevel@tonic-gate */
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate const char *
pkt_type_to_string(uchar_t type,boolean_t isv6)79d04ccbb3Scarlsonj pkt_type_to_string(uchar_t type, boolean_t isv6)
807c478bd9Sstevel@tonic-gate {
817c478bd9Sstevel@tonic-gate /*
82d04ccbb3Scarlsonj * note: the ordering in these arrays allows direct indexing of the
83d04ccbb3Scarlsonj * table based on the RFC packet type value passed in.
847c478bd9Sstevel@tonic-gate */
857c478bd9Sstevel@tonic-gate
86d04ccbb3Scarlsonj static const char *v4types[] = {
877c478bd9Sstevel@tonic-gate "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
887c478bd9Sstevel@tonic-gate "ACK", "NAK", "RELEASE", "INFORM"
897c478bd9Sstevel@tonic-gate };
90d04ccbb3Scarlsonj static const char *v6types[] = {
91d04ccbb3Scarlsonj NULL, "SOLICIT", "ADVERTISE", "REQUEST",
92d04ccbb3Scarlsonj "CONFIRM", "RENEW", "REBIND", "REPLY",
93d04ccbb3Scarlsonj "RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
94d04ccbb3Scarlsonj "RELAY-FORW", "RELAY-REPL"
95d04ccbb3Scarlsonj };
967c478bd9Sstevel@tonic-gate
97d04ccbb3Scarlsonj if (isv6) {
98d04ccbb3Scarlsonj if (type >= sizeof (v6types) / sizeof (*v6types) ||
99d04ccbb3Scarlsonj v6types[type] == NULL)
1007c478bd9Sstevel@tonic-gate return ("<unknown>");
101d04ccbb3Scarlsonj else
102d04ccbb3Scarlsonj return (v6types[type]);
103d04ccbb3Scarlsonj } else {
104d04ccbb3Scarlsonj if (type >= sizeof (v4types) / sizeof (*v4types) ||
105d04ccbb3Scarlsonj v4types[type] == NULL)
106d04ccbb3Scarlsonj return ("<unknown>");
107d04ccbb3Scarlsonj else
108d04ccbb3Scarlsonj return (v4types[type]);
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate }
1117c478bd9Sstevel@tonic-gate
1127c478bd9Sstevel@tonic-gate /*
1137c478bd9Sstevel@tonic-gate * monosec_to_string(): converts a monosec_t into a date string
1147c478bd9Sstevel@tonic-gate *
1157c478bd9Sstevel@tonic-gate * input: monosec_t: the monosec_t to convert
1167c478bd9Sstevel@tonic-gate * output: const char *: the corresponding date string
1177c478bd9Sstevel@tonic-gate */
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate const char *
monosec_to_string(monosec_t monosec)1207c478bd9Sstevel@tonic-gate monosec_to_string(monosec_t monosec)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate time_t time = monosec_to_time(monosec);
1237c478bd9Sstevel@tonic-gate char *time_string = ctime(&time);
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate /* strip off the newline -- ugh, why, why, why.. */
1267c478bd9Sstevel@tonic-gate time_string[strlen(time_string) - 1] = '\0';
1277c478bd9Sstevel@tonic-gate return (time_string);
1287c478bd9Sstevel@tonic-gate }
1297c478bd9Sstevel@tonic-gate
1307c478bd9Sstevel@tonic-gate /*
1317c478bd9Sstevel@tonic-gate * monosec(): returns a monotonically increasing time in seconds that
1327c478bd9Sstevel@tonic-gate * is not affected by stime(2) or adjtime(2).
1337c478bd9Sstevel@tonic-gate *
1347c478bd9Sstevel@tonic-gate * input: void
1357c478bd9Sstevel@tonic-gate * output: monosec_t: the number of seconds since some time in the past
1367c478bd9Sstevel@tonic-gate */
1377c478bd9Sstevel@tonic-gate
1387c478bd9Sstevel@tonic-gate monosec_t
monosec(void)1397c478bd9Sstevel@tonic-gate monosec(void)
1407c478bd9Sstevel@tonic-gate {
1417c478bd9Sstevel@tonic-gate return (gethrtime() / NANOSEC);
1427c478bd9Sstevel@tonic-gate }
1437c478bd9Sstevel@tonic-gate
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate * monosec_to_time(): converts a monosec_t into real wall time
1467c478bd9Sstevel@tonic-gate *
1477c478bd9Sstevel@tonic-gate * input: monosec_t: the absolute monosec_t to convert
1487c478bd9Sstevel@tonic-gate * output: time_t: the absolute time that monosec_t represents in wall time
1497c478bd9Sstevel@tonic-gate */
1507c478bd9Sstevel@tonic-gate
1517c478bd9Sstevel@tonic-gate time_t
monosec_to_time(monosec_t abs_monosec)1527c478bd9Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec)
1537c478bd9Sstevel@tonic-gate {
1547c478bd9Sstevel@tonic-gate return (abs_monosec - monosec()) + time(NULL);
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate
1577c478bd9Sstevel@tonic-gate /*
158d04ccbb3Scarlsonj * hrtime_to_monosec(): converts a hrtime_t to monosec_t
1597c478bd9Sstevel@tonic-gate *
160d04ccbb3Scarlsonj * input: hrtime_t: the time to convert
161d04ccbb3Scarlsonj * output: monosec_t: the time in monosec_t
1627c478bd9Sstevel@tonic-gate */
1637c478bd9Sstevel@tonic-gate
164d04ccbb3Scarlsonj monosec_t
hrtime_to_monosec(hrtime_t hrtime)165d04ccbb3Scarlsonj hrtime_to_monosec(hrtime_t hrtime)
1667c478bd9Sstevel@tonic-gate {
167d04ccbb3Scarlsonj return (hrtime / NANOSEC);
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate
1707c478bd9Sstevel@tonic-gate /*
1717c478bd9Sstevel@tonic-gate * print_server_msg(): prints a message from a DHCP server
1727c478bd9Sstevel@tonic-gate *
173d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine the message is associated with
174d04ccbb3Scarlsonj * const char *: the string to display
175d04ccbb3Scarlsonj * uint_t: length of string
1767c478bd9Sstevel@tonic-gate * output: void
1777c478bd9Sstevel@tonic-gate */
1787c478bd9Sstevel@tonic-gate
1797c478bd9Sstevel@tonic-gate void
print_server_msg(dhcp_smach_t * dsmp,const char * msg,uint_t msglen)180d04ccbb3Scarlsonj print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
1817c478bd9Sstevel@tonic-gate {
182d04ccbb3Scarlsonj if (msglen > 0) {
183d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
184d04ccbb3Scarlsonj dsmp->dsm_name, msglen, msg);
185d04ccbb3Scarlsonj }
1867c478bd9Sstevel@tonic-gate }
1877c478bd9Sstevel@tonic-gate
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
1907c478bd9Sstevel@tonic-gate *
1917c478bd9Sstevel@tonic-gate * input: int: signal the handler was called with.
1927c478bd9Sstevel@tonic-gate *
1937c478bd9Sstevel@tonic-gate * output: void
1947c478bd9Sstevel@tonic-gate */
1957c478bd9Sstevel@tonic-gate
1967c478bd9Sstevel@tonic-gate static void
alrm_exit(int sig)1977c478bd9Sstevel@tonic-gate alrm_exit(int sig)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate int exitval;
2007c478bd9Sstevel@tonic-gate
2017c478bd9Sstevel@tonic-gate if (sig == SIGALRM && grandparent != 0)
2027c478bd9Sstevel@tonic-gate exitval = EXIT_SUCCESS;
2037c478bd9Sstevel@tonic-gate else
2047c478bd9Sstevel@tonic-gate exitval = EXIT_FAILURE;
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate _exit(exitval);
2077c478bd9Sstevel@tonic-gate }
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate * daemonize(): daemonizes the process
2117c478bd9Sstevel@tonic-gate *
2127c478bd9Sstevel@tonic-gate * input: void
2137c478bd9Sstevel@tonic-gate * output: int: 1 on success, 0 on failure
2147c478bd9Sstevel@tonic-gate */
2157c478bd9Sstevel@tonic-gate
2167c478bd9Sstevel@tonic-gate int
daemonize(void)2177c478bd9Sstevel@tonic-gate daemonize(void)
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate * We've found that adoption takes sufficiently long that
2217c478bd9Sstevel@tonic-gate * a dhcpinfo run after dhcpagent -a is started may occur
2227c478bd9Sstevel@tonic-gate * before the agent is ready to process the request.
2237c478bd9Sstevel@tonic-gate * The result is an error message and an unhappy user.
2247c478bd9Sstevel@tonic-gate *
2257c478bd9Sstevel@tonic-gate * The initial process now sleeps for DHCP_ADOPT_SLEEP,
2267c478bd9Sstevel@tonic-gate * unless interrupted by a SIGALRM, in which case it
2277c478bd9Sstevel@tonic-gate * exits immediately. This has the effect that the
2287c478bd9Sstevel@tonic-gate * grandparent doesn't exit until the dhcpagent is ready
2297c478bd9Sstevel@tonic-gate * to process requests. This defers the the balance of
2307c478bd9Sstevel@tonic-gate * the system start-up script processing until the
2317c478bd9Sstevel@tonic-gate * dhcpagent is ready to field requests.
2327c478bd9Sstevel@tonic-gate *
2337c478bd9Sstevel@tonic-gate * grandparent is only set for the adopt case; other
2347c478bd9Sstevel@tonic-gate * cases do not require the wait.
2357c478bd9Sstevel@tonic-gate */
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate if (grandparent != 0)
2387c478bd9Sstevel@tonic-gate (void) signal(SIGALRM, alrm_exit);
2397c478bd9Sstevel@tonic-gate
2407c478bd9Sstevel@tonic-gate switch (fork()) {
2417c478bd9Sstevel@tonic-gate
2427c478bd9Sstevel@tonic-gate case -1:
2437c478bd9Sstevel@tonic-gate return (0);
2447c478bd9Sstevel@tonic-gate
2457c478bd9Sstevel@tonic-gate case 0:
2467c478bd9Sstevel@tonic-gate if (grandparent != 0)
2477c478bd9Sstevel@tonic-gate (void) signal(SIGALRM, SIG_DFL);
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate * setsid() makes us lose our controlling terminal,
2517c478bd9Sstevel@tonic-gate * and become both a session leader and a process
2527c478bd9Sstevel@tonic-gate * group leader.
2537c478bd9Sstevel@tonic-gate */
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate (void) setsid();
2567c478bd9Sstevel@tonic-gate
2577c478bd9Sstevel@tonic-gate /*
2587c478bd9Sstevel@tonic-gate * under POSIX, a session leader can accidentally
2597c478bd9Sstevel@tonic-gate * (through open(2)) acquire a controlling terminal if
2607c478bd9Sstevel@tonic-gate * it does not have one. just to be safe, fork again
2617c478bd9Sstevel@tonic-gate * so we are not a session leader.
2627c478bd9Sstevel@tonic-gate */
2637c478bd9Sstevel@tonic-gate
2647c478bd9Sstevel@tonic-gate switch (fork()) {
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate case -1:
2677c478bd9Sstevel@tonic-gate return (0);
2687c478bd9Sstevel@tonic-gate
2697c478bd9Sstevel@tonic-gate case 0:
2707c478bd9Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN);
2717c478bd9Sstevel@tonic-gate (void) chdir("/");
2727c478bd9Sstevel@tonic-gate (void) umask(022);
2737c478bd9Sstevel@tonic-gate closefrom(0);
2747c478bd9Sstevel@tonic-gate break;
2757c478bd9Sstevel@tonic-gate
2767c478bd9Sstevel@tonic-gate default:
2777c478bd9Sstevel@tonic-gate _exit(EXIT_SUCCESS);
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate break;
2807c478bd9Sstevel@tonic-gate
2817c478bd9Sstevel@tonic-gate default:
2827c478bd9Sstevel@tonic-gate if (grandparent != 0) {
2837c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN);
284d04ccbb3Scarlsonj /*
285d04ccbb3Scarlsonj * Note that we're not the agent here, so the DHCP
286d04ccbb3Scarlsonj * logging subsystem hasn't been configured yet.
287d04ccbb3Scarlsonj */
288d04ccbb3Scarlsonj syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
2897c478bd9Sstevel@tonic-gate "waiting for adoption to complete.");
2907c478bd9Sstevel@tonic-gate if (sleep(DHCP_ADOPT_SLEEP) == 0) {
291d04ccbb3Scarlsonj syslog(LOG_WARNING | LOG_DAEMON,
292d04ccbb3Scarlsonj "dhcpagent: daemonize: timed out awaiting "
293d04ccbb3Scarlsonj "adoption.");
2947c478bd9Sstevel@tonic-gate }
295d04ccbb3Scarlsonj syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
296d04ccbb3Scarlsonj "wait finished");
2977c478bd9Sstevel@tonic-gate }
2987c478bd9Sstevel@tonic-gate _exit(EXIT_SUCCESS);
2997c478bd9Sstevel@tonic-gate }
3007c478bd9Sstevel@tonic-gate
3017c478bd9Sstevel@tonic-gate return (1);
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate * update_default_route(): update the interface's default route
3067c478bd9Sstevel@tonic-gate *
3077c478bd9Sstevel@tonic-gate * input: int: the type of message; either RTM_ADD or RTM_DELETE
3087c478bd9Sstevel@tonic-gate * struct in_addr: the default gateway to use
3097c478bd9Sstevel@tonic-gate * const char *: the interface associated with the route
3107c478bd9Sstevel@tonic-gate * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
311d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
3127c478bd9Sstevel@tonic-gate */
3137c478bd9Sstevel@tonic-gate
314d04ccbb3Scarlsonj static boolean_t
update_default_route(uint32_t ifindex,int type,struct in_addr * gateway_nbo,int flags)315cfb9c9abScarlsonj update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo,
3167c478bd9Sstevel@tonic-gate int flags)
3177c478bd9Sstevel@tonic-gate {
3187c478bd9Sstevel@tonic-gate struct {
3197c478bd9Sstevel@tonic-gate struct rt_msghdr rm_mh;
3207c478bd9Sstevel@tonic-gate struct sockaddr_in rm_dst;
3217c478bd9Sstevel@tonic-gate struct sockaddr_in rm_gw;
3227c478bd9Sstevel@tonic-gate struct sockaddr_in rm_mask;
3237c478bd9Sstevel@tonic-gate struct sockaddr_dl rm_ifp;
3247c478bd9Sstevel@tonic-gate } rtmsg;
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate (void) memset(&rtmsg, 0, sizeof (rtmsg));
3277c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_version = RTM_VERSION;
3287c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg);
3297c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_type = type;
3307c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_pid = getpid();
3317c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags;
3327c478bd9Sstevel@tonic-gate rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
3337c478bd9Sstevel@tonic-gate
3347c478bd9Sstevel@tonic-gate rtmsg.rm_gw.sin_family = AF_INET;
3357c478bd9Sstevel@tonic-gate rtmsg.rm_gw.sin_addr = *gateway_nbo;
3367c478bd9Sstevel@tonic-gate
3377c478bd9Sstevel@tonic-gate rtmsg.rm_dst.sin_family = AF_INET;
3387c478bd9Sstevel@tonic-gate rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate rtmsg.rm_mask.sin_family = AF_INET;
3417c478bd9Sstevel@tonic-gate rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
3427c478bd9Sstevel@tonic-gate
3437c478bd9Sstevel@tonic-gate rtmsg.rm_ifp.sdl_family = AF_LINK;
344cfb9c9abScarlsonj rtmsg.rm_ifp.sdl_index = ifindex;
3457c478bd9Sstevel@tonic-gate
3467c478bd9Sstevel@tonic-gate return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate
3497c478bd9Sstevel@tonic-gate /*
3507c478bd9Sstevel@tonic-gate * add_default_route(): add the default route to the given gateway
3517c478bd9Sstevel@tonic-gate *
3527c478bd9Sstevel@tonic-gate * input: const char *: the name of the interface associated with the route
3537c478bd9Sstevel@tonic-gate * struct in_addr: the default gateway to add
354d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE otherwise
3557c478bd9Sstevel@tonic-gate */
3567c478bd9Sstevel@tonic-gate
357d04ccbb3Scarlsonj boolean_t
add_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)358cfb9c9abScarlsonj add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3597c478bd9Sstevel@tonic-gate {
360cfb9c9abScarlsonj return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP));
3617c478bd9Sstevel@tonic-gate }
3627c478bd9Sstevel@tonic-gate
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate * del_default_route(): deletes the default route to the given gateway
3657c478bd9Sstevel@tonic-gate *
3667c478bd9Sstevel@tonic-gate * input: const char *: the name of the interface associated with the route
3677c478bd9Sstevel@tonic-gate * struct in_addr: if not INADDR_ANY, the default gateway to remove
368d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
3697c478bd9Sstevel@tonic-gate */
3707c478bd9Sstevel@tonic-gate
371d04ccbb3Scarlsonj boolean_t
del_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)372cfb9c9abScarlsonj del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3737c478bd9Sstevel@tonic-gate {
3747c478bd9Sstevel@tonic-gate if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
375d04ccbb3Scarlsonj return (B_TRUE);
3767c478bd9Sstevel@tonic-gate
377cfb9c9abScarlsonj return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0));
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate
3807c478bd9Sstevel@tonic-gate /*
381d04ccbb3Scarlsonj * inactivity_shutdown(): shuts down agent if there are no state machines left
382d04ccbb3Scarlsonj * to manage
3837c478bd9Sstevel@tonic-gate *
3847c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused
3857c478bd9Sstevel@tonic-gate * void *: unused
3867c478bd9Sstevel@tonic-gate * output: void
3877c478bd9Sstevel@tonic-gate */
3887c478bd9Sstevel@tonic-gate
3897c478bd9Sstevel@tonic-gate /* ARGSUSED */
3907c478bd9Sstevel@tonic-gate void
inactivity_shutdown(iu_tq_t * tqp,void * arg)3917c478bd9Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
3927c478bd9Sstevel@tonic-gate {
393d04ccbb3Scarlsonj if (smach_count() > 0) /* shouldn't happen, but... */
3947c478bd9Sstevel@tonic-gate return;
3957c478bd9Sstevel@tonic-gate
396d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
397d04ccbb3Scarlsonj
3987c478bd9Sstevel@tonic-gate iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate
4017c478bd9Sstevel@tonic-gate /*
4027c478bd9Sstevel@tonic-gate * graceful_shutdown(): shuts down the agent gracefully
4037c478bd9Sstevel@tonic-gate *
4047c478bd9Sstevel@tonic-gate * input: int: the signal that caused graceful_shutdown to be called
4057c478bd9Sstevel@tonic-gate * output: void
4067c478bd9Sstevel@tonic-gate */
4077c478bd9Sstevel@tonic-gate
4087c478bd9Sstevel@tonic-gate void
graceful_shutdown(int sig)4097c478bd9Sstevel@tonic-gate graceful_shutdown(int sig)
4107c478bd9Sstevel@tonic-gate {
411842620c7Smeem iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
412842620c7Smeem DHCP_REASON_SIGNAL), drain_script, NULL);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate * bind_sock(): binds a socket to a given IP address and port number
4177c478bd9Sstevel@tonic-gate *
4187c478bd9Sstevel@tonic-gate * input: int: the socket to bind
4197c478bd9Sstevel@tonic-gate * in_port_t: the port number to bind to, host byte order
4207c478bd9Sstevel@tonic-gate * in_addr_t: the address to bind to, host byte order
421d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
4227c478bd9Sstevel@tonic-gate */
4237c478bd9Sstevel@tonic-gate
424d04ccbb3Scarlsonj boolean_t
bind_sock(int fd,in_port_t port_hbo,in_addr_t addr_hbo)4257c478bd9Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
4267c478bd9Sstevel@tonic-gate {
4277c478bd9Sstevel@tonic-gate struct sockaddr_in sin;
4287c478bd9Sstevel@tonic-gate int on = 1;
4297c478bd9Sstevel@tonic-gate
4307c478bd9Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (struct sockaddr_in));
4317c478bd9Sstevel@tonic-gate sin.sin_family = AF_INET;
4327c478bd9Sstevel@tonic-gate sin.sin_port = htons(port_hbo);
4337c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(addr_hbo);
4347c478bd9Sstevel@tonic-gate
4357c478bd9Sstevel@tonic-gate (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4367c478bd9Sstevel@tonic-gate
4377c478bd9Sstevel@tonic-gate return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
4387c478bd9Sstevel@tonic-gate }
4397c478bd9Sstevel@tonic-gate
4407c478bd9Sstevel@tonic-gate /*
441d04ccbb3Scarlsonj * bind_sock_v6(): binds a socket to a given IP address and port number
442d04ccbb3Scarlsonj *
443d04ccbb3Scarlsonj * input: int: the socket to bind
444d04ccbb3Scarlsonj * in_port_t: the port number to bind to, host byte order
445d04ccbb3Scarlsonj * in6_addr_t: the address to bind to, network byte order
446d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
447d04ccbb3Scarlsonj */
448d04ccbb3Scarlsonj
449d04ccbb3Scarlsonj boolean_t
bind_sock_v6(int fd,in_port_t port_hbo,const in6_addr_t * addr_nbo)450d04ccbb3Scarlsonj bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
451d04ccbb3Scarlsonj {
452d04ccbb3Scarlsonj struct sockaddr_in6 sin6;
453d04ccbb3Scarlsonj int on = 1;
454d04ccbb3Scarlsonj
455d04ccbb3Scarlsonj (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
456d04ccbb3Scarlsonj sin6.sin6_family = AF_INET6;
457d04ccbb3Scarlsonj sin6.sin6_port = htons(port_hbo);
458d04ccbb3Scarlsonj if (addr_nbo != NULL) {
459d04ccbb3Scarlsonj (void) memcpy(&sin6.sin6_addr, addr_nbo,
460d04ccbb3Scarlsonj sizeof (sin6.sin6_addr));
461d04ccbb3Scarlsonj }
462d04ccbb3Scarlsonj
463d04ccbb3Scarlsonj (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
464d04ccbb3Scarlsonj
465d04ccbb3Scarlsonj return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
466d04ccbb3Scarlsonj }
467d04ccbb3Scarlsonj
468d04ccbb3Scarlsonj /*
4697c478bd9Sstevel@tonic-gate * valid_hostname(): check whether a string is a valid hostname
4707c478bd9Sstevel@tonic-gate *
4717c478bd9Sstevel@tonic-gate * input: const char *: the string to verify as a hostname
4727c478bd9Sstevel@tonic-gate * output: boolean_t: B_TRUE if the string is a valid hostname
4737c478bd9Sstevel@tonic-gate *
4747c478bd9Sstevel@tonic-gate * Note that we accept both host names beginning with a digit and
4757c478bd9Sstevel@tonic-gate * those containing hyphens. Neither is strictly legal according
4767c478bd9Sstevel@tonic-gate * to the RFCs, but both are in common practice, so we endeavour
4777c478bd9Sstevel@tonic-gate * to not break what customers are using.
4787c478bd9Sstevel@tonic-gate */
4797c478bd9Sstevel@tonic-gate
4807c478bd9Sstevel@tonic-gate static boolean_t
valid_hostname(const char * hostname)4817c478bd9Sstevel@tonic-gate valid_hostname(const char *hostname)
4827c478bd9Sstevel@tonic-gate {
4837c478bd9Sstevel@tonic-gate unsigned int i;
4847c478bd9Sstevel@tonic-gate
4857c478bd9Sstevel@tonic-gate for (i = 0; hostname[i] != '\0'; i++) {
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
4887c478bd9Sstevel@tonic-gate (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
4897c478bd9Sstevel@tonic-gate continue;
4907c478bd9Sstevel@tonic-gate
4917c478bd9Sstevel@tonic-gate return (B_FALSE);
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate
4947c478bd9Sstevel@tonic-gate return (i > 0);
4957c478bd9Sstevel@tonic-gate }
4967c478bd9Sstevel@tonic-gate
4977c478bd9Sstevel@tonic-gate /*
4987c478bd9Sstevel@tonic-gate * iffile_to_hostname(): return the hostname contained on a line of the form
4997c478bd9Sstevel@tonic-gate *
5007c478bd9Sstevel@tonic-gate * [ ^I]*inet[ ^I]+hostname[\n]*\0
5017c478bd9Sstevel@tonic-gate *
5027c478bd9Sstevel@tonic-gate * in the file located at the specified path
5037c478bd9Sstevel@tonic-gate *
5047c478bd9Sstevel@tonic-gate * input: const char *: the path of the file to look in for the hostname
5057c478bd9Sstevel@tonic-gate * output: const char *: the hostname at that path, or NULL on failure
5067c478bd9Sstevel@tonic-gate */
5077c478bd9Sstevel@tonic-gate
5087c478bd9Sstevel@tonic-gate #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate const char *
iffile_to_hostname(const char * path)5117c478bd9Sstevel@tonic-gate iffile_to_hostname(const char *path)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate FILE *fp;
5147c478bd9Sstevel@tonic-gate static char ifline[IFLINE_MAX];
5157c478bd9Sstevel@tonic-gate
5167c478bd9Sstevel@tonic-gate fp = fopen(path, "r");
5177c478bd9Sstevel@tonic-gate if (fp == NULL)
5187c478bd9Sstevel@tonic-gate return (NULL);
5197c478bd9Sstevel@tonic-gate
5207c478bd9Sstevel@tonic-gate /*
5217c478bd9Sstevel@tonic-gate * /etc/hostname.<if> may contain multiple ifconfig commands, but each
5227c478bd9Sstevel@tonic-gate * such command is on a separate line (see the "while read ifcmds" code
5237c478bd9Sstevel@tonic-gate * in /etc/init.d/inetinit). Thus we will read the file a line at a
5247c478bd9Sstevel@tonic-gate * time, searching for a line of the form
5257c478bd9Sstevel@tonic-gate *
5267c478bd9Sstevel@tonic-gate * [ ^I]*inet[ ^I]+hostname[\n]*\0
5277c478bd9Sstevel@tonic-gate *
5287c478bd9Sstevel@tonic-gate * extract the host name from it, and check it for validity.
5297c478bd9Sstevel@tonic-gate */
5307c478bd9Sstevel@tonic-gate while (fgets(ifline, sizeof (ifline), fp) != NULL) {
5317c478bd9Sstevel@tonic-gate char *p;
5327c478bd9Sstevel@tonic-gate
5337c478bd9Sstevel@tonic-gate if ((p = strstr(ifline, "inet")) != NULL) {
5347c478bd9Sstevel@tonic-gate if ((p != ifline) && !isspace(p[-1])) {
5357c478bd9Sstevel@tonic-gate (void) fclose(fp);
5367c478bd9Sstevel@tonic-gate return (NULL);
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate p += 4; /* skip over "inet" and expect spaces or tabs */
5397c478bd9Sstevel@tonic-gate if ((*p == '\n') || (*p == '\0')) {
5407c478bd9Sstevel@tonic-gate (void) fclose(fp);
5417c478bd9Sstevel@tonic-gate return (NULL);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate if (isspace(*p)) {
5447c478bd9Sstevel@tonic-gate char *nlptr;
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate /* no need to read more of the file */
5477c478bd9Sstevel@tonic-gate (void) fclose(fp);
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate while (isspace(*p))
5507c478bd9Sstevel@tonic-gate p++;
5517c478bd9Sstevel@tonic-gate if ((nlptr = strrchr(p, '\n')) != NULL)
5527c478bd9Sstevel@tonic-gate *nlptr = '\0';
5537c478bd9Sstevel@tonic-gate if (strlen(p) > MAXHOSTNAMELEN) {
5547c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING,
5557c478bd9Sstevel@tonic-gate "iffile_to_hostname:"
5567c478bd9Sstevel@tonic-gate " host name too long");
5577c478bd9Sstevel@tonic-gate return (NULL);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate if (valid_hostname(p)) {
5607c478bd9Sstevel@tonic-gate return (p);
5617c478bd9Sstevel@tonic-gate } else {
5627c478bd9Sstevel@tonic-gate dhcpmsg(MSG_WARNING,
5637c478bd9Sstevel@tonic-gate "iffile_to_hostname:"
5647c478bd9Sstevel@tonic-gate " host name not valid");
5657c478bd9Sstevel@tonic-gate return (NULL);
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate } else {
5687c478bd9Sstevel@tonic-gate (void) fclose(fp);
5697c478bd9Sstevel@tonic-gate return (NULL);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate }
5727c478bd9Sstevel@tonic-gate }
5737c478bd9Sstevel@tonic-gate
5747c478bd9Sstevel@tonic-gate (void) fclose(fp);
5757c478bd9Sstevel@tonic-gate return (NULL);
5767c478bd9Sstevel@tonic-gate }
577d04ccbb3Scarlsonj
578d04ccbb3Scarlsonj /*
579d04ccbb3Scarlsonj * init_timer(): set up a DHCP timer
580d04ccbb3Scarlsonj *
581d04ccbb3Scarlsonj * input: dhcp_timer_t *: the timer to set up
582d04ccbb3Scarlsonj * output: void
583d04ccbb3Scarlsonj */
584d04ccbb3Scarlsonj
585d04ccbb3Scarlsonj void
init_timer(dhcp_timer_t * dt,lease_t startval)586d04ccbb3Scarlsonj init_timer(dhcp_timer_t *dt, lease_t startval)
587d04ccbb3Scarlsonj {
588d04ccbb3Scarlsonj dt->dt_id = -1;
589d04ccbb3Scarlsonj dt->dt_start = startval;
590d04ccbb3Scarlsonj }
591d04ccbb3Scarlsonj
592d04ccbb3Scarlsonj /*
593d04ccbb3Scarlsonj * cancel_timer(): cancel a DHCP timer
594d04ccbb3Scarlsonj *
595d04ccbb3Scarlsonj * input: dhcp_timer_t *: the timer to cancel
596d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE otherwise
597d04ccbb3Scarlsonj */
598d04ccbb3Scarlsonj
599d04ccbb3Scarlsonj boolean_t
cancel_timer(dhcp_timer_t * dt)600d04ccbb3Scarlsonj cancel_timer(dhcp_timer_t *dt)
601d04ccbb3Scarlsonj {
602d04ccbb3Scarlsonj if (dt->dt_id == -1)
603d04ccbb3Scarlsonj return (B_TRUE);
604d04ccbb3Scarlsonj
605d04ccbb3Scarlsonj if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
606d04ccbb3Scarlsonj dt->dt_id = -1;
607d04ccbb3Scarlsonj return (B_TRUE);
608d04ccbb3Scarlsonj }
609d04ccbb3Scarlsonj
610d04ccbb3Scarlsonj return (B_FALSE);
611d04ccbb3Scarlsonj }
612d04ccbb3Scarlsonj
613d04ccbb3Scarlsonj /*
614d04ccbb3Scarlsonj * schedule_timer(): schedule a DHCP timer. Note that it must not be already
615d04ccbb3Scarlsonj * running, and that we can't cancel here. If it were, and
616d04ccbb3Scarlsonj * we did, we'd leak a reference to the callback argument.
617d04ccbb3Scarlsonj *
618d04ccbb3Scarlsonj * input: dhcp_timer_t *: the timer to schedule
619d04ccbb3Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE otherwise
620d04ccbb3Scarlsonj */
621d04ccbb3Scarlsonj
622d04ccbb3Scarlsonj boolean_t
schedule_timer(dhcp_timer_t * dt,iu_tq_callback_t * cbfunc,void * arg)623d04ccbb3Scarlsonj schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
624d04ccbb3Scarlsonj {
625d04ccbb3Scarlsonj if (dt->dt_id != -1)
626d04ccbb3Scarlsonj return (B_FALSE);
627d04ccbb3Scarlsonj dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
628d04ccbb3Scarlsonj return (dt->dt_id != -1);
629d04ccbb3Scarlsonj }
630d04ccbb3Scarlsonj
631d04ccbb3Scarlsonj /*
632d04ccbb3Scarlsonj * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
633d04ccbb3Scarlsonj * buffer.
634d04ccbb3Scarlsonj *
635d04ccbb3Scarlsonj * input: const dhcpv6_option_t *: pointer to option
636d04ccbb3Scarlsonj * uint_t: option length
637d04ccbb3Scarlsonj * const char **: error string (nul-terminated)
638d04ccbb3Scarlsonj * const char **: message from server (unterminated)
639d04ccbb3Scarlsonj * uint_t *: length of server message
640d04ccbb3Scarlsonj * output: int: -1 on error, or >= 0 for a DHCPv6 status code
641d04ccbb3Scarlsonj */
642d04ccbb3Scarlsonj
643d04ccbb3Scarlsonj int
dhcpv6_status_code(const dhcpv6_option_t * d6o,uint_t olen,const char ** estr,const char ** msg,uint_t * msglenp)644d04ccbb3Scarlsonj dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
645d04ccbb3Scarlsonj const char **msg, uint_t *msglenp)
646d04ccbb3Scarlsonj {
647d04ccbb3Scarlsonj uint16_t status;
648d04ccbb3Scarlsonj static const char *v6_status[] = {
649d04ccbb3Scarlsonj NULL,
650d04ccbb3Scarlsonj "Unknown reason",
651d04ccbb3Scarlsonj "Server has no addresses available",
652d04ccbb3Scarlsonj "Client record unavailable",
653d04ccbb3Scarlsonj "Prefix inappropriate for link",
654d04ccbb3Scarlsonj "Client must use multicast",
655d04ccbb3Scarlsonj "No prefix available"
656d04ccbb3Scarlsonj };
657d04ccbb3Scarlsonj static char sbuf[32];
658d04ccbb3Scarlsonj
659d04ccbb3Scarlsonj *estr = "";
660d04ccbb3Scarlsonj *msg = "";
661d04ccbb3Scarlsonj *msglenp = 0;
662d04ccbb3Scarlsonj if (d6o == NULL)
663d04ccbb3Scarlsonj return (0);
664d04ccbb3Scarlsonj olen -= sizeof (*d6o);
665d04ccbb3Scarlsonj if (olen < 2) {
666d04ccbb3Scarlsonj *estr = "garbled status code";
667d04ccbb3Scarlsonj return (-1);
668d04ccbb3Scarlsonj }
669d04ccbb3Scarlsonj
670d04ccbb3Scarlsonj *msg = (const char *)(d6o + 1) + 2;
671d04ccbb3Scarlsonj *msglenp = olen - 2;
672d04ccbb3Scarlsonj
673d04ccbb3Scarlsonj (void) memcpy(&status, d6o + 1, sizeof (status));
674d04ccbb3Scarlsonj status = ntohs(status);
675d04ccbb3Scarlsonj if (status > 0) {
676d04ccbb3Scarlsonj if (status > DHCPV6_STAT_NOPREFIX) {
677d04ccbb3Scarlsonj (void) snprintf(sbuf, sizeof (sbuf), "status %u",
678d04ccbb3Scarlsonj status);
679d04ccbb3Scarlsonj *estr = sbuf;
680d04ccbb3Scarlsonj } else {
681d04ccbb3Scarlsonj *estr = v6_status[status];
682d04ccbb3Scarlsonj }
683d04ccbb3Scarlsonj }
684d04ccbb3Scarlsonj return (status);
685d04ccbb3Scarlsonj }
686*0a3e1f6cSVasumathi Sundaram
687*0a3e1f6cSVasumathi Sundaram void
write_lease_to_hostconf(dhcp_smach_t * dsmp)688*0a3e1f6cSVasumathi Sundaram write_lease_to_hostconf(dhcp_smach_t *dsmp)
689*0a3e1f6cSVasumathi Sundaram {
690*0a3e1f6cSVasumathi Sundaram PKT_LIST *plp[2];
691*0a3e1f6cSVasumathi Sundaram const char *hcfile;
692*0a3e1f6cSVasumathi Sundaram
693*0a3e1f6cSVasumathi Sundaram hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
694*0a3e1f6cSVasumathi Sundaram plp[0] = dsmp->dsm_ack;
695*0a3e1f6cSVasumathi Sundaram plp[1] = dsmp->dsm_orig_ack;
696*0a3e1f6cSVasumathi Sundaram if (write_hostconf(dsmp->dsm_name, plp, 2,
697*0a3e1f6cSVasumathi Sundaram monosec_to_time(dsmp->dsm_curstart_monosec),
698*0a3e1f6cSVasumathi Sundaram dsmp->dsm_isv6) != -1) {
699*0a3e1f6cSVasumathi Sundaram dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
700*0a3e1f6cSVasumathi Sundaram } else if (errno == EROFS) {
701*0a3e1f6cSVasumathi Sundaram dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
702*0a3e1f6cSVasumathi Sundaram "system; not saving lease", hcfile);
703*0a3e1f6cSVasumathi Sundaram } else {
704*0a3e1f6cSVasumathi Sundaram dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
705*0a3e1f6cSVasumathi Sundaram "not use cached configuration)", hcfile);
706*0a3e1f6cSVasumathi Sundaram }
707*0a3e1f6cSVasumathi Sundaram }
708