xref: /netbsd-src/usr.sbin/mrouted/config.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*
2  * The mrouted program is covered by the license in the accompanying file
3  * named "LICENSE".  Use of the mrouted program represents acceptance of
4  * the terms and conditions listed in that file.
5  *
6  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7  * Leland Stanford Junior University.
8  *
9  *
10  * from: Id: config.c,v 1.3 1993/05/30 01:36:38 deering Exp
11  *      $Id: config.c,v 1.3 1994/06/09 16:04:00 brezak Exp $
12  */
13 
14 #ifndef lint
15 static char rcsid[] = "$Id: config.c,v 1.3 1994/06/09 16:04:00 brezak Exp $";
16 #endif
17 
18 #include "defs.h"
19 
20 
21 char *configfilename = _PATH_MROUTED_CONF;
22 
23 
24 /*
25  * Forward declarations.
26  */
27 static char *next_word();
28 
29 
30 /*
31  * Query the kernel to find network interfaces that are multicast-capable
32  * and install them in the uvifs array.
33  */
34 void config_vifs_from_kernel()
35 {
36     struct ifreq ifbuf[32];
37     struct ifreq *ifrp, *ifend, *mp;
38     struct ifconf ifc;
39     register struct uvif *v;
40     register vifi_t vifi;
41     int i, n;
42     u_long addr, mask, subnet;
43     short flags;
44 
45     ifc.ifc_buf = (char *)ifbuf;
46     ifc.ifc_len = sizeof(ifbuf);
47     if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
48 	log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
49 
50     ifrp = (struct ifreq *)ifbuf;
51     ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len);
52     /*
53      * Loop through all of the interfaces.
54      */
55     for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
56 	struct ifreq ifr;
57 #if BSD >= 199006
58 	n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
59 	if (n < sizeof(*ifrp))
60 	    n = sizeof(*ifrp);
61 #else
62 	n = sizeof(*ifrp);
63 #endif
64 	/*
65 	 * Ignore any interface for an address family other than IP.
66 	 */
67 	addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
68 	if (ifrp->ifr_addr.sa_family != AF_INET)
69 	    continue;
70 
71 	/*
72 	 * Need a template to preserve address info that is
73 	 * used below to locate the next entry.  (Otherwise,
74 	 * SIOCGIFFLAGS stomps over it because the requests
75 	 * are returned in a union.)
76 	 */
77 	bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
78 
79 	/*
80 	 * Ignore loopback interfaces and interfaces that do not support
81 	 * multicast.
82 	 */
83 	if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
84 	    log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
85 	flags = ifr.ifr_flags;
86 	if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue;
87 
88 	/*
89 	 * Ignore any interface whose address and mask do not define a
90 	 * valid subnet number, or whose address is of the form {subnet,0}
91 	 * or {subnet,-1}.
92 	 */
93 	if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0)
94 	    log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name);
95 	mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
96 	subnet = addr & mask;
97 	if (!inet_valid_subnet(subnet, mask) ||
98 	    addr == subnet ||
99 	    addr == (subnet | ~mask)) {
100 	    log(LOG_WARNING, 0,
101 		"ignoring %s, has invalid address (%s) and/or mask (%08x)",
102 		ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask));
103 	    continue;
104 	}
105 
106 	/*
107 	 * Ignore any interface that is connected to the same subnet as
108 	 * one already installed in the uvifs array.
109 	 */
110 	for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
111 	    if ((addr & v->uv_subnetmask) == v->uv_subnet ||
112 		(v->uv_subnet & mask) == subnet) {
113 		log(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
114 					ifr.ifr_name, v->uv_name);
115 		break;
116 	    }
117 	}
118 	if (vifi != numvifs) continue;
119 
120 	/*
121 	 * If there is room in the uvifs array, install this interface.
122 	 */
123 	if (numvifs == MAXVIFS) {
124 	    log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name);
125 	    continue;
126 	}
127 	v  = &uvifs[numvifs];
128 	v->uv_flags       = 0;
129 	v->uv_metric      = DEFAULT_METRIC;
130 	v->uv_threshold   = DEFAULT_THRESHOLD;
131 	v->uv_lcl_addr    = addr;
132 	v->uv_rmt_addr    = 0;
133 	v->uv_subnet      = subnet;
134 	v->uv_subnetmask  = mask;
135 	v->uv_subnetbcast = subnet | ~mask;
136 	strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
137 	v->uv_groups      = NULL;
138 	v->uv_neighbors   = NULL;
139 
140 	log(LOG_INFO, 0, "installing %s (%s on subnet %s) as vif #%u",
141 	    v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
142 	    numvifs);
143 
144 	++numvifs;
145 
146 	/*
147 	 * If the interface is not yet up, set the vifs_down flag to
148 	 * remind us to check again later.
149 	 */
150 	if (!(flags & IFF_UP)) {
151 	    v->uv_flags |= VIFF_DOWN;
152 	    vifs_down = TRUE;
153 	}
154     }
155 }
156 
157 static struct ifreq *
158 ifconfaddr(ifcp, a)
159     struct ifconf *ifcp;
160     u_long a;
161 {
162     int n;
163     struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf;
164     struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len);
165 
166     while (ifrp < ifend) {
167 	    if (ifrp->ifr_addr.sa_family == AF_INET &&
168 		((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
169 		    return (ifrp);
170 #if BSD >= 199006
171 		n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
172 		if (n < sizeof(*ifrp))
173 			++ifrp;
174 		else
175 			ifrp = (struct ifreq *)((char *)ifrp + n);
176 #else
177 		++ifrp;
178 #endif
179     }
180     return (0);
181 }
182 /*
183  * Read the config file to learn about tunnel vifs and
184  * non-default phyint parameters.
185  */
186 void config_vifs_from_file()
187 {
188     FILE *f;
189     char linebuf[100];
190     char *w, *s, c;
191     u_long lcl_addr, rmt_addr;
192     struct ifconf ifc;
193     struct ifreq *ifr;
194     struct ifreq ffr;
195     int i;
196     u_int n;
197     struct ifreq ifbuf[32];
198     vifi_t vifi;
199     struct uvif *v;
200 
201     f = fopen(configfilename, "r");
202     if (f == NULL) {
203 	if (errno != ENOENT)
204 	    log(LOG_WARNING, errno, "can't open %s", configfilename);
205 	return;
206     }
207 
208     ifc.ifc_buf = (char *)ifbuf;
209     ifc.ifc_len = sizeof(ifbuf);
210     if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
211 	log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
212 
213     while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
214 
215 	s = linebuf;
216 	if (EQUAL((w = next_word(&s)), "")) {
217 	    /*
218 	     * blank or comment line; ignore
219 	     */
220 	}
221 
222 	else if (EQUAL(w, "phyint")) {
223 	    /*
224 	     * phyint <local-addr> [disable] [metric <m>] [threshold <t>]
225 	     */
226 
227 	    /*
228 	     * Parse the local address.
229 	     */
230 	    if (EQUAL((w = next_word(&s)), "")) {
231 		log(LOG_WARNING, 0,
232 		    "missing phyint address in %s",
233 		    configfilename);
234 		continue;
235 	    }
236 	    if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
237 		!inet_valid_host(lcl_addr)) {
238 		log(LOG_WARNING, 0,
239 		    "invalid phyint address '%s' in %s",
240 		    w, configfilename);
241 		continue;
242 	    }
243 
244 	    /*
245 	     * Look up the vif with the specified local address.
246 	     */
247 	    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
248 		if (!(v->uv_flags & VIFF_TUNNEL) &&
249 		    lcl_addr == v->uv_lcl_addr) {
250 		    break;
251 		}
252 	    }
253 	    if (vifi == numvifs) {
254 		log(LOG_WARNING, 0,
255 		    "phyint %s in %s is not a configured interface",
256 		    inet_fmt(lcl_addr, s1), configfilename);
257 		continue;
258 	    }
259 
260 	    /*
261 	     * Look for "disable", "metric" and "threshold" options.
262 	     */
263 	    while (!EQUAL((w = next_word(&s)), "")) {
264 		if (EQUAL(w, "disable")) {
265 		    v->uv_flags |= VIFF_DISABLED;
266 		}
267 		else if (EQUAL(w, "metric")) {
268 		    if(EQUAL((w = next_word(&s)), "")) {
269 			log(LOG_WARNING, 0,
270 			    "missing metric for phyint %s in %s",
271 			    inet_fmt(lcl_addr, s1), configfilename);
272 			w = "garbage";
273 			break;
274 		    }
275 		    if(sscanf(w, "%u%c", &n, &c) != 1 ||
276 			      n < 1 || n >= UNREACHABLE ) {
277 			log(LOG_WARNING, 0,
278 			    "invalid metric '%s' for phyint %s in %s",
279 			    w, inet_fmt(lcl_addr, s1), configfilename);
280 			break;
281 		    }
282 		    v->uv_metric = n;
283 		}
284 		else if (EQUAL(w, "threshold")) {
285 		    if(EQUAL((w = next_word(&s)), "")) {
286 			log(LOG_WARNING, 0,
287 			    "missing threshold for phyint %s in %s",
288 			    inet_fmt(lcl_addr, s1), configfilename);
289 			w = "garbage";
290 			break;
291 		    }
292 		    if(sscanf(w, "%u%c", &n, &c) != 1 ||
293 			      n < 1 || n > 255 ) {
294 			log(LOG_WARNING, 0,
295 			    "invalid threshold '%s' for phyint %s in %s",
296 			    w, inet_fmt(lcl_addr, s1), configfilename);
297 			break;
298 		    }
299 		    v->uv_threshold = n;
300 		}
301 		else break;
302 	    }
303 	    if (!EQUAL(w, "")) continue;
304 	}
305 
306 	else if (EQUAL(w, "tunnel")) {
307 	    /*
308 	     * tunnel <local-addr> <remote-addr> [srcrt] [metric <m>] [threshold <t>]
309 	     */
310 
311 	    /*
312 	     * Parse the local address.
313 	     */
314 	    if (EQUAL((w = next_word(&s)), "")) {
315 		log(LOG_WARNING, 0,
316 		    "missing tunnel local address in %s",
317 		    configfilename);
318 		continue;
319 	    }
320 	    if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
321 		!inet_valid_host(lcl_addr)) {
322 		log(LOG_WARNING, 0,
323 		    "invalid tunnel local address '%s' in %s",
324 		    w, configfilename);
325 		continue;
326 	    }
327 
328 	    /*
329 	     * Make sure the local address is one of ours.
330 	     */
331 	    ifr = ifconfaddr(&ifc, lcl_addr);
332 	    if (ifr == 0) {
333 		log(LOG_WARNING, 0,
334 		    "tunnel local address %s in %s is not one of ours",
335 		    inet_fmt(lcl_addr, s1), configfilename);
336 		continue;
337 	    }
338 
339 	    /*
340 	     * Make sure the local address doesn't name a loopback interface..
341 	     */
342 	    strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
343 	    if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) {
344 		log(LOG_ERR, errno,
345 		    "ioctl SIOCGIFFLAGS for %s", ffr.ifr_name);
346 	    }
347 	    if (ffr.ifr_flags & IFF_LOOPBACK) {
348 		log(LOG_WARNING, 0,
349 		    "tunnel local address %s in %s is a loopback interface",
350 		    inet_fmt(lcl_addr, s1), configfilename);
351 		continue;
352 	    }
353 
354 	    /*
355 	     * Parse the remote address.
356 	     */
357 	    if (EQUAL((w = next_word(&s)), "")) {
358 		log(LOG_WARNING, 0,
359 		    "missing tunnel remote address in %s",
360 		    configfilename);
361 		continue;
362 	    }
363 	    if ((rmt_addr = inet_parse(w)) == 0xffffffff ||
364 		!inet_valid_host(rmt_addr)) {
365 		log(LOG_WARNING, 0,
366 		    "invalid tunnel remote address %s in %s",
367 		    w, configfilename);
368 		continue;
369 	    }
370 
371 	    /*
372 	     * Make sure the remote address is not one of ours.
373 	     */
374 	    if (ifconfaddr(&ifc, rmt_addr) != 0) {
375 		log(LOG_WARNING, 0,
376 		    "tunnel remote address %s in %s is one of ours",
377 		    inet_fmt(rmt_addr, s1), configfilename);
378 		continue;
379 	    }
380 
381 	    /*
382 	     * Make sure the remote address has not been used for another
383 	     * tunnel and does not belong to a subnet to which we have direct
384 	     * access on an enabled phyint.
385 	     */
386 	    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
387 		if (v->uv_flags & VIFF_TUNNEL) {
388 		    if (rmt_addr == v->uv_rmt_addr) {
389 			log(LOG_WARNING, 0,
390 			    "duplicate tunnel remote address %s in %s",
391 			    inet_fmt(rmt_addr, s1), configfilename);
392 			break;
393 		    }
394 		}
395 		else if (!(v->uv_flags & VIFF_DISABLED)) {
396 		    if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) {
397 			log(LOG_WARNING, 0,
398 			    "unnecessary tunnel remote address %s in %s",
399 			    inet_fmt(rmt_addr, s1), configfilename);
400 			break;
401 		    }
402 		}
403 	    }
404 	    if (vifi != numvifs) continue;
405 
406 	    /*
407 	     * OK, let's initialize a uvif structure for the tunnel.
408 	     */
409 	    if (numvifs == MAXVIFS) {
410 		log(LOG_WARNING, 0, "too many vifs, ignoring tunnel to %s",
411 		    inet_fmt(rmt_addr, s1));
412 		continue;
413 	    }
414 	    v  = &uvifs[numvifs];
415 	    v->uv_flags       = VIFF_TUNNEL;
416 	    v->uv_metric      = DEFAULT_METRIC;
417 	    v->uv_threshold   = DEFAULT_THRESHOLD;
418 	    v->uv_lcl_addr    = lcl_addr;
419 	    v->uv_rmt_addr    = rmt_addr;
420 	    v->uv_subnet      = 0;
421 	    v->uv_subnetmask  = 0;
422 	    v->uv_subnetbcast = 0;
423 	    strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
424 	    v->uv_groups      = NULL;
425 	    v->uv_neighbors   = NULL;
426 
427 	    /*
428 	     * Look for "metric" and "threshold" options.
429 	     */
430 	    while (!EQUAL((w = next_word(&s)), "")) {
431 		if (EQUAL(w, "metric")) {
432 		    if(EQUAL((w = next_word(&s)), "")) {
433 			log(LOG_WARNING, 0,
434 			    "missing metric for tunnel to %s in %s",
435 			    inet_fmt(rmt_addr, s1), configfilename);
436 			w = "garbage";
437 			break;
438 		    }
439 		    if(sscanf(w, "%u%c", &n, &c) != 1 ||
440 			      n < 1 || n >= UNREACHABLE ) {
441 			log(LOG_WARNING, 0,
442 			    "invalid metric '%s' for tunnel to %s in %s",
443 			    w, inet_fmt(rmt_addr, s1), configfilename);
444 			break;
445 		    }
446 		    v->uv_metric = n;
447 		}
448 		else if (EQUAL(w, "threshold")) {
449 		    if(EQUAL((w = next_word(&s)), "")) {
450 			log(LOG_WARNING, 0,
451 			    "missing threshold for tunnel to %s in %s",
452 			    inet_fmt(rmt_addr, s1), configfilename);
453 			w = "garbage";
454 			break;
455 		    }
456 		    if(sscanf(w, "%u%c", &n, &c) != 1 ||
457 			      n < 1 || n > 255 ) {
458 			log(LOG_WARNING, 0,
459 			    "invalid threshold '%s' for tunnel to %s in %s",
460 			    w, inet_fmt(rmt_addr, s1), configfilename);
461 			break;
462 		    }
463 		    v->uv_threshold = n;
464 		}
465 		else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) {
466 		    v->uv_flags |= VIFF_SRCRT;
467 		}
468 		else break;
469 	    }
470 	    if (!EQUAL(w, "")) continue;
471 
472 	    log(LOG_INFO, 0,
473 		"installing %stunnel from %s to %s as vif #%u",
474 		v->uv_flags & VIFF_SRCRT? "srcrt " : "",
475 		inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2), numvifs);
476 
477 	    ++numvifs;
478 
479 	    if (!(ffr.ifr_flags & IFF_UP)) {
480 		v->uv_flags |= VIFF_DOWN;
481 		vifs_down = TRUE;
482 	    }
483 	}
484 
485 	else {
486 	    log(LOG_WARNING, 0,
487 		"unknown command '%s' in %s", w, configfilename);
488 	}
489     }
490 
491     close(f);
492 }
493 
494 
495 /*
496  * Return a pointer to the next "word" in the string to which '*s' points,
497  * lower-cased and null terminated, and advance '*s' to point beyond the word.
498  * Words are separated by blanks and/or tabs, and the input string is
499  * considered to terminate at a newline, '#' (comment), or null character.
500  * If no words remain, a pointer to a null string ("") is returned.
501  * Warning: This function clobbers the input string.
502  */
503 static char *next_word(s)
504     char **s;
505 {
506     char *w;
507 
508     w = *s;
509     while (*w == ' ' || *w == '\t')
510 	++w;
511 
512     *s = w;
513     for(;;) {
514 	switch (**s) {
515 
516 	    case ' '  :
517 	    case '\t' : **s = '\0';
518 			++*s;
519 			return (w);
520 
521 	    case '\n' :
522 	    case '#'  : **s = '\0';
523 			return (w);
524 
525 	    case '\0' : return (w);
526 
527 	    default   : if (isascii(**s) && isupper(**s))
528 			    **s = tolower(**s);
529 			++*s;
530 	}
531     }
532 }
533