xref: /openbsd/usr.sbin/dvmrpd/kroute.c (revision 6c98de8b)
1 /*	$OpenBSD: kroute.c,v 1.2 2006/06/01 22:07:30 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
5  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/sysctl.h>
24 #include <sys/tree.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <net/if.h>
28 #include <net/if_dl.h>
29 #include <net/route.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include "dvmrp.h"
39 #include "dvmrpd.h"
40 #include "log.h"
41 
42 struct {
43 	u_int32_t		rtseq;
44 	pid_t			pid;
45 	struct event		ev;
46 } kr_state;
47 
48 struct kif_node {
49 	RB_ENTRY(kif_node)	 entry;
50 	struct kif		 k;
51 };
52 
53 int	kif_compare(struct kif_node *, struct kif_node *);
54 
55 struct kif_node		*kif_find(int);
56 int			 kif_insert(struct kif_node *);
57 int			 kif_remove(struct kif_node *);
58 void			 kif_clear(void);
59 
60 in_addr_t	prefixlen2mask(u_int8_t);
61 void		get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
62 void		if_change(u_short, int, struct if_data *);
63 void		if_announce(void *);
64 
65 int		fetchifs(int);
66 
67 RB_HEAD(kif_tree, kif_node)		kit;
68 RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
69 RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
70 
71 int
72 kif_init(void)
73 {
74 	RB_INIT(&kit);
75 
76 	if (fetchifs(0) == -1)
77 		return (-1);
78 
79 	return (0);
80 }
81 
82 int
83 kr_init(void)
84 {
85 	int opt, fd;
86 
87 	if ((fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
88 		log_warn("kr_init: socket");
89 		return (-1);
90 	}
91 
92 	/* not interested in my own messages */
93 	if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, sizeof(opt)) == -1)
94 		log_warn("kr_init: setsockopt");	/* not fatal */
95 
96 	kr_state.pid = getpid();
97 	kr_state.rtseq = 1;
98 
99 	event_set(&kr_state.ev, fd, EV_READ | EV_PERSIST,
100 	    kr_dispatch_msg, NULL);
101 	event_add(&kr_state.ev, NULL);
102 
103 	return (0);
104 }
105 
106 void
107 kr_shutdown(void)
108 {
109 	kif_clear();
110 	close(EVENT_FD((&kr_state.ev)));
111 }
112 
113 void
114 kr_ifinfo(char *ifname)
115 {
116 	struct kif_node	*kif;
117 
118 	RB_FOREACH(kif, kif_tree, &kit)
119 		if (!strcmp(ifname, kif->k.ifname)) {
120 			main_imsg_compose_dvmrpe(IMSG_CTL_IFINFO, 0,
121 			    &kif->k, sizeof(kif->k));
122 			return;
123 		}
124 }
125 
126 /* rb-tree compare */
127 int
128 kif_compare(struct kif_node *a, struct kif_node *b)
129 {
130 	return (b->k.ifindex - a->k.ifindex);
131 }
132 
133 struct kif_node *
134 kif_find(int ifindex)
135 {
136 	struct kif_node	s;
137 
138 	bzero(&s, sizeof(s));
139 	s.k.ifindex = ifindex;
140 
141 	return (RB_FIND(kif_tree, &kit, &s));
142 }
143 
144 struct kif *
145 kif_findname(char *ifname)
146 {
147 	struct kif_node	*kif;
148 
149 	RB_FOREACH(kif, kif_tree, &kit)
150 		if (!strcmp(ifname, kif->k.ifname))
151 			return (&kif->k);
152 
153 	return (NULL);
154 }
155 
156 int
157 kif_insert(struct kif_node *kif)
158 {
159 	if (RB_INSERT(kif_tree, &kit, kif) != NULL) {
160 		log_warnx("RB_INSERT(kif_tree, &kit, kif)");
161 		free(kif);
162 		return (-1);
163 	}
164 
165 	return (0);
166 }
167 
168 int
169 kif_remove(struct kif_node *kif)
170 {
171 	if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
172 		log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
173 		return (-1);
174 	}
175 
176 	free(kif);
177 	return (0);
178 }
179 
180 void
181 kif_clear(void)
182 {
183 	struct kif_node	*kif;
184 
185 	while ((kif = RB_MIN(kif_tree, &kit)) != NULL)
186 		kif_remove(kif);
187 }
188 
189 /* misc */
190 u_int8_t
191 prefixlen_classful(in_addr_t ina)
192 {
193 	/* it hurt to write this. */
194 
195 	if (ina >= 0xf0000000U)		/* class E */
196 		return (32);
197 	else if (ina >= 0xe0000000U)	/* class D */
198 		return (4);
199 	else if (ina >= 0xc0000000U)	/* class C */
200 		return (24);
201 	else if (ina >= 0x80000000U)	/* class B */
202 		return (16);
203 	else				/* class A */
204 		return (8);
205 }
206 
207 u_int8_t
208 mask2prefixlen(in_addr_t ina)
209 {
210 	if (ina == 0)
211 		return (0);
212 	else
213 		return (33 - ffs(ntohl(ina)));
214 }
215 
216 in_addr_t
217 prefixlen2mask(u_int8_t prefixlen)
218 {
219 	if (prefixlen == 0)
220 		return (0);
221 
222 	return (0xffffffff << (32 - prefixlen));
223 }
224 
225 void
226 if_change(u_short ifindex, int flags, struct if_data *ifd)
227 {
228 	struct kif_node		*kif;
229 	u_int8_t		 reachable;
230 
231 	if ((kif = kif_find(ifindex)) == NULL) {
232 		log_warnx("interface with index %u not found",
233 		    ifindex);
234 		return;
235 	}
236 
237 	kif->k.flags = flags;
238 	kif->k.link_state = ifd->ifi_link_state;
239 	kif->k.media_type = ifd->ifi_type;
240 	kif->k.baudrate = ifd->ifi_baudrate;
241 
242 	if ((reachable = (flags & IFF_UP) &&
243 	    (ifd->ifi_link_state != LINK_STATE_DOWN)) == kif->k.nh_reachable)
244 		return;		/* nothing changed wrt nexthop validity */
245 
246 	kif->k.nh_reachable = reachable;
247 	main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
248 }
249 
250 void
251 if_announce(void *msg)
252 {
253 	struct if_announcemsghdr	*ifan;
254 	struct kif_node			*kif;
255 
256 	ifan = msg;
257 
258 	switch (ifan->ifan_what) {
259 	case IFAN_ARRIVAL:
260 		if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
261 			log_warn("if_announce");
262 			return;
263 		}
264 
265 		kif->k.ifindex = ifan->ifan_index;
266 		strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
267 		kif_insert(kif);
268 		break;
269 	case IFAN_DEPARTURE:
270 		kif = kif_find(ifan->ifan_index);
271 		kif_remove(kif);
272 		break;
273 	}
274 }
275 
276 /* rtsock */
277 #define	ROUNDUP(a, size)	\
278     (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
279 
280 void
281 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
282 {
283 	int	i;
284 
285 	for (i = 0; i < RTAX_MAX; i++) {
286 		if (addrs & (1 << i)) {
287 			rti_info[i] = sa;
288 			sa = (struct sockaddr *)((char *)(sa) +
289 			    ROUNDUP(sa->sa_len, sizeof(long)));
290 		} else
291 			rti_info[i] = NULL;
292 	}
293 }
294 
295 int
296 fetchifs(int ifindex)
297 {
298 	size_t			 len;
299 	int			 mib[6];
300 	char			*buf, *next, *lim;
301 	struct if_msghdr	 ifm;
302 	struct kif_node		*kif;
303 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
304 	struct sockaddr_dl	*sdl;
305 
306 	mib[0] = CTL_NET;
307 	mib[1] = AF_ROUTE;
308 	mib[2] = 0;
309 	mib[3] = AF_INET;
310 	mib[4] = NET_RT_IFLIST;
311 	mib[5] = ifindex;
312 
313 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
314 		log_warn("sysctl");
315 		return (-1);
316 	}
317 	if ((buf = malloc(len)) == NULL) {
318 		log_warn("fetchif");
319 		return (-1);
320 	}
321 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
322 		log_warn("sysctl");
323 		free(buf);
324 		return (-1);
325 	}
326 
327 	lim = buf + len;
328 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
329 		memcpy(&ifm, next, sizeof(ifm));
330 		sa = (struct sockaddr *)(next + sizeof(ifm));
331 		get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
332 
333 		if (ifm.ifm_type != RTM_IFINFO)
334 			continue;
335 
336 		if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
337 			log_warn("fetchifs");
338 			free(buf);
339 			return (-1);
340 		}
341 
342 		kif->k.ifindex = ifm.ifm_index;
343 		kif->k.flags = ifm.ifm_flags;
344 		kif->k.link_state = ifm.ifm_data.ifi_link_state;
345 		kif->k.media_type = ifm.ifm_data.ifi_type;
346 		kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
347 		kif->k.mtu = ifm.ifm_data.ifi_mtu;
348 		kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
349 		    (ifm.ifm_data.ifi_link_state != LINK_STATE_DOWN);
350 		if ((sa = rti_info[RTAX_IFP]) != NULL)
351 			if (sa->sa_family == AF_LINK) {
352 				sdl = (struct sockaddr_dl *)sa;
353 				if (sdl->sdl_nlen > 0)
354 					strlcpy(kif->k.ifname, sdl->sdl_data,
355 					    sizeof(kif->k.ifname));
356 			}
357 
358 		kif_insert(kif);
359 	}
360 	free(buf);
361 	return (0);
362 }
363 
364 void
365 kr_dispatch_msg(int fd, short event, void *bula)
366 {
367 	char			 buf[RT_BUF_SIZE];
368 	ssize_t			 n;
369 	char			*next, *lim;
370 	struct rt_msghdr	*rtm;
371 	struct if_msghdr	 ifm;
372 
373 	if ((n = read(fd, &buf, sizeof(buf))) == -1)
374 		fatal("dispatch_rtmsg: read error");
375 
376 	if (n == 0)
377 		fatalx("routing socket closed");
378 
379 	lim = buf + n;
380 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
381 		rtm = (struct rt_msghdr *)next;
382 
383 		switch (rtm->rtm_type) {
384 		case RTM_IFINFO:
385 			memcpy(&ifm, next, sizeof(ifm));
386 			if_change(ifm.ifm_index, ifm.ifm_flags,
387 			    &ifm.ifm_data);
388 			break;
389 		case RTM_IFANNOUNCE:
390 			if_announce(next);
391 			break;
392 		default:
393 			/* ignore for now */
394 			break;
395 		}
396 	}
397 }
398 
399