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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>. 25 * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org> 26 */ 27 28 #include <sys/types.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <ctype.h> 32 #include <dhcpmsg.h> 33 #include <stdio.h> 34 #include <sys/stat.h> 35 #include <libnvpair.h> 36 37 #include "common.h" 38 #include "defaults.h" 39 40 struct dhcp_default { 41 42 const char *df_name; /* parameter name */ 43 const char *df_default; /* default value */ 44 int df_min; /* min value if type DF_INTEGER */ 45 int df_max; /* max value if type DF_INTEGER */ 46 }; 47 48 /* 49 * NOTE: Keep in the same order as tunable parameter constants in defaults.h 50 */ 51 static struct dhcp_default defaults[] = { 52 { "RELEASE_ON_SIGTERM", "0", 0, 0 }, 53 { "IGNORE_FAILED_ARP", "1", 0, -1 }, 54 { "OFFER_WAIT", "3", 1, 20 }, 55 { "ARP_WAIT", "1000", 0, -1 }, 56 { "CLIENT_ID", NULL, 0, 0 }, 57 { "PARAM_REQUEST_LIST", NULL, 0, 0 }, 58 { "REQUEST_HOSTNAME", "1", 0, 0 }, 59 { "DEBUG_LEVEL", "0", 0, 3 }, 60 { "VERBOSE", "0", 0, 0 }, 61 { "VERIFIED_LEASE_ONLY", "0", 0, 0 }, 62 { "PARAM_IGNORE_LIST", NULL, 0, 0 }, 63 { "REQUEST_FQDN", "1", 0, 0 }, 64 { "V4_DEFAULT_IAID_DUID", "0", 0, 0 }, 65 { "DNS_DOMAINNAME", NULL, 0, 0 }, 66 { "ADOPT_DOMAINNAME", "0", 0, 0 }, 67 { "SET_MTU", "1", 0, 0 }, 68 { "CLASSLESS_ROUTES", "1", 0, 0 }, 69 }; 70 71 /* 72 * df_build_cache(): builds the defaults nvlist cache 73 * 74 * input: void 75 * output: a pointer to an nvlist of the current defaults, or NULL on failure 76 */ 77 78 static nvlist_t * 79 df_build_cache(void) 80 { 81 char entry[1024]; 82 int i; 83 char *param, *pastv6, *value, *end; 84 FILE *fp; 85 nvlist_t *nvlist; 86 struct dhcp_default *defp; 87 88 if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL) 89 return (NULL); 90 91 if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { 92 dhcpmsg(MSG_WARNING, "cannot build default value cache; " 93 "using built-in defaults"); 94 (void) fclose(fp); 95 return (NULL); 96 } 97 98 while (fgets(entry, sizeof (entry), fp) != NULL) { 99 for (i = 0; entry[i] == ' '; i++) 100 ; 101 102 end = strrchr(entry, '\n'); 103 value = strchr(entry, '='); 104 if (end == NULL || value == NULL || entry[i] == '#') 105 continue; 106 107 *end = '\0'; 108 *value++ = '\0'; 109 110 /* 111 * to be compatible with the old defread()-based code 112 * which ignored case, store the parameters (except for the 113 * leading interface name) in upper case. 114 */ 115 116 if ((param = strchr(entry, '.')) == NULL) { 117 pastv6 = param = entry; 118 } else { 119 pastv6 = ++param; 120 if (strncasecmp(param, "v6.", 3) == 0) 121 pastv6 += 3; 122 } 123 124 for (defp = defaults; 125 (char *)defp < (char *)defaults + sizeof (defaults); 126 defp++) { 127 if (strcasecmp(pastv6, defp->df_name) == 0) { 128 if (defp->df_max == -1) { 129 dhcpmsg(MSG_WARNING, "parameter %s is " 130 "obsolete; ignored", defp->df_name); 131 } 132 break; 133 } 134 } 135 136 for (; *param != '\0'; param++) 137 *param = toupper(*param); 138 139 if (nvlist_add_string(nvlist, &entry[i], value) != 0) { 140 dhcpmsg(MSG_WARNING, "cannot build default value cache;" 141 " using built-in defaults"); 142 nvlist_free(nvlist); 143 nvlist = NULL; 144 break; 145 } 146 } 147 148 (void) fclose(fp); 149 return (nvlist); 150 } 151 152 /* 153 * df_get_string(): gets the string value of a given user-tunable parameter 154 * 155 * input: const char *: the interface the parameter applies to 156 * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 157 * uint_t: the parameter number to look up 158 * output: const char *: the parameter's value, or default if not set 159 * (must be copied by caller to be kept) 160 * NOTE: df_get_string() is both used by functions outside this source 161 * file to retrieve strings from the defaults file, *and* 162 * internally by other df_get_*() functions. 163 */ 164 165 const char * 166 df_get_string(const char *if_name, boolean_t isv6, uint_t param) 167 { 168 char *value; 169 char paramstr[256]; 170 char name[256]; 171 struct stat statbuf; 172 static struct stat df_statbuf; 173 static boolean_t df_unavail_msg = B_FALSE; 174 static nvlist_t *df_nvlist = NULL; 175 176 if (param >= (sizeof (defaults) / sizeof (*defaults))) 177 return (NULL); 178 179 if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) { 180 if (!df_unavail_msg) { 181 dhcpmsg(MSG_WARNING, "cannot access %s; using " 182 "built-in defaults", DHCP_AGENT_DEFAULTS); 183 df_unavail_msg = B_TRUE; 184 } 185 return (defaults[param].df_default); 186 } 187 188 /* 189 * if our cached parameters are stale, rebuild. 190 */ 191 192 if (statbuf.st_mtime != df_statbuf.st_mtime || 193 statbuf.st_size != df_statbuf.st_size) { 194 df_statbuf = statbuf; 195 nvlist_free(df_nvlist); 196 df_nvlist = df_build_cache(); 197 } 198 199 if (isv6) { 200 (void) snprintf(name, sizeof (name), ".V6.%s", 201 defaults[param].df_name); 202 (void) snprintf(paramstr, sizeof (paramstr), "%s%s", if_name, 203 name); 204 } else { 205 (void) strlcpy(name, defaults[param].df_name, sizeof (name)); 206 (void) snprintf(paramstr, sizeof (paramstr), "%s.%s", if_name, 207 name); 208 } 209 210 /* 211 * first look for `if_name.[v6.]param', then `[v6.]param'. if neither 212 * has been set, use the built-in default. 213 */ 214 215 if (nvlist_lookup_string(df_nvlist, paramstr, &value) == 0 || 216 nvlist_lookup_string(df_nvlist, name, &value) == 0) 217 return (value); 218 219 return (defaults[param].df_default); 220 } 221 222 /* 223 * df_get_int(): gets the integer value of a given user-tunable parameter 224 * 225 * input: const char *: the interface the parameter applies to 226 * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 227 * uint_t: the parameter number to look up 228 * output: int: the parameter's value, or default if not set 229 */ 230 231 int 232 df_get_int(const char *if_name, boolean_t isv6, uint_t param) 233 { 234 const char *value; 235 int value_int; 236 237 if (param >= (sizeof (defaults) / sizeof (*defaults))) 238 return (0); 239 240 value = df_get_string(if_name, isv6, param); 241 if (value == NULL || !isdigit(*value)) 242 goto failure; 243 244 value_int = atoi(value); 245 if (value_int > defaults[param].df_max || 246 value_int < defaults[param].df_min) 247 goto failure; 248 249 return (value_int); 250 251 failure: 252 dhcpmsg(MSG_WARNING, "df_get_int: parameter `%s' is not between %d and " 253 "%d, defaulting to `%s'", defaults[param].df_name, 254 defaults[param].df_min, defaults[param].df_max, 255 defaults[param].df_default); 256 return (atoi(defaults[param].df_default)); 257 } 258 259 /* 260 * df_get_bool(): gets the boolean value of a given user-tunable parameter 261 * 262 * input: const char *: the interface the parameter applies to 263 * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 264 * uint_t: the parameter number to look up 265 * output: boolean_t: B_TRUE if true, B_FALSE if false, default if not set 266 */ 267 268 boolean_t 269 df_get_bool(const char *if_name, boolean_t isv6, uint_t param) 270 { 271 const char *value; 272 273 if (param >= (sizeof (defaults) / sizeof (*defaults))) 274 return (0); 275 276 value = df_get_string(if_name, isv6, param); 277 if (value != NULL) { 278 279 if (strcasecmp(value, "true") == 0 || 280 strcasecmp(value, "yes") == 0 || strcmp(value, "1") == 0) 281 return (B_TRUE); 282 283 if (strcasecmp(value, "false") == 0 || 284 strcasecmp(value, "no") == 0 || strcmp(value, "0") == 0) 285 return (B_FALSE); 286 } 287 288 dhcpmsg(MSG_WARNING, "df_get_bool: parameter `%s' has invalid value " 289 "`%s', defaulting to `%s'", defaults[param].df_name, 290 value != NULL ? value : "NULL", defaults[param].df_default); 291 292 return ((atoi(defaults[param].df_default) == 0) ? B_FALSE : B_TRUE); 293 } 294