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
check_var_consistency(struct confvar * cv,void * save,int size)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
check_if_var_consistency(struct confvar * cv,void * save,int size)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
parse_config(char * config_file,boolean_t file_required)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
set_protocol_defaults(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
print_iflist(struct confvar * confvar)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
print_prefixlist(struct confvar * confvar)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
print_defaults(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
readline(FILE * fp,char * line,int length)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
parse_line(char * line,char * argvec[],int argcount)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
parse_var_value(config_type_t type,struct configinfo * list,char * varstr,char * valstr,struct confvar * confvar)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
parse_default(config_type_t type,struct configinfo * list,char * argvec[],int argcount,struct confvar * defaults)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
parse_if(struct configinfo * list,char * argvec[],int argcount)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
parse_prefix(struct configinfo * list,char * argvec[],int argcount)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
parse_onoff(char * str,uint_t * resp)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
parse_int(char * str,uint_t * resp)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
parse_ms(char * str,uint_t * resp)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
parse_s(char * str,uint_t * resp)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
parse_addrprefix(char * strin,struct in6_addr * in6)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
parse_date(char * str,uint_t * resp)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
conferr(char * fmt,...)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 *
open_conffile(char * filename)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