xref: /openbsd/usr.sbin/dvmrpd/kroute.c (revision cca36db2)
1 /*	$OpenBSD: kroute.c,v 1.7 2011/07/04 04:34:14 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/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)) == kif->k.nh_reachable)
245 		return;		/* nothing changed wrt nexthop validity */
246 
247 	kif->k.nh_reachable = reachable;
248 	main_imsg_compose_dvmrpe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k));
249 }
250 
251 void
252 if_announce(void *msg)
253 {
254 	struct if_announcemsghdr	*ifan;
255 	struct kif_node			*kif;
256 
257 	ifan = msg;
258 
259 	switch (ifan->ifan_what) {
260 	case IFAN_ARRIVAL:
261 		if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
262 			log_warn("if_announce");
263 			return;
264 		}
265 
266 		kif->k.ifindex = ifan->ifan_index;
267 		strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname));
268 		kif_insert(kif);
269 		break;
270 	case IFAN_DEPARTURE:
271 		kif = kif_find(ifan->ifan_index);
272 		kif_remove(kif);
273 		break;
274 	}
275 }
276 
277 /* rtsock */
278 #define	ROUNDUP(a, size)	\
279     (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
280 
281 void
282 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
283 {
284 	int	i;
285 
286 	for (i = 0; i < RTAX_MAX; i++) {
287 		if (addrs & (1 << i)) {
288 			rti_info[i] = sa;
289 			sa = (struct sockaddr *)((char *)(sa) +
290 			    ROUNDUP(sa->sa_len, sizeof(long)));
291 		} else
292 			rti_info[i] = NULL;
293 	}
294 }
295 
296 int
297 fetchifs(int ifindex)
298 {
299 	size_t			 len;
300 	int			 mib[6];
301 	char			*buf, *next, *lim;
302 	struct if_msghdr	 ifm;
303 	struct kif_node		*kif;
304 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
305 	struct sockaddr_dl	*sdl;
306 
307 	mib[0] = CTL_NET;
308 	mib[1] = AF_ROUTE;
309 	mib[2] = 0;
310 	mib[3] = AF_INET;
311 	mib[4] = NET_RT_IFLIST;
312 	mib[5] = ifindex;
313 
314 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
315 		log_warn("sysctl");
316 		return (-1);
317 	}
318 	if ((buf = malloc(len)) == NULL) {
319 		log_warn("fetchif");
320 		return (-1);
321 	}
322 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
323 		log_warn("sysctl");
324 		free(buf);
325 		return (-1);
326 	}
327 
328 	lim = buf + len;
329 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
330 		memcpy(&ifm, next, sizeof(ifm));
331 		sa = (struct sockaddr *)(next + sizeof(ifm));
332 		get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
333 
334 		if (ifm.ifm_version != RTM_VERSION)
335 			continue;
336 		if (ifm.ifm_type != RTM_IFINFO)
337 			continue;
338 
339 		if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
340 			log_warn("fetchifs");
341 			free(buf);
342 			return (-1);
343 		}
344 
345 		kif->k.ifindex = ifm.ifm_index;
346 		kif->k.flags = ifm.ifm_flags;
347 		kif->k.link_state = ifm.ifm_data.ifi_link_state;
348 		kif->k.media_type = ifm.ifm_data.ifi_type;
349 		kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
350 		kif->k.mtu = ifm.ifm_data.ifi_mtu;
351 		kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
352 		    LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state);
353 		if ((sa = rti_info[RTAX_IFP]) != NULL)
354 			if (sa->sa_family == AF_LINK) {
355 				sdl = (struct sockaddr_dl *)sa;
356 				if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
357 					memcpy(kif->k.ifname, sdl->sdl_data,
358 					    sizeof(kif->k.ifname) - 1);
359 				else if (sdl->sdl_nlen > 0)
360 					memcpy(kif->k.ifname, sdl->sdl_data,
361 					    sdl->sdl_nlen);
362 				/* string already terminated via calloc() */
363 			}
364 
365 		kif_insert(kif);
366 	}
367 	free(buf);
368 	return (0);
369 }
370 
371 void
372 kr_dispatch_msg(int fd, short event, void *bula)
373 {
374 	char			 buf[RT_BUF_SIZE];
375 	ssize_t			 n;
376 	char			*next, *lim;
377 	struct rt_msghdr	*rtm;
378 	struct if_msghdr	 ifm;
379 
380 	if ((n = read(fd, &buf, sizeof(buf))) == -1)
381 		fatal("dispatch_rtmsg: read error");
382 
383 	if (n == 0)
384 		fatalx("routing socket closed");
385 
386 	lim = buf + n;
387 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
388 		rtm = (struct rt_msghdr *)next;
389 		if (rtm->rtm_version != RTM_VERSION)
390 			continue;
391 
392 		switch (rtm->rtm_type) {
393 		case RTM_IFINFO:
394 			memcpy(&ifm, next, sizeof(ifm));
395 			if_change(ifm.ifm_index, ifm.ifm_flags,
396 			    &ifm.ifm_data);
397 			break;
398 		case RTM_IFANNOUNCE:
399 			if_announce(next);
400 			break;
401 		default:
402 			/* ignore for now */
403 			break;
404 		}
405 	}
406 }
407