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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <netinet/in.h>
31 #include <netinet/inetutil.h>
32 #include <netinet/dhcp.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <dhcpmsg.h>
37 #include <stdio.h>
38 #include <sys/stat.h>
39 #include <libnvpair.h>
40 
41 #include "defaults.h"
42 
43 struct dhcp_default {
44 
45 	const char	*df_name;	/* parameter name */
46 	const char	*df_default;	/* default value */
47 	int		df_min;		/* min value if type DF_INTEGER */
48 	int		df_max;		/* max value if type DF_INTEGER */
49 };
50 
51 /*
52  * note: keep in the same order as tunable parameter constants in defaults.h
53  */
54 
55 static struct dhcp_default defaults[] = {
56 
57 	{ "RELEASE_ON_SIGTERM",  "0",	 0,   0	  },
58 	{ "IGNORE_FAILED_ARP",	 "1",	 0,   0	  },
59 	{ "OFFER_WAIT",		 "3",	 1,   20  },
60 	{ "ARP_WAIT",		 "1000", 100, 4000 },
61 	{ "CLIENT_ID",		 NULL,	 0,   0	  },
62 	{ "PARAM_REQUEST_LIST",  NULL,	 0,   0    },
63 	{ "REQUEST_HOSTNAME",	 "1",	 0,   0	  }
64 };
65 
66 /*
67  * df_build_cache(): builds the defaults nvlist cache
68  *
69  *   input: void
70  *  output: a pointer to an nvlist of the current defaults, or NULL on failure
71  */
72 
73 static nvlist_t *
74 df_build_cache(void)
75 {
76 	char		entry[1024];
77 	int		i;
78 	char		*param, *value, *end;
79 	FILE		*fp;
80 	nvlist_t 	*nvlist;
81 
82 	if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL)
83 		return (NULL);
84 
85 	if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
86 		dhcpmsg(MSG_WARNING, "cannot build default value cache; "
87 		    "using built-in defaults");
88 		(void) fclose(fp);
89 		return (NULL);
90 	}
91 
92 	while (fgets(entry, sizeof (entry), fp) != NULL) {
93 		for (i = 0; entry[i] == ' '; i++)
94 			;
95 
96 		end = strrchr(entry, '\n');
97 		value = strchr(entry, '=');
98 		if (end == NULL || value == NULL || entry[i] == '#')
99 			continue;
100 
101 		*end = '\0';
102 		*value++ = '\0';
103 
104 		/*
105 		 * to be compatible with the old defread()-based code
106 		 * which ignored case, store the parameters (except for the
107 		 * leading interface name) in upper case.
108 		 */
109 
110 		if ((param = strchr(entry, '.')) == NULL)
111 			param = entry;
112 		else
113 			param++;
114 
115 		for (; *param != '\0'; param++)
116 			*param = toupper(*param);
117 
118 		if (nvlist_add_string(nvlist, &entry[i], value) != 0) {
119 			dhcpmsg(MSG_WARNING, "cannot build default value cache;"
120 			    " using built-in defaults");
121 			nvlist_free(nvlist);
122 			nvlist = NULL;
123 			break;
124 		}
125 	}
126 
127 	(void) fclose(fp);
128 	return (nvlist);
129 }
130 
131 /*
132  * df_get_string(): gets the string value of a given user-tunable parameter
133  *
134  *   input: const char *: the interface the parameter applies to
135  *	    unsigned int: the parameter number to look up
136  *  output: const char *: the parameter's value, or default if not set
137  *			  (must be copied by caller to be kept)
138  *    NOTE: df_get_string() is both used by functions outside this source
139  *	    file to retrieve strings from the defaults file, *and*
140  *	    internally by other df_get_*() functions.
141  */
142 
143 const char *
144 df_get_string(const char *if_name, unsigned int p)
145 {
146 	char			*value;
147 	char			param[256];
148 	struct stat		statbuf;
149 	static struct stat	df_statbuf;
150 	static boolean_t	df_unavail_msg = B_FALSE;
151 	static nvlist_t		*df_nvlist = NULL;
152 
153 	if (p >= (sizeof (defaults) / sizeof (*defaults)))
154 		return (NULL);
155 
156 	if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) {
157 		if (!df_unavail_msg) {
158 			dhcpmsg(MSG_WARNING, "cannot access %s; using "
159 			    "built-in defaults", DHCP_AGENT_DEFAULTS);
160 			df_unavail_msg = B_TRUE;
161 		}
162 		return (defaults[p].df_default);
163 	}
164 
165 	/*
166 	 * if our cached parameters are stale, rebuild.
167 	 */
168 
169 	if (statbuf.st_mtime != df_statbuf.st_mtime ||
170 	    statbuf.st_size != df_statbuf.st_size) {
171 		df_statbuf = statbuf;
172 		if (df_nvlist != NULL)
173 			nvlist_free(df_nvlist);
174 		df_nvlist = df_build_cache();
175 	}
176 
177 	(void) snprintf(param, sizeof (param), "%s.%s", if_name,
178 	    defaults[p].df_name);
179 
180 	/*
181 	 * first look for `if_name.param', then `param'.  if neither
182 	 * has been set, use the built-in default.
183 	 */
184 
185 	if (nvlist_lookup_string(df_nvlist, param, &value) == 0 ||
186 	    nvlist_lookup_string(df_nvlist, defaults[p].df_name, &value) == 0)
187 		return (value);
188 
189 	return (defaults[p].df_default);
190 }
191 
192 /*
193  * df_get_octet(): gets the integer value of a given user-tunable parameter
194  *
195  *   input: const char *: the interface the parameter applies to
196  *	    unsigned int: the parameter number to look up
197  *	    unsigned int *: the length of the returned value
198  *  output: uchar_t *: a pointer to byte array (default value if not set)
199  *		       (must be copied by caller to be kept)
200  */
201 
202 uchar_t *
203 df_get_octet(const char *if_name, unsigned int p, unsigned int *len)
204 {
205 	const char	*value;
206 	static uchar_t	octet_value[256]; /* as big as defread() returns */
207 
208 	if (p >= (sizeof (defaults) / sizeof (*defaults)))
209 		return (NULL);
210 
211 	value = df_get_string(if_name, p);
212 	if (value == NULL)
213 		goto do_default;
214 
215 	if (strncasecmp("0x", value, 2) != 0) {
216 		*len = strlen(value);			/* no NUL */
217 		return ((uchar_t *)value);
218 	}
219 
220 	/* skip past the 0x and convert the value to binary */
221 	value += 2;
222 	*len = sizeof (octet_value);
223 	if (hexascii_to_octet(value, strlen(value), octet_value, len) != 0) {
224 		dhcpmsg(MSG_WARNING, "df_get_octet: cannot convert value "
225 		    "for parameter `%s', using default", defaults[p].df_name);
226 		goto do_default;
227 	}
228 	return (octet_value);
229 
230 do_default:
231 	if (defaults[p].df_default == NULL) {
232 		*len = 0;
233 		return (NULL);
234 	}
235 
236 	*len = strlen(defaults[p].df_default);		/* no NUL */
237 	return ((uchar_t *)defaults[p].df_default);
238 }
239 
240 /*
241  * df_get_int(): gets the integer value of a given user-tunable parameter
242  *
243  *   input: const char *: the interface the parameter applies to
244  *	    unsigned int: the parameter number to look up
245  *  output: int: the parameter's value, or default if not set
246  */
247 
248 int
249 df_get_int(const char *if_name, unsigned int p)
250 {
251 	const char	*value;
252 	int		value_int;
253 
254 	if (p >= (sizeof (defaults) / sizeof (*defaults)))
255 		return (0);
256 
257 	value = df_get_string(if_name, p);
258 	if (value == NULL || !isdigit(*value))
259 		goto failure;
260 
261 	value_int = atoi(value);
262 	if (value_int > defaults[p].df_max || value_int < defaults[p].df_min)
263 		goto failure;
264 
265 	return (value_int);
266 
267 failure:
268 	dhcpmsg(MSG_WARNING, "df_get_int: parameter `%s' is not between %d and "
269 	    "%d, defaulting to `%s'", defaults[p].df_name, defaults[p].df_min,
270 	    defaults[p].df_max, defaults[p].df_default);
271 	return (atoi(defaults[p].df_default));
272 }
273 
274 /*
275  * df_get_bool(): gets the boolean value of a given user-tunable parameter
276  *
277  *   input: const char *: the interface the parameter applies to
278  *	    unsigned int: the parameter number to look up
279  *  output: boolean_t: B_TRUE if true, B_FALSE if false, default if not set
280  */
281 
282 boolean_t
283 df_get_bool(const char *if_name, unsigned int p)
284 {
285 	const char	*value;
286 
287 	if (p >= (sizeof (defaults) / sizeof (*defaults)))
288 		return (0);
289 
290 	value = df_get_string(if_name, p);
291 	if (value != NULL) {
292 
293 		if (strcasecmp(value, "true") == 0 ||
294 		    strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0)
295 			return (B_TRUE);
296 
297 		if (strcasecmp(value, "false") == 0 ||
298 		    strcasecmp(value, "no") == 0 || strcmp(value, "0") == 0)
299 			return (B_FALSE);
300 	}
301 
302 	dhcpmsg(MSG_WARNING, "df_get_bool: parameter `%s' has invalid value "
303 	    "`%s', defaulting to `%s'", defaults[p].df_name,
304 	    value ? value : "NULL", defaults[p].df_default);
305 
306 	return ((atoi(defaults[p].df_default) == 0) ? B_FALSE : B_TRUE);
307 }
308