xref: /original-bsd/sbin/routed/startup.c (revision 00695d63)
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)startup.c	8.2 (Berkeley) 04/28/95";
10 #endif /* not lint */
11 
12 /*
13  * Routing Table Management Daemon
14  */
15 #include "defs.h"
16 #include <sys/ioctl.h>
17 #include <sys/sysctl.h>
18 #include <net/if.h>
19 #include <net/if_dl.h>
20 #include <syslog.h>
21 #include <stdlib.h>
22 #include "pathnames.h"
23 
24 struct	interface *ifnet;
25 struct	interface **ifnext = &ifnet;
26 int	lookforinterfaces = 1;
27 int	externalinterfaces = 0;		/* # of remote and local interfaces */
28 int	foundloopback;			/* valid flag for loopaddr */
29 struct	sockaddr loopaddr;		/* our address on loopback */
30 
31 
32 void
33 quit(s)
34 	char *s;
35 {
36 	extern int errno;
37 	int sverrno = errno;
38 
39 	(void) fprintf(stderr, "route: ");
40 	if (s)
41 		(void) fprintf(stderr, "%s: ", s);
42 	(void) fprintf(stderr, "%s\n", strerror(sverrno));
43 	exit(1);
44 	/* NOTREACHED */
45 }
46 
47 struct rt_addrinfo info;
48 /* Sleazy use of local variables throughout file, warning!!!! */
49 #define netmask	info.rti_info[RTAX_NETMASK]
50 #define ifaaddr	info.rti_info[RTAX_IFA]
51 #define brdaddr	info.rti_info[RTAX_BRD]
52 
53 #define ROUNDUP(a) \
54 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
55 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
56 
57 void
58 rt_xaddrs(cp, cplim, rtinfo)
59 	register caddr_t cp, cplim;
60 	register struct rt_addrinfo *rtinfo;
61 {
62 	register struct sockaddr *sa;
63 	register int i;
64 
65 	memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
66 	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
67 		if ((rtinfo->rti_addrs & (1 << i)) == 0)
68 			continue;
69 		rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
70 		ADVANCE(cp, sa);
71 	}
72 }
73 
74 /*
75  * Find the network interfaces which have configured themselves.
76  * If the interface is present but not yet up (for example an
77  * ARPANET IMP), set the lookforinterfaces flag so we'll
78  * come back later and look again.
79  */
80 ifinit()
81 {
82 	struct interface ifs, *ifp;
83 	size_t needed;
84 	int mib[6], no_ipaddr = 0, flags = 0;
85 	char *buf, *cplim, *cp;
86 	register struct if_msghdr *ifm;
87 	register struct ifa_msghdr *ifam;
88 	struct sockaddr_dl *sdl;
89         struct sockaddr_in *sin;
90 	u_long i;
91 
92         mib[0] = CTL_NET;
93         mib[1] = PF_ROUTE;
94         mib[2] = 0;
95         mib[3] = AF_INET;
96         mib[4] = NET_RT_IFLIST;
97         mib[5] = 0;
98         if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
99                 quit("route-sysctl-estimate");
100 	if ((buf = malloc(needed)) == NULL)
101 		quit("malloc");
102         if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
103 		quit("actual retrieval of interface table");
104 	lookforinterfaces = 0;
105 	cplim = buf + needed;
106 	for (cp = buf; cp < cplim; cp += ifm->ifm_msglen) {
107 		ifm = (struct if_msghdr *)cp;
108 		if (ifm->ifm_type == RTM_IFINFO) {
109 			memset(&ifs, 0, sizeof(ifs));
110 			ifs.int_flags = flags = (0xffff & ifm->ifm_flags) | IFF_INTERFACE;
111 			if ((flags & IFF_UP) == 0 || no_ipaddr)
112 				lookforinterfaces = 1;
113 			sdl = (struct sockaddr_dl *) (ifm + 1);
114 			sdl->sdl_data[sdl->sdl_nlen] = 0;
115 			no_ipaddr = 1;
116 			continue;
117 		}
118 		if (ifm->ifm_type != RTM_NEWADDR)
119 			quit("ifinit: out of sync");
120 		if ((flags & IFF_UP) == 0)
121 			continue;
122 		ifam = (struct ifa_msghdr *)ifm;
123 		info.rti_addrs = ifam->ifam_addrs;
124 		rt_xaddrs((char *)(ifam + 1), cp + ifam->ifam_msglen, &info);
125 		if (ifaaddr == 0) {
126 			syslog(LOG_ERR, "%s: (get addr)", sdl->sdl_data);
127 			continue;
128 		}
129 		ifs.int_addr = *ifaaddr;
130 		if (ifs.int_addr.sa_family != AF_INET)
131 			continue;
132 		no_ipaddr = 0;
133 		if (ifs.int_flags & IFF_POINTOPOINT) {
134 			if (brdaddr == 0) {
135 				syslog(LOG_ERR, "%s: (get dstaddr)",
136 					sdl->sdl_data);
137 				continue;
138 			}
139 			if (brdaddr->sa_family == AF_UNSPEC) {
140 				lookforinterfaces = 1;
141 				continue;
142 			}
143 			ifs.int_dstaddr = *brdaddr;
144 		}
145 		/*
146 		 * already known to us?
147 		 * This allows multiple point-to-point links
148 		 * to share a source address (possibly with one
149 		 * other link), but assumes that there will not be
150 		 * multiple links with the same destination address.
151 		 */
152 		if (ifs.int_flags & IFF_POINTOPOINT) {
153 			if (if_ifwithdstaddr(&ifs.int_dstaddr))
154 				continue;
155 		} else if (if_ifwithaddr(&ifs.int_addr))
156 			continue;
157 		if (ifs.int_flags & IFF_LOOPBACK) {
158 			ifs.int_flags |= IFF_PASSIVE;
159 			foundloopback = 1;
160 			loopaddr = ifs.int_addr;
161 			for (ifp = ifnet; ifp; ifp = ifp->int_next)
162 			    if (ifp->int_flags & IFF_POINTOPOINT)
163 				add_ptopt_localrt(ifp);
164 		}
165 		if (ifs.int_flags & IFF_BROADCAST) {
166 			if (brdaddr == 0) {
167 				syslog(LOG_ERR, "%s: (get broadaddr)",
168 					sdl->sdl_data);
169 				continue;
170 			}
171 			ifs.int_dstaddr = *brdaddr;
172 		}
173 		/*
174 		 * Use a minimum metric of one;
175 		 * treat the interface metric (default 0)
176 		 * as an increment to the hop count of one.
177 		 */
178 		ifs.int_metric = ifam->ifam_metric + 1;
179 		if (netmask == 0) {
180 				syslog(LOG_ERR, "%s: (get netmask)",
181 					sdl->sdl_data);
182 				continue;
183 		}
184 		sin = (struct sockaddr_in *)netmask;
185 		ifs.int_subnetmask = ntohl(sin->sin_addr.s_addr);
186 		sin = (struct sockaddr_in *)&ifs.int_addr;
187 		i = ntohl(sin->sin_addr.s_addr);
188 		if (IN_CLASSA(i))
189 			ifs.int_netmask = IN_CLASSA_NET;
190 		else if (IN_CLASSB(i))
191 			ifs.int_netmask = IN_CLASSB_NET;
192 		else
193 			ifs.int_netmask = IN_CLASSC_NET;
194 		ifs.int_net = i & ifs.int_netmask;
195 		ifs.int_subnet = i & ifs.int_subnetmask;
196 		if (ifs.int_subnetmask != ifs.int_netmask)
197 			ifs.int_flags |= IFF_SUBNET;
198 		ifp = (struct interface *)
199 			malloc(sdl->sdl_nlen + 1 + sizeof(ifs));
200 		if (ifp == 0) {
201 			printf("routed: out of memory\n");
202 			lookforinterfaces = 1;
203 			break;
204 		}
205 		*ifp = ifs;
206 		/*
207 		 * Count the # of directly connected networks
208 		 * and point to point links which aren't looped
209 		 * back to ourself.  This is used below to
210 		 * decide if we should be a routing ``supplier''.
211 		 */
212 		if ((ifs.int_flags & IFF_LOOPBACK) == 0 &&
213 		    ((ifs.int_flags & IFF_POINTOPOINT) == 0 ||
214 		    if_ifwithaddr(&ifs.int_dstaddr) == 0))
215 			externalinterfaces++;
216 		/*
217 		 * If we have a point-to-point link, we want to act
218 		 * as a supplier even if it's our only interface,
219 		 * as that's the only way our peer on the other end
220 		 * can tell that the link is up.
221 		 */
222 		if ((ifs.int_flags & IFF_POINTOPOINT) && supplier < 0)
223 			supplier = 1;
224 		ifp->int_name = (char *)(ifp + 1);
225 		strcpy(ifp->int_name, sdl->sdl_data);
226 		*ifnext = ifp;
227 		ifnext = &ifp->int_next;
228 		traceinit(ifp);
229 		addrouteforif(ifp);
230 	}
231 	if (externalinterfaces > 1 && supplier < 0)
232 		supplier = 1;
233 	free(buf);
234 }
235 
236 /*
237  * Add route for interface if not currently installed.
238  * Create route to other end if a point-to-point link,
239  * otherwise a route to this (sub)network.
240  * INTERNET SPECIFIC.
241  */
242 addrouteforif(ifp)
243 	register struct interface *ifp;
244 {
245 	struct sockaddr_in net;
246 	struct sockaddr *dst;
247 	int state;
248 	register struct rt_entry *rt;
249 
250 	if (ifp->int_flags & IFF_POINTOPOINT)
251 		dst = &ifp->int_dstaddr;
252 	else {
253 		memset(&net, 0, sizeof (net));
254 		net.sin_family = AF_INET;
255 		net.sin_addr = inet_makeaddr(ifp->int_subnet, INADDR_ANY);
256 		dst = (struct sockaddr *)&net;
257 	}
258 	rt = rtfind(dst);
259 	if (rt &&
260 	    (rt->rt_state & (RTS_INTERFACE | RTS_INTERNAL)) == RTS_INTERFACE)
261 		return;
262 	if (rt)
263 		rtdelete(rt);
264 	/*
265 	 * If interface on subnetted network,
266 	 * install route to network as well.
267 	 * This is meant for external viewers.
268 	 */
269 	if ((ifp->int_flags & (IFF_SUBNET|IFF_POINTOPOINT)) == IFF_SUBNET) {
270 		struct in_addr subnet;
271 
272 		subnet = net.sin_addr;
273 		net.sin_addr = inet_makeaddr(ifp->int_net, INADDR_ANY);
274 		rt = rtfind(dst);
275 		if (rt == 0)
276 			rtadd(dst, &ifp->int_addr, ifp->int_metric,
277 			    ((ifp->int_flags & (IFF_INTERFACE|IFF_REMOTE)) |
278 			    RTS_PASSIVE | RTS_INTERNAL | RTS_SUBNET));
279 		else if ((rt->rt_state & (RTS_INTERNAL|RTS_SUBNET)) ==
280 		    (RTS_INTERNAL|RTS_SUBNET) &&
281 		    ifp->int_metric < rt->rt_metric)
282 			rtchange(rt, &rt->rt_router, ifp->int_metric);
283 		net.sin_addr = subnet;
284 	}
285 	if (ifp->int_transitions++ > 0)
286 		syslog(LOG_ERR, "re-installing interface %s", ifp->int_name);
287 	state = ifp->int_flags &
288 	    (IFF_INTERFACE | IFF_PASSIVE | IFF_REMOTE | IFF_SUBNET);
289 	if (ifp->int_flags & IFF_POINTOPOINT &&
290 	    (ntohl(((struct sockaddr_in *)&ifp->int_dstaddr)->sin_addr.s_addr) &
291 	    ifp->int_netmask) != ifp->int_net)
292 		state &= ~RTS_SUBNET;
293 	if (ifp->int_flags & IFF_LOOPBACK)
294 		state |= RTS_EXTERNAL;
295 	rtadd(dst, &ifp->int_addr, ifp->int_metric, state);
296 	if (ifp->int_flags & IFF_POINTOPOINT && foundloopback)
297 		add_ptopt_localrt(ifp);
298 }
299 
300 /*
301  * Add route to local end of point-to-point using loopback.
302  * If a route to this network is being sent to neighbors on other nets,
303  * mark this route as subnet so we don't have to propagate it too.
304  */
305 add_ptopt_localrt(ifp)
306 	register struct interface *ifp;
307 {
308 	struct rt_entry *rt;
309 	struct sockaddr *dst;
310 	struct sockaddr_in net;
311 	int state;
312 
313 	state = RTS_INTERFACE | RTS_PASSIVE;
314 
315 	/* look for route to logical network */
316 	memset(&net, 0, sizeof (net));
317 	net.sin_family = AF_INET;
318 	net.sin_addr = inet_makeaddr(ifp->int_net, INADDR_ANY);
319 	dst = (struct sockaddr *)&net;
320 	rt = rtfind(dst);
321 	if (rt && rt->rt_state & RTS_INTERNAL)
322 		state |= RTS_SUBNET;
323 
324 	dst = &ifp->int_addr;
325 	if (rt = rtfind(dst)) {
326 		if (rt && rt->rt_state & RTS_INTERFACE)
327 			return;
328 		rtdelete(rt);
329 	}
330 	rtadd(dst, &loopaddr, 1, state);
331 }
332 
333 /*
334  * As a concession to the ARPANET we read a list of gateways
335  * from /etc/gateways and add them to our tables.  This file
336  * exists at each ARPANET gateway and indicates a set of ``remote''
337  * gateways (i.e. a gateway which we can't immediately determine
338  * if it's present or not as we can do for those directly connected
339  * at the hardware level).  If a gateway is marked ``passive''
340  * in the file, then we assume it doesn't have a routing process
341  * of our design and simply assume it's always present.  Those
342  * not marked passive are treated as if they were directly
343  * connected -- they're added into the interface list so we'll
344  * send them routing updates.
345  *
346  * PASSIVE ENTRIES AREN'T NEEDED OR USED ON GATEWAYS RUNNING EGP.
347  */
348 gwkludge()
349 {
350 	struct sockaddr_in dst, gate;
351 	FILE *fp;
352 	char *type, *dname, *gname, *qual, buf[BUFSIZ];
353 	struct interface *ifp;
354 	int metric, n;
355 	struct rt_entry route;
356 
357 	fp = fopen(_PATH_GATEWAYS, "r");
358 	if (fp == NULL)
359 		return;
360 	qual = buf;
361 	dname = buf + 64;
362 	gname = buf + ((BUFSIZ - 64) / 3);
363 	type = buf + (((BUFSIZ - 64) * 2) / 3);
364 	memset(&dst, 0, sizeof (dst));
365 	memset(&gate, 0, sizeof (gate));
366 	memset(&route, 0, sizeof(route));
367 /* format: {net | host} XX gateway XX metric DD [passive | external]\n */
368 #define	readentry(fp) \
369 	fscanf((fp), "%s %s gateway %s metric %d %s\n", \
370 		type, dname, gname, &metric, qual)
371 	for (;;) {
372 		if ((n = readentry(fp)) == EOF)
373 			break;
374 		if (!getnetorhostname(type, dname, &dst))
375 			continue;
376 		if (!gethostnameornumber(gname, &gate))
377 			continue;
378 		if (metric == 0)			/* XXX */
379 			metric = 1;
380 		if (strcmp(qual, "passive") == 0) {
381 			/*
382 			 * Passive entries aren't placed in our tables,
383 			 * only the kernel's, so we don't copy all of the
384 			 * external routing information within a net.
385 			 * Internal machines should use the default
386 			 * route to a suitable gateway (like us).
387 			 */
388 			route.rt_dst = *(struct sockaddr *) &dst;
389 			route.rt_router = *(struct sockaddr *) &gate;
390 			route.rt_flags = RTF_UP;
391 			if (strcmp(type, "host") == 0)
392 				route.rt_flags |= RTF_HOST;
393 			if (metric)
394 				route.rt_flags |= RTF_GATEWAY;
395 			(void) rtioctl(ADD, &route.rt_rt);
396 			continue;
397 		}
398 		if (strcmp(qual, "external") == 0) {
399 			/*
400 			 * Entries marked external are handled
401 			 * by other means, e.g. EGP,
402 			 * and are placed in our tables only
403 			 * to prevent overriding them
404 			 * with something else.
405 			 */
406 			rtadd(&dst, &gate, metric, RTS_EXTERNAL|RTS_PASSIVE);
407 			continue;
408 		}
409 		/* assume no duplicate entries */
410 		externalinterfaces++;
411 		ifp = (struct interface *)malloc(sizeof (*ifp));
412 		memset(ifp, 0, sizeof (*ifp));
413 		ifp->int_flags = IFF_REMOTE;
414 		/* can't identify broadcast capability */
415 		ifp->int_net = inet_netof(dst.sin_addr);
416 		if (strcmp(type, "host") == 0) {
417 			ifp->int_flags |= IFF_POINTOPOINT;
418 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
419 		}
420 		ifp->int_addr = *((struct sockaddr *)&gate);
421 		ifp->int_metric = metric;
422 		ifp->int_next = ifnet;
423 		ifnet = ifp;
424 		addrouteforif(ifp);
425 	}
426 	fclose(fp);
427 }
428 
429 getnetorhostname(type, name, sin)
430 	char *type, *name;
431 	struct sockaddr_in *sin;
432 {
433 
434 	if (strcmp(type, "net") == 0) {
435 		struct netent *np = getnetbyname(name);
436 		int n;
437 
438 		if (np == 0)
439 			n = inet_network(name);
440 		else {
441 			if (np->n_addrtype != AF_INET)
442 				return (0);
443 			n = np->n_net;
444 			/*
445 			 * getnetbyname returns right-adjusted value.
446 			 */
447 			if (n < 128)
448 				n <<= IN_CLASSA_NSHIFT;
449 			else if (n < 65536)
450 				n <<= IN_CLASSB_NSHIFT;
451 			else
452 				n <<= IN_CLASSC_NSHIFT;
453 		}
454 		sin->sin_family = AF_INET;
455 		sin->sin_addr = inet_makeaddr(n, INADDR_ANY);
456 		return (1);
457 	}
458 	if (strcmp(type, "host") == 0) {
459 		struct hostent *hp = gethostbyname(name);
460 
461 		if (hp == 0)
462 			sin->sin_addr.s_addr = inet_addr(name);
463 		else {
464 			if (hp->h_addrtype != AF_INET)
465 				return (0);
466 			memmove(&sin->sin_addr, hp->h_addr, hp->h_length);
467 		}
468 		sin->sin_family = AF_INET;
469 		return (1);
470 	}
471 	return (0);
472 }
473 
474 gethostnameornumber(name, sin)
475 	char *name;
476 	struct sockaddr_in *sin;
477 {
478 	struct hostent *hp;
479 
480 	hp = gethostbyname(name);
481 	if (hp) {
482 		memmove(&sin->sin_addr, hp->h_addr, hp->h_length);
483 		sin->sin_family = hp->h_addrtype;
484 		return (1);
485 	}
486 	sin->sin_addr.s_addr = inet_addr(name);
487 	sin->sin_family = AF_INET;
488 	return (sin->sin_addr.s_addr != -1);
489 }
490