xref: /openbsd-src/usr.sbin/dvmrpd/kroute.c (revision 018a085a0a42a5ac0134c8e54842d4b413e0ddbc)
1 /*	$OpenBSD: kroute.c,v 1.15 2025/01/02 06:35:57 anton 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/types.h>
21 #include <sys/socket.h>
22 #include <sys/sysctl.h>
23 #include <sys/tree.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <net/if.h>
27 #include <net/if_dl.h>
28 #include <net/if_types.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 | SOCK_CLOEXEC | SOCK_NONBLOCK,
88 	    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 	memset(&s, 0, 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.if_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 		if (kif != NULL)
273 			kif_remove(kif);
274 		break;
275 	}
276 }
277 
278 /* rtsock */
279 #define ROUNDUP(a) \
280 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
281 
282 void
283 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
284 {
285 	int	i;
286 
287 	for (i = 0; i < RTAX_MAX; i++) {
288 		if (addrs & (1 << i)) {
289 			rti_info[i] = sa;
290 			sa = (struct sockaddr *)((char *)(sa) +
291 			    ROUNDUP(sa->sa_len));
292 		} else
293 			rti_info[i] = NULL;
294 	}
295 }
296 
297 int
298 fetchifs(int ifindex)
299 {
300 	size_t			 len;
301 	int			 mib[6];
302 	char			*buf, *next, *lim;
303 	struct if_msghdr	 ifm;
304 	struct kif_node		*kif;
305 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
306 	struct sockaddr_dl	*sdl;
307 
308 	mib[0] = CTL_NET;
309 	mib[1] = PF_ROUTE;
310 	mib[2] = 0;
311 	mib[3] = AF_INET;
312 	mib[4] = NET_RT_IFLIST;
313 	mib[5] = ifindex;
314 
315 	if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
316 		log_warn("sysctl");
317 		return (-1);
318 	}
319 	if ((buf = malloc(len)) == NULL) {
320 		log_warn("fetchif");
321 		return (-1);
322 	}
323 	if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
324 		log_warn("sysctl");
325 		free(buf);
326 		return (-1);
327 	}
328 
329 	lim = buf + len;
330 	for (next = buf; next < lim; next += ifm.ifm_msglen) {
331 		memcpy(&ifm, next, sizeof(ifm));
332 		sa = (struct sockaddr *)(next + sizeof(ifm));
333 		get_rtaddrs(ifm.ifm_addrs, sa, rti_info);
334 
335 		if (ifm.ifm_version != RTM_VERSION)
336 			continue;
337 		if (ifm.ifm_type != RTM_IFINFO)
338 			continue;
339 
340 		if ((kif = calloc(1, sizeof(struct kif_node))) == NULL) {
341 			log_warn("fetchifs");
342 			free(buf);
343 			return (-1);
344 		}
345 
346 		kif->k.ifindex = ifm.ifm_index;
347 		kif->k.flags = ifm.ifm_flags;
348 		kif->k.link_state = ifm.ifm_data.ifi_link_state;
349 		kif->k.if_type = ifm.ifm_data.ifi_type;
350 		kif->k.baudrate = ifm.ifm_data.ifi_baudrate;
351 		kif->k.mtu = ifm.ifm_data.ifi_mtu;
352 		kif->k.nh_reachable = (kif->k.flags & IFF_UP) &&
353 		    LINK_STATE_IS_UP(ifm.ifm_data.ifi_link_state);
354 		if ((sa = rti_info[RTAX_IFP]) != NULL)
355 			if (sa->sa_family == AF_LINK) {
356 				sdl = (struct sockaddr_dl *)sa;
357 				if (sdl->sdl_nlen >= sizeof(kif->k.ifname))
358 					memcpy(kif->k.ifname, sdl->sdl_data,
359 					    sizeof(kif->k.ifname) - 1);
360 				else if (sdl->sdl_nlen > 0)
361 					memcpy(kif->k.ifname, sdl->sdl_data,
362 					    sdl->sdl_nlen);
363 				/* string already terminated via calloc() */
364 			}
365 
366 		kif_insert(kif);
367 	}
368 	free(buf);
369 	return (0);
370 }
371 
372 void
373 kr_dispatch_msg(int fd, short event, void *bula)
374 {
375 	char			 buf[RT_BUF_SIZE];
376 	ssize_t			 n;
377 	char			*next, *lim;
378 	struct rt_msghdr	*rtm;
379 	struct if_msghdr	 ifm;
380 
381 	if ((n = read(fd, &buf, sizeof(buf))) == -1) {
382 		if (errno == EAGAIN || errno == EINTR)
383 			return;
384 		fatal("dispatch_rtmsg: read error");
385 	}
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 (lim < next + sizeof(u_short) ||
394 		    lim < next + rtm->rtm_msglen)
395 			fatalx("dispatch_rtmsg: partial rtm in buffer");
396 		if (rtm->rtm_version != RTM_VERSION)
397 			continue;
398 
399 		switch (rtm->rtm_type) {
400 		case RTM_IFINFO:
401 			memcpy(&ifm, next, sizeof(ifm));
402 			if_change(ifm.ifm_index, ifm.ifm_flags,
403 			    &ifm.ifm_data);
404 			break;
405 		case RTM_IFANNOUNCE:
406 			if_announce(next);
407 			break;
408 		default:
409 			/* ignore for now */
410 			break;
411 		}
412 	}
413 }
414