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