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