1/*	$OpenBSD: kroute.c,v 1.14 2017/07/24 11:00:01 friehm 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
42struct {
43	u_int32_t		rtseq;
44	pid_t			pid;
45	struct event		ev;
46} kr_state;
47
48struct kif_node {
49	RB_ENTRY(kif_node)	 entry;
50	struct kif		 k;
51};
52
53int	kif_compare(struct kif_node *, struct kif_node *);
54
55struct kif_node		*kif_find(int);
56int			 kif_insert(struct kif_node *);
57int			 kif_remove(struct kif_node *);
58void			 kif_clear(void);
59
60in_addr_t	prefixlen2mask(u_int8_t);
61void		get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
62void		if_change(u_short, int, struct if_data *);
63void		if_announce(void *);
64
65int		fetchifs(int);
66
67RB_HEAD(kif_tree, kif_node)		kit;
68RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
69RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
70
71int
72kif_init(void)
73{
74	RB_INIT(&kit);
75
76	if (fetchifs(0) == -1)
77		return (-1);
78
79	return (0);
80}
81
82int
83kr_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
107void
108kr_shutdown(void)
109{
110	kif_clear();
111	close(EVENT_FD((&kr_state.ev)));
112}
113
114void
115kr_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 */
128int
129kif_compare(struct kif_node *a, struct kif_node *b)
130{
131	return (b->k.ifindex - a->k.ifindex);
132}
133
134struct kif_node *
135kif_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
145struct kif *
146kif_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
157int
158kif_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
169int
170kif_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
181void
182kif_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 */
191u_int8_t
192prefixlen_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
208u_int8_t
209mask2prefixlen(in_addr_t ina)
210{
211	if (ina == 0)
212		return (0);
213	else
214		return (33 - ffs(ntohl(ina)));
215}
216
217in_addr_t
218prefixlen2mask(u_int8_t prefixlen)
219{
220	if (prefixlen == 0)
221		return (0);
222
223	return (0xffffffff << (32 - prefixlen));
224}
225
226void
227if_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
251void
252if_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) \
279	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
280
281void
282get_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));
291		} else
292			rti_info[i] = NULL;
293	}
294}
295
296int
297fetchifs(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] = PF_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.if_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
371void
372kr_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		if (errno == EAGAIN || errno == EINTR)
382			return;
383		fatal("dispatch_rtmsg: read error");
384	}
385
386	if (n == 0)
387		fatalx("routing socket closed");
388
389	lim = buf + n;
390	for (next = buf; next < lim; next += rtm->rtm_msglen) {
391		rtm = (struct rt_msghdr *)next;
392		if (lim < next + sizeof(u_short) ||
393		    lim < next + rtm->rtm_msglen)
394			fatalx("dispatch_rtmsg: partial rtm in buffer");
395		if (rtm->rtm_version != RTM_VERSION)
396			continue;
397
398		switch (rtm->rtm_type) {
399		case RTM_IFINFO:
400			memcpy(&ifm, next, sizeof(ifm));
401			if_change(ifm.ifm_index, ifm.ifm_flags,
402			    &ifm.ifm_data);
403			break;
404		case RTM_IFANNOUNCE:
405			if_announce(next);
406			break;
407		default:
408			/* ignore for now */
409			break;
410		}
411	}
412}
413