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