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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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