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
47typedef	boolean_t	(*pfb_t)(char *, uint_t *);
48
49struct 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
58enum config_type { CONFIG_IF, CONFIG_PREFIX};
59typedef enum config_type config_type_t;
60
61static void set_protocol_defaults(void);
62static void print_defaults(void);
63static void parse_var_value(config_type_t, struct configinfo *, char *, char *,
64    struct confvar *);
65static void parse_default(config_type_t, struct configinfo *, char **, int,
66    struct confvar *);
67static void parse_if(struct configinfo *, char **, int);
68static void parse_prefix(struct configinfo *, char **, int);
69static boolean_t parse_onoff(char *, uint_t *);	/* boolean */
70static boolean_t parse_int(char *, uint_t *);	/* integer */
71static boolean_t parse_ms(char *, uint_t *);	/* milliseconds */
72static boolean_t parse_s(char *, uint_t *);	/* seconds */
73static boolean_t parse_date(char *, uint_t *);	/* date format */
74static void conferr(char *fmt, ...);
75static FILE *open_conffile(char *filename);
76static int parse_line(char *line, char *argvec[], int argcount);
77static int readline(FILE *fp, char *line, int length);
78static 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 */
84static 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 */
125static 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 */
146struct confvar ifdefaults[I_IFSIZE];
147static struct confvar prefixdefaults[I_PREFIXSIZE];
148
149static char	conf_filename[MAXPATHLEN];
150static int	lineno;
151
152/*
153 * Checks for violations of section 5.5.3 (c) of RFC 2462.
154 */
155static void
156check_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 */
185static void
186check_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
225int
226parse_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 */
323static void
324set_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
340void
341print_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
353void
354print_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
367static void
368print_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 */
380static int
381readline(FILE *fp, char *line, int length)
382{
383	int got = 0;
384
385retry:
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 */
418static int
419parse_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
492static void
493parse_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 */
579static void
580parse_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 */
598static void
599parse_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
643static void
644parse_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 */
720static boolean_t
721parse_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 */
749static boolean_t
750parse_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 */
768static boolean_t
769parse_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 */
827static boolean_t
828parse_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 */
870static int
871parse_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 */
909static boolean_t
910parse_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 */
951static void
952conferr(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
970static FILE *
971open_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