xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/config.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "defs.h"
27 #include "tables.h"
28 
29 /*
30  * Parse the config file which consists of entries of the form:
31  *	ifdefault	[<variable> <value>]*
32  *	prefixdefault	[<variable> <value>]*
33  *	if <ifname>	[<variable> <value>]*
34  *	prefix <prefix>/<length> <ifname>	[<variable> <value>]*
35  *
36  * All "ifdefault" and "prefixdefault" entries must preceed any
37  * "if" and "prefix" entries.
38  *
39  * Values (such as expiry dates) which contain white space
40  * can be quoted with single or double quotes.
41  */
42 
43 /* maximum length of messages we send to syslog */
44 #define	NDPD_LOGMSGSIZE	1024
45 typedef	boolean_t	(*pfb_t)(char *, uint_t *);
46 
47 struct configinfo {
48 	char	*ci_name;
49 	uint_t	ci_min;		/* 0: no min check */
50 	uint_t	ci_max;		/* ~0U: no max check */
51 	uint_t	ci_default;
52 	uint_t	ci_index;	/* Into result array */
53 	pfb_t	ci_parsefunc;	/* Parse function returns -1 on failure */
54 };
55 
56 enum config_type { CONFIG_IF, CONFIG_PREFIX};
57 typedef enum config_type config_type_t;
58 
59 static void set_protocol_defaults(void);
60 static void print_defaults(void);
61 static void parse_var_value(config_type_t, struct configinfo *, char *, char *,
62     struct confvar *);
63 static void parse_default(config_type_t, struct configinfo *, char **, int,
64     struct confvar *);
65 static void parse_if(struct configinfo *, char **, int);
66 static void parse_prefix(struct configinfo *, char **, int);
67 static boolean_t parse_onoff(char *, uint_t *);	/* boolean */
68 static boolean_t parse_int(char *, uint_t *);	/* integer */
69 static boolean_t parse_ms(char *, uint_t *);	/* milliseconds */
70 static boolean_t parse_s(char *, uint_t *);	/* seconds */
71 static boolean_t parse_date(char *, uint_t *);	/* date format */
72 static void conferr(char *fmt, ...);
73 static FILE *open_conffile(char *filename);
74 static int parse_line(char *line, char *argvec[], int argcount);
75 static int readline(FILE *fp, char *line, int length);
76 static int parse_addrprefix(char *strin, struct in6_addr *in6);
77 
78 /*
79  * Per interface configuration variables.
80  * Min, max, and default values are from RFC 2461.
81  */
82 static struct configinfo iflist[] = {
83 	/* Name, Min, Max, Default, Index */
84 	{ "DupAddrDetectTransmits", 0, 100, 1, I_DupAddrDetectTransmits,
85 	parse_int },
86 	{ "AdvSendAdvertisements", 0, 1, 0, I_AdvSendAdvertisements,
87 	parse_onoff },
88 	{ "MaxRtrAdvInterval", 4, 1800, 600, I_MaxRtrAdvInterval, parse_s },
89 	{ "MinRtrAdvInterval", 3, 1350, 200, I_MinRtrAdvInterval, parse_s },
90 	/*
91 	 * No greater than .75 * MaxRtrAdvInterval.
92 	 * Default: 0.33 * MaxRtrAdvInterval
93 	 */
94 	{ "AdvManagedFlag", 0, 1, 0, I_AdvManagedFlag, parse_onoff },
95 	{ "AdvOtherConfigFlag", 0, 1, 0, I_AdvOtherConfigFlag, parse_onoff },
96 	{ "AdvLinkMTU", IPV6_MIN_MTU, 65535, 0, I_AdvLinkMTU, parse_int },
97 	{ "AdvReachableTime", 0, 3600000, 0, I_AdvReachableTime, parse_ms },
98 	{ "AdvRetransTimer", 0, ~0U, 0, I_AdvRetransTimer, parse_ms },
99 	{ "AdvCurHopLimit", 0, 255, 0, I_AdvCurHopLimit, parse_int },
100 	{ "AdvDefaultLifetime", 0, 9000, 1800, I_AdvDefaultLifetime, parse_s },
101 	/*
102 	 * MUST be either zero or between MaxRtrAdvInterval and 9000 seconds.
103 	 * Default: 3 * MaxRtrAdvInterval
104 	 */
105 	{ "StatelessAddrConf", 0, 1, 1, I_StatelessAddrConf, parse_onoff },
106 	{ "StatefulAddrConf", 0, 1, 1, I_StatefulAddrConf, parse_onoff },
107 	/*
108 	 * Tmp* variables from RFC 3041, where defaults are defined.
109 	 */
110 	{ "TmpAddrsEnabled", 0, 1, 0, I_TmpAddrsEnabled, parse_onoff },
111 	{ "TmpValidLifetime", 0, ~0U, 604800, I_TmpValidLifetime, parse_s },
112 	{ "TmpPreferredLifetime", 0, ~0U, 86400, I_TmpPreferredLifetime,
113 	parse_s },
114 	{ "TmpRegenAdvance", 0, 60, 5, I_TmpRegenAdvance, parse_s },
115 	{ "TmpMaxDesyncFactor", 0, 600, 600, I_TmpMaxDesyncFactor, parse_s },
116 	{ NULL, 0, 0, 0, 0 }
117 };
118 
119 /*
120  * Per prefix: AdvPrefixList configuration variables.
121  * Min, max, and default values are from RFC 2461.
122  */
123 static struct configinfo prefixlist[] = {
124 	/* Name, Min, Max, Default, Index */
125 	{ "AdvValidLifetime", 0, ~0U, 2592000, I_AdvValidLifetime,
126 	parse_s },
127 	{ "AdvOnLinkFlag", 0, 1, 1, I_AdvOnLinkFlag, parse_onoff },
128 	{ "AdvPreferredLifetime", 0, ~0U, 604800, I_AdvPreferredLifetime,
129 	parse_s},
130 	{ "AdvAutonomousFlag", 0, 1, 1, I_AdvAutonomousFlag, parse_onoff },
131 	{ "AdvValidExpiration", 0, ~0U, 0, I_AdvValidExpiration,
132 	parse_date },
133 	{ "AdvPreferredExpiration", 0, ~0U, 0, I_AdvPreferredExpiration,
134 	parse_date},
135 	{ NULL, 0, 0, 0, 0 },
136 };
137 
138 /*
139  * Data structures used to merge above protocol defaults
140  * with defaults specified in the configuration file.
141  * ifdefault is not static because new interfaces can be
142  * created outside of the configuration context.
143  */
144 struct confvar ifdefaults[I_IFSIZE];
145 static struct confvar prefixdefaults[I_PREFIXSIZE];
146 
147 static char	conf_filename[MAXPATHLEN];
148 static int	lineno;
149 
150 /*
151  * Checks for violations of section 5.5.3 (c) of RFC 2462.
152  */
153 static void
154 check_var_consistency(struct confvar *cv, void *save, int size)
155 {
156 	boolean_t rollback = _B_FALSE;
157 	int prefl, prefe, valid;
158 
159 	prefl = cv[I_AdvPreferredLifetime].cf_value;
160 	prefe = cv[I_AdvPreferredExpiration].cf_value;
161 	valid = cv[I_AdvValidLifetime].cf_value;
162 
163 	if (prefl > valid) {
164 		conferr("AdvPreferredLifetime (%u) is greater than "
165 		    "valid lifetime (%u)\n", prefl, valid);
166 		rollback = _B_TRUE;
167 	}
168 
169 	if (prefe > valid) {
170 		conferr("AdvPreferredExpiration (%u) is greater than "
171 		    "valid lifetime (%u)\n", prefe, valid);
172 		rollback = _B_TRUE;
173 	}
174 
175 	if (rollback) {
176 		(void) memcpy(cv, save, size);
177 	}
178 }
179 
180 /*
181  * Check for invalid lifetime values for RFC3041 addresses
182  */
183 static void
184 check_if_var_consistency(struct confvar *cv, void *save, int size)
185 {
186 	boolean_t rollback = _B_FALSE;
187 	int tpref, tvalid, tdesync, tregen;
188 
189 	tpref = cv[I_TmpPreferredLifetime].cf_value;
190 	tvalid = cv[I_TmpValidLifetime].cf_value;
191 	tdesync = cv[I_TmpMaxDesyncFactor].cf_value;
192 	tregen = cv[I_TmpRegenAdvance].cf_value;
193 
194 	/*
195 	 * Only need to do this if tmp addrs are enabled.
196 	 */
197 	if (cv[I_TmpAddrsEnabled].cf_value == 0)
198 		return;
199 
200 	if (tdesync > tpref) {
201 		conferr("TmpDesyncFactor (%u) is greater than "
202 		    "TmpPreferredLifetime (%u)\n", tdesync, tpref);
203 		rollback = _B_TRUE;
204 	}
205 
206 	if (tpref > tvalid) {
207 		conferr("TmpPreferredLifetime (%u) is greater than "
208 		    "TmpValidLifetime (%u)\n", tpref, tvalid);
209 		rollback = _B_TRUE;
210 	}
211 
212 	if (tregen > tvalid) {
213 		conferr("TmpRegenAdvance (%u) is greater than "
214 		    "TmpValidLifetime (%u)\n", tregen, tvalid);
215 		rollback = _B_TRUE;
216 	}
217 
218 	if (rollback) {
219 		(void) memcpy(cv, save, size);
220 	}
221 }
222 
223 int
224 parse_config(char *config_file, boolean_t file_required)
225 {
226 	FILE *fp;
227 	char line[MAXLINELEN];
228 	char pline[MAXLINELEN];
229 	int argcount;
230 	char *argvec[MAXARGSPERLINE];
231 	int defaultdone = 0;	/* Set when first non-default command found */
232 
233 	if (debug & D_CONFIG)
234 		logmsg(LOG_DEBUG, "parse_config()\n");
235 
236 	set_protocol_defaults();
237 	if (debug & D_DEFAULTS)
238 		print_defaults();
239 
240 	fp = open_conffile(config_file);
241 	if (fp == NULL) {
242 		if (errno == ENOENT && !file_required)
243 			return (0);
244 		logperror(config_file);
245 		return (-1);
246 	}
247 	while (readline(fp, line, sizeof (line)) != 0) {
248 		(void) strncpy(pline, line, sizeof (pline));
249 		pline[sizeof (pline) - 1] = '\0';	/* NULL terminate */
250 		argcount = parse_line(pline, argvec,
251 		    sizeof (argvec) / sizeof (argvec[0]));
252 		if (debug & D_PARSE) {
253 			int i;
254 
255 			logmsg(LOG_DEBUG, "scanned %d args\n", argcount);
256 			for (i = 0; i < argcount; i++)
257 				logmsg(LOG_DEBUG, "arg[%d]: %s\n",
258 				    i, argvec[i]);
259 		}
260 		if (argcount == 0) {
261 			/* Empty line - or comment only line */
262 			continue;
263 		}
264 		if (strcmp(argvec[0], "ifdefault") == 0) {
265 			char save[sizeof (ifdefaults)];
266 
267 			if (defaultdone) {
268 				conferr("ifdefault after non-default "
269 				    "command\n");
270 				continue;
271 			}
272 			/*
273 			 * Save existing values in case what we read is
274 			 * invalid and we need to restore previous settings.
275 			 */
276 			(void) memcpy(save, ifdefaults, sizeof (ifdefaults));
277 			parse_default(CONFIG_IF, iflist, argvec+1, argcount-1,
278 			    ifdefaults);
279 			check_if_var_consistency(ifdefaults, save,
280 			    sizeof (save));
281 		} else if (strcmp(argvec[0], "prefixdefault") == 0) {
282 			char save[sizeof (prefixdefaults)];
283 
284 			if (defaultdone) {
285 				conferr("prefixdefault after non-default "
286 				    "command\n");
287 				continue;
288 			}
289 			/*
290 			 * Save existing values in case what we read is
291 			 * invalid and we need to restore previous settings.
292 			 */
293 			(void) memcpy(save, prefixdefaults,
294 			    sizeof (prefixdefaults));
295 			parse_default(CONFIG_PREFIX, prefixlist, argvec+1,
296 			    argcount-1, prefixdefaults);
297 			check_var_consistency(prefixdefaults, save,
298 			    sizeof (save));
299 		} else if (strcmp(argvec[0], "if") == 0) {
300 			defaultdone = 1;
301 			parse_if(iflist, argvec+1, argcount-1);
302 		} else if (strcmp(argvec[0], "prefix") == 0) {
303 			defaultdone = 1;
304 			parse_prefix(prefixlist, argvec+1, argcount-1);
305 		} else {
306 			conferr("Unknown command: %s\n", argvec[0]);
307 		}
308 	}
309 	(void) fclose(fp);
310 	if (debug & D_DEFAULTS)
311 		print_defaults();
312 	return (0);
313 }
314 
315 /*
316  * Extract the defaults from the configinfo tables to initialize
317  * the ifdefaults and prefixdefaults arrays.
318  * The arrays are needed to track which defaults have been changed
319  * by the config file.
320  */
321 static void
322 set_protocol_defaults(void)
323 {
324 	struct configinfo *cip;
325 
326 	if (debug & D_DEFAULTS)
327 		logmsg(LOG_DEBUG, "extract_protocol_defaults\n");
328 	for (cip = iflist; cip->ci_name != NULL; cip++) {
329 		ifdefaults[cip->ci_index].cf_value = cip->ci_default;
330 		ifdefaults[cip->ci_index].cf_notdefault = _B_FALSE;
331 	}
332 	for (cip = prefixlist; cip->ci_name != NULL; cip++) {
333 		prefixdefaults[cip->ci_index].cf_value = cip->ci_default;
334 		prefixdefaults[cip->ci_index].cf_notdefault = _B_FALSE;
335 	}
336 }
337 
338 void
339 print_iflist(struct confvar *confvar)
340 {
341 	struct configinfo *cip;
342 
343 	for (cip = iflist; cip->ci_name != NULL; cip++) {
344 		logmsg(LOG_DEBUG, "\t%s min %u max %u def %u value %u set %d\n",
345 		    cip->ci_name, cip->ci_min, cip->ci_max, cip->ci_default,
346 		    confvar[cip->ci_index].cf_value,
347 		    confvar[cip->ci_index].cf_notdefault);
348 	}
349 }
350 
351 void
352 print_prefixlist(struct confvar *confvar)
353 {
354 	struct configinfo *cip;
355 
356 	for (cip = prefixlist; cip->ci_name != NULL; cip++) {
357 		logmsg(LOG_DEBUG, "\t%s min %u max %u def %u value %u set %d\n",
358 		    cip->ci_name, cip->ci_min, cip->ci_max, cip->ci_default,
359 		    confvar[cip->ci_index].cf_value,
360 		    confvar[cip->ci_index].cf_notdefault);
361 	}
362 }
363 
364 
365 static void
366 print_defaults(void)
367 {
368 	logmsg(LOG_DEBUG, "Default interface variables:\n");
369 	print_iflist(ifdefaults);
370 	logmsg(LOG_DEBUG, "Default prefix variables:\n");
371 	print_prefixlist(prefixdefaults);
372 }
373 
374 /*
375  * Read from fp. Handle \ at the end of the line by joining lines together.
376  * Return 0 on EOF.
377  */
378 static int
379 readline(FILE *fp, char *line, int length)
380 {
381 	int got = 0;
382 
383 retry:
384 	errno = 0;
385 	if (fgets(line, length, fp) == NULL) {
386 		if (errno == EINTR)
387 			goto retry;
388 		if (got != 0)
389 			return (1);
390 		else
391 			return (0);
392 	}
393 	lineno++;
394 	got = strlen(line);
395 	/* Look for trailing \. Note that fgets includes the linefeed. */
396 	if (got >= 2 && line[got-2] == '\\') {
397 		/* Skip \ and LF */
398 		line += got - 2;
399 		length -= got - 2;
400 		goto retry;
401 	}
402 	/* Remove the trailing linefeed */
403 	if (got > 0)
404 		line[got-1] = '\0';
405 
406 	return (1);
407 }
408 
409 /*
410  * Parse a line splitting it off at whitspace characters.
411  * Modifies the content of the string by inserting NULLs.
412  * If more arguments than fits in argvec/argcount then ignore the last.
413  * Returns argcount.
414  * Handles single quotes and double quotes.
415  */
416 static int
417 parse_line(char *line, char *argvec[], int argcount)
418 {
419 	int i = 0;
420 	char *cp;
421 	boolean_t insingle_quote = _B_FALSE;
422 	boolean_t indouble_quote = _B_FALSE;
423 
424 	/* Truncate at the beginning of a comment */
425 	cp = strchr(line, '#');
426 	if (cp != NULL)
427 		*cp = '\0';
428 
429 	for (;;) {
430 		/* Skip any whitespace */
431 		while (isspace(*line) && *line != '\0')
432 			line++;
433 
434 		if (*line == '\'') {
435 			line++;
436 			if (*line == '\0')
437 				return (i);
438 			insingle_quote = _B_TRUE;
439 		} else if (*line == '"') {
440 			line++;
441 			if (*line == '\0')
442 				return (i);
443 			indouble_quote = _B_TRUE;
444 		}
445 		argvec[i] = line;
446 		if (*line == '\0')
447 			return (i);
448 		i++;
449 		/* Skip until next whitespace or end of quoted text */
450 		if (insingle_quote) {
451 			while (*line != '\'' && *line != '\0')
452 				line++;
453 			if (*line == '\'') {
454 				*line = ' ';
455 			} else {
456 				/* Handle missing quote at end */
457 				i--;
458 				conferr("Missing end quote - ignoring <%s>\n",
459 				    argvec[i]);
460 				return (i);
461 			}
462 			insingle_quote = _B_FALSE;
463 		} else if (indouble_quote) {
464 			while (*line != '"' && *line != '\0')
465 				line++;
466 			if (*line == '"') {
467 				*line = ' ';
468 			} else {
469 				/* Handle missing quote at end */
470 				i--;
471 				conferr("Missing end quote - ignoring <%s>\n",
472 				    argvec[i]);
473 				return (i);
474 			}
475 			indouble_quote = _B_FALSE;
476 		} else {
477 			while (!isspace(*line) && *line != '\0')
478 				line++;
479 		}
480 		if (*line != '\0') {
481 			/* Break off argument */
482 			*line++ = '\0';
483 		}
484 		if (i > argcount)
485 			return (argcount);
486 	}
487 	/* NOTREACHED */
488 }
489 
490 static void
491 parse_var_value(config_type_t type, struct configinfo *list, char *varstr,
492     char *valstr, struct confvar *confvar)
493 {
494 	struct configinfo *cip;
495 	uint_t val;
496 
497 	if (debug & D_CONFIG) {
498 		logmsg(LOG_DEBUG, "parse_var_value(%d, %s, %s)\n",
499 		    (int)type, varstr, valstr);
500 	}
501 
502 	for (cip = list; cip->ci_name != NULL; cip++) {
503 		if (strcasecmp(cip->ci_name, varstr) == 0)
504 			break;
505 	}
506 	if (cip->ci_name == NULL) {
507 		conferr("Unknown variable: <%s>\n", varstr);
508 		return;
509 	}
510 	if (!(*cip->ci_parsefunc)(valstr, &val)) {
511 		conferr("Bad value: <%s>\n", valstr);
512 		return;
513 	}
514 	if (cip->ci_min != 0 && val < cip->ci_min) {
515 		conferr("Value %s is below minimum %u for %s\n",
516 		    valstr, cip->ci_min, varstr);
517 		return;
518 	}
519 	if (cip->ci_max != ~0U && val > cip->ci_max) {
520 		conferr("Value %s is above maximum %u for %s\n",
521 		    valstr, cip->ci_max, varstr);
522 		return;
523 	}
524 	/* Check against dynamic/relative limits */
525 	if (type == CONFIG_IF) {
526 		if (cip->ci_index == I_MinRtrAdvInterval &&
527 		    confvar[I_MaxRtrAdvInterval].cf_notdefault &&
528 		    val > confvar[I_MaxRtrAdvInterval].cf_value * 0.75) {
529 			conferr("MinRtrAdvInterval exceeds .75 * "
530 			    "MaxRtrAdvInterval (%u)\n",
531 			    confvar[I_MaxRtrAdvInterval].cf_value);
532 			return;
533 		}
534 		if (cip->ci_index == I_MaxRtrAdvInterval &&
535 		    confvar[I_MinRtrAdvInterval].cf_notdefault &&
536 		    confvar[I_MinRtrAdvInterval].cf_value > val * 0.75) {
537 			conferr("MinRtrAdvInterval (%u) exceeds .75 * "
538 			    "MaxRtrAdvInterval\n",
539 			    confvar[I_MinRtrAdvInterval].cf_value);
540 			return;
541 		}
542 		if (cip->ci_index == I_AdvDefaultLifetime &&
543 		    confvar[I_MaxRtrAdvInterval].cf_notdefault &&
544 		    val != 0 &&
545 		    val < confvar[I_MaxRtrAdvInterval].cf_value) {
546 			conferr("AdvDefaultLifetime is not between "
547 			    "MaxRtrAdrInterval (%u) and 9000 seconds\n",
548 			    confvar[I_MaxRtrAdvInterval].cf_value);
549 			return;
550 		}
551 		if (cip->ci_index == I_MaxRtrAdvInterval &&
552 		    confvar[I_AdvDefaultLifetime].cf_notdefault &&
553 		    confvar[I_AdvDefaultLifetime].cf_value < val) {
554 			conferr("AdvDefaultLifetime (%u) is not between "
555 			    "MaxRtrAdrInterval and 9000 seconds\n",
556 			    confvar[I_AdvDefaultLifetime].cf_value);
557 			return;
558 		}
559 	}
560 	confvar[cip->ci_index].cf_value = val;
561 	confvar[cip->ci_index].cf_notdefault = _B_TRUE;
562 
563 	/* Derive dynamic/relative variables based on this one */
564 	if (type == CONFIG_IF) {
565 		if (cip->ci_index == I_MaxRtrAdvInterval &&
566 		    !confvar[I_MinRtrAdvInterval].cf_notdefault)
567 			confvar[I_MinRtrAdvInterval].cf_value = val / 3;
568 		if (cip->ci_index == I_MaxRtrAdvInterval &&
569 		    !confvar[I_AdvDefaultLifetime].cf_notdefault)
570 		    confvar[I_AdvDefaultLifetime].cf_value = 3 * val;
571 	}
572 }
573 
574 /*
575  * Split up the line into <variable> <value> pairs
576  */
577 static void
578 parse_default(config_type_t type, struct configinfo *list,
579     char *argvec[], int argcount, struct confvar *defaults)
580 {
581 	if (debug & D_CONFIG)
582 		logmsg(LOG_DEBUG, "parse_default: argc %d\n", argcount);
583 	while (argcount >= 2) {
584 		parse_var_value(type, list, argvec[0], argvec[1], defaults);
585 
586 		argcount -= 2;
587 		argvec += 2;
588 	}
589 	if (argcount != 0)
590 		conferr("Trailing text <%s> ignored\n", argvec[0]);
591 }
592 
593 /*
594  * Returns true if ok; otherwise false.
595  */
596 static void
597 parse_if(struct configinfo *list, char *argvec[], int argcount)
598 {
599 	char *ifname;
600 	struct phyint *pi;
601 	char save[sizeof (pi->pi_config)];
602 
603 	if (debug & D_CONFIG)
604 		logmsg(LOG_DEBUG, "parse_if: argc %d\n", argcount);
605 
606 	if (argcount < 1) {
607 		conferr("Missing interface name\n");
608 		return;
609 	}
610 	ifname = argvec[0];
611 	argvec++;
612 	argcount--;
613 
614 	pi = phyint_lookup(ifname);
615 	if (pi == NULL) {
616 		/*
617 		 * Create the physical interface structure.
618 		 * Note, phyint_create() sets the interface
619 		 * defaults in pi_config.
620 		 */
621 		pi = phyint_create(ifname);
622 		if (pi == NULL) {
623 			conferr("Unable to use interface %s\n", ifname);
624 			return;
625 		}
626 	}
627 
628 	(void) memcpy(save, pi->pi_config, sizeof (save));
629 	while (argcount >= 2) {
630 		parse_var_value(CONFIG_IF, list, argvec[0], argvec[1],
631 		    pi->pi_config);
632 
633 		argcount -= 2;
634 		argvec += 2;
635 	}
636 	if (argcount != 0)
637 		logmsg(LOG_ERR, "Trailing text <%s> ignored\n", argvec[0]);
638 	check_if_var_consistency(pi->pi_config, save, sizeof (save));
639 }
640 
641 static void
642 parse_prefix(struct configinfo *list, char *argvec[], int argcount)
643 {
644 	char *ifname, *prefix;
645 	struct phyint *pi;
646 	struct adv_prefix *adv_pr;
647 	struct in6_addr in6;
648 	int prefixlen;
649 	char save[sizeof (adv_pr->adv_pr_config)];
650 
651 	if (debug & D_CONFIG)
652 		logmsg(LOG_DEBUG, "parse_prefix: argc %d\n", argcount);
653 
654 	if (argcount < 2) {
655 		conferr("Missing prefix and/or interface name\n");
656 		return;
657 	}
658 	prefix = argvec[0];
659 	ifname = argvec[1];
660 	argvec += 2;
661 	argcount -= 2;
662 
663 	prefixlen = parse_addrprefix(prefix, &in6);
664 	if (prefixlen == -1) {
665 		conferr("Bad prefix %s\n", prefix);
666 		return;
667 	}
668 
669 	pi = phyint_lookup(ifname);
670 	if (pi == NULL) {
671 		/*
672 		 * Create the physical interface structure.
673 		 * Note, phyint_create() sets the interface
674 		 * defaults in pi_config.
675 		 */
676 		pi = phyint_create(ifname);
677 		if (pi == NULL) {
678 			conferr("Unable to use interface %s\n", ifname);
679 			return;
680 		}
681 	}
682 	adv_pr = adv_prefix_lookup(pi, in6, prefixlen);
683 	if (adv_pr == NULL) {
684 		int i;
685 
686 		adv_pr = adv_prefix_create(pi, in6, prefixlen);
687 		if (adv_pr == NULL) {
688 			conferr("Unable to create prefix %s\n", prefix);
689 			return;
690 		}
691 		/*
692 		 * Copy the defaults from the default array.
693 		 */
694 		for (i = 0; i < I_PREFIXSIZE; i++) {
695 			adv_pr->adv_pr_config[i].cf_value =
696 			    prefixdefaults[i].cf_value;
697 			adv_pr->adv_pr_config[i].cf_notdefault =
698 			    prefixdefaults[i].cf_notdefault;
699 		}
700 	}
701 
702 	(void) memcpy(save, adv_pr->adv_pr_config, sizeof (save));
703 	while (argcount >= 2) {
704 		parse_var_value(CONFIG_PREFIX, list, argvec[0], argvec[1],
705 		    adv_pr->adv_pr_config);
706 
707 		argcount -= 2;
708 		argvec += 2;
709 	}
710 	check_var_consistency(adv_pr->adv_pr_config, save, sizeof (save));
711 	if (argcount != 0)
712 		logmsg(LOG_ERR, "Trailing text <%s> ignored\n", argvec[0]);
713 }
714 
715 /*
716  * Returns true if ok (and *resp updated) and false if failed.
717  */
718 static boolean_t
719 parse_onoff(char *str, uint_t *resp)
720 {
721 	if (strcasecmp(str, "on") == 0) {
722 		*resp = 1;
723 		return (_B_TRUE);
724 	}
725 	if (strcasecmp(str, "off") == 0) {
726 		*resp = 0;
727 		return (_B_TRUE);
728 	}
729 	if (strcasecmp(str, "true") == 0) {
730 		*resp = 1;
731 		return (_B_TRUE);
732 	}
733 	if (strcasecmp(str, "false") == 0) {
734 		*resp = 0;
735 		return (_B_TRUE);
736 	}
737 	if (parse_int(str, resp)) {
738 		if (*resp == 0 || *resp == 1)
739 			return (_B_TRUE);
740 	}
741 	return (_B_FALSE);
742 }
743 
744 /*
745  * Returns true if ok (and *resp updated) and false if failed.
746  */
747 static boolean_t
748 parse_int(char *str, uint_t *resp)
749 {
750 	char *end;
751 	int res;
752 
753 	res = strtoul(str, &end, 0);
754 	if (end == str)
755 		return (_B_FALSE);
756 	*resp = res;
757 	return (_B_TRUE);
758 }
759 
760 /*
761  * Parse something with a unit of millseconds.
762  * Regognizes the suffixes "ms", "s", "m", "h", and "d".
763  *
764  * Returns true if ok (and *resp updated) and false if failed.
765  */
766 static boolean_t
767 parse_ms(char *str, uint_t *resp)
768 {
769 	/* Look at the last and next to last character */
770 	char *cp, *last, *nlast;
771 	char str2[BUFSIZ];	/* For local modification */
772 	int multiplier = 1;
773 
774 	(void) strncpy(str2, str, sizeof (str2));
775 	str2[sizeof (str2) - 1] = '\0';
776 
777 	last = str2;
778 	nlast = NULL;
779 	for (cp = str2; *cp != '\0'; cp++) {
780 		nlast = last;
781 		last = cp;
782 	}
783 	if (debug & D_PARSE) {
784 		logmsg(LOG_DEBUG, "parse_ms: last <%c> nlast <%c>\n",
785 		    (last != NULL ? *last : ' '),
786 		    (nlast != NULL ? *nlast : ' '));
787 	}
788 	switch (*last) {
789 	case 'd':
790 		multiplier *= 24;
791 		/* FALLTHRU */
792 	case 'h':
793 		multiplier *= 60;
794 		/* FALLTHRU */
795 	case 'm':
796 		multiplier *= 60;
797 		*last = '\0';
798 		multiplier *= 1000;	/* Convert to milliseconds */
799 		break;
800 	case 's':
801 		/* Could be "ms" or "s" */
802 		if (nlast != NULL && *nlast == 'm') {
803 			/* "ms" */
804 			*nlast = '\0';
805 		} else {
806 			*last = '\0';
807 			multiplier *= 1000;	/* Convert to milliseconds */
808 		}
809 		break;
810 	}
811 
812 	if (!parse_int(str2, resp))
813 		return (_B_FALSE);
814 
815 	*resp *= multiplier;
816 	return (_B_TRUE);
817 }
818 
819 /*
820  * Parse something with a unit of seconds.
821  * Regognizes the suffixes "s", "m", "h", and "d".
822  *
823  * Returns true if ok (and *resp updated) and false if failed.
824  */
825 static boolean_t
826 parse_s(char *str, uint_t *resp)
827 {
828 	/* Look at the last character */
829 	char *cp, *last;
830 	char str2[BUFSIZ];	/* For local modification */
831 	int multiplier = 1;
832 
833 	(void) strncpy(str2, str, sizeof (str2));
834 	str2[sizeof (str2) - 1] = '\0';
835 
836 	last = str2;
837 	for (cp = str2; *cp != '\0'; cp++) {
838 		last = cp;
839 	}
840 	if (debug & D_PARSE) {
841 		logmsg(LOG_DEBUG, "parse_s: last <%c>\n",
842 		    (last != NULL ? *last : ' '));
843 	}
844 	switch (*last) {
845 	case 'd':
846 		multiplier *= 24;
847 		/* FALLTHRU */
848 	case 'h':
849 		multiplier *= 60;
850 		/* FALLTHRU */
851 	case 'm':
852 		multiplier *= 60;
853 		/* FALLTHRU */
854 	case 's':
855 		*last = '\0';
856 		break;
857 	}
858 	if (!parse_int(str2, resp))
859 		return (_B_FALSE);
860 
861 	*resp *= multiplier;
862 	return (_B_TRUE);
863 }
864 
865 /*
866  * Return prefixlen (0 to 128) if ok; -1 if failed.
867  */
868 static int
869 parse_addrprefix(char *strin, struct in6_addr *in6)
870 {
871 	char str[BUFSIZ];	/* Local copy for modification */
872 	int prefixlen;
873 	char *cp;
874 	char *end;
875 
876 	(void) strncpy(str, strin, sizeof (str));
877 	str[sizeof (str) - 1] = '\0';
878 
879 	cp = strchr(str, '/');
880 	if (cp == NULL)
881 		return (-1);
882 	*cp = '\0';
883 	cp++;
884 
885 	prefixlen = strtol(cp, &end, 10);
886 	if (cp == end)
887 		return (-1);
888 
889 	if (prefixlen < 0 || prefixlen > IPV6_ABITS)
890 		return (-1);
891 
892 	if (inet_pton(AF_INET6, str, in6) != 1)
893 		return (-1);
894 
895 	return (prefixlen);
896 }
897 
898 /*
899  * Parse an absolute date using a datemsk config file.
900  * Return the difference (measured in seconds) between that date/time and
901  * the current date/time.
902  * If the date has passed return zero.
903  *
904  * Returns true if ok (and *resp updated) and false if failed.
905  * XXX Due to getdate limitations can not exceed year 2038.
906  */
907 static boolean_t
908 parse_date(char *str, uint_t *resp)
909 {
910 	struct tm *tm;
911 	struct timeval tvs;
912 	time_t time, ntime;
913 
914 	if (getenv("DATEMSK") == NULL) {
915 		(void) putenv("DATEMSK=/etc/inet/datemsk.ndpd");
916 	}
917 
918 	if (gettimeofday(&tvs, NULL) < 0) {
919 		logperror("gettimeofday");
920 		return (_B_FALSE);
921 	}
922 	time = tvs.tv_sec;
923 	tm = getdate(str);
924 	if (tm == NULL) {
925 		logmsg(LOG_ERR, "Bad date <%s> (error %d)\n",
926 		    str, getdate_err);
927 		return (_B_FALSE);
928 	}
929 
930 	ntime = mktime(tm);
931 
932 	if (debug & D_PARSE) {
933 		char buf[BUFSIZ];
934 
935 		(void) strftime(buf, sizeof (buf), "%Y-%m-%d %R %Z", tm);
936 		logmsg(LOG_DEBUG, "parse_date: <%s>, delta %ld seconds\n",
937 		    buf, ntime - time);
938 	}
939 	if (ntime < time) {
940 		conferr("Date in the past <%s>\n", str);
941 		*resp = 0;
942 		return (_B_TRUE);
943 	}
944 	*resp = (ntime - time);
945 	return (_B_TRUE);
946 }
947 
948 /* PRINTFLIKE1 */
949 static void
950 conferr(char *fmt, ...)
951 {
952 	char msg[NDPD_LOGMSGSIZE];
953 	size_t slen;
954 
955 	va_list ap;
956 	va_start(ap, fmt);
957 
958 	(void) snprintf(msg, NDPD_LOGMSGSIZE, "%s line %d: ",
959 	    conf_filename, lineno);
960 	slen = strlen(msg);
961 	(void) vsnprintf(msg + slen, NDPD_LOGMSGSIZE - slen, fmt, ap);
962 
963 	logmsg(LOG_ERR, "%s", msg);
964 
965 	va_end(ap);
966 }
967 
968 static FILE *
969 open_conffile(char *filename)
970 {
971 	if (strlcpy(conf_filename, filename, MAXPATHLEN) >= MAXPATHLEN) {
972 		logmsg(LOG_ERR, "config file pathname is too long\n");
973 		return (NULL);
974 	}
975 
976 	lineno = 0;
977 
978 	return (fopen(filename, "r"));
979 
980 }
981