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