1/*	$OpenBSD: report.c,v 1.12 2023/06/26 10:08:56 claudio Exp $ */
2
3/*
4 * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <netinet/in.h>
22#include <netinet/ip.h>
23#include <arpa/inet.h>
24
25#include <stdlib.h>
26#include <string.h>
27
28#include "igmp.h"
29#include "dvmrpd.h"
30#include "dvmrp.h"
31#include "dvmrpe.h"
32#include "log.h"
33
34extern struct dvmrpd_conf	*deconf;
35
36void	 rr_list_remove(struct route_report *);
37
38/* DVMRP report packet handling */
39int
40send_report(struct iface *iface, struct in_addr addr, void *data, int len)
41{
42	struct sockaddr_in	 dst;
43	struct ibuf		*buf;
44	int			 ret = 0;
45
46	log_debug("send_report: interface %s addr %s",
47	    iface->name, inet_ntoa(addr));
48
49	if (iface->passive)
50		return (0);
51
52	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
53		fatal("send_report");
54
55	/* DVMRP header */
56	if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT))
57		goto fail;
58
59	ibuf_add(buf, data, len);
60
61	dst.sin_family = AF_INET;
62	dst.sin_len = sizeof(struct sockaddr_in);
63	dst.sin_addr.s_addr = addr.s_addr;
64
65	ret = send_packet(iface, buf, &dst);
66	ibuf_free(buf);
67	return (ret);
68fail:
69	log_warn("send_report");
70	ibuf_free(buf);
71	return (-1);
72}
73
74void
75recv_report(struct nbr *nbr, char *buf, u_int16_t len)
76{
77	struct route_report	 rr;
78	u_int32_t		 netid, netmask;
79	u_int8_t		 metric, netid_len, prefixlen;
80
81	log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id));
82
83	if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) {
84		log_warnx("recv_report: neighbor %s not in state %s",
85		    inet_ntoa(nbr->id), "2-WAY");
86		return;
87	}
88
89	/* parse route report */
90	do {
91		/*
92		 * get netmask
93		 *
94		 * The netmask in a DVMRP report is only represented by 3 bytes,
95		 * to cope with that we read 4 bytes and shift 8 bits.
96		 * The most significant part of the mask is always 255.
97		 */
98
99		/* read four bytes */
100		memcpy(&netmask, buf, sizeof(netmask));
101		/* ditch one byte, since we only need three */
102		netmask = ntohl(netmask) >> 8;
103		netmask = htonl(netmask);
104
105		/* set the highest byte to 255 */
106		netmask |= htonl(0xff000000);
107		buf += 3;
108		len -= 3;
109
110		prefixlen = mask2prefixlen(netmask);
111		netid_len = PREFIX_SIZE(prefixlen);
112
113		do {
114			/*
115			 * get netid
116			 *
117			 * The length of the netid is depending on the above
118			 * netmask.
119			 * Read 4 bytes and use the netmask from above to
120			 * determine the netid.
121			 */
122			memcpy(&netid, buf, sizeof(netid));
123			netid &= netmask;
124
125			buf += netid_len;
126			len -= netid_len;
127
128			/* get metric */
129			memcpy(&metric, buf, sizeof(metric));
130			buf += sizeof(metric);
131			len -= sizeof(metric);
132
133			rr.net.s_addr = netid;
134			rr.mask.s_addr = netmask;
135			rr.nexthop = nbr->id;
136			rr.metric = (metric & METRIC_MASK);
137
138			/* ifindex */
139			rr.ifindex = nbr->iface->ifindex;
140
141			/* send route report to RDE */
142			dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid,
143			    0, &rr, sizeof(rr));
144
145		} while (!(metric & LAST_MASK) && (len > 0));
146	} while (len > 0);
147
148	return;
149}
150
151/* timers */
152void
153report_timer(int fd, short event, void *arg)
154{
155	struct timeval		 tv;
156
157	/* request full route report */
158	dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0);
159
160	/* restart report timer */
161	timerclear(&tv);
162	tv.tv_sec = ROUTE_REPORT_INTERVAL;
163	evtimer_add(&deconf->report_timer, &tv);
164}
165
166int
167start_report_timer(void)
168{
169	struct timeval	tv;
170
171	timerclear(&tv);
172	tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL;	/* XXX safe?? */
173	return (evtimer_add(&deconf->report_timer, &tv));
174}
175
176int
177stop_report_timer(void)
178{
179	return (evtimer_del(&deconf->report_timer));
180}
181
182/* route report list */
183void
184rr_list_add(struct rr_head *rr_list, struct route_report *rr)
185{
186	struct rr_entry		*le;
187
188	if (rr == NULL)
189		fatalx("rr_list_add: no route report");
190
191	if ((le = calloc(1, sizeof(*le))) == NULL)
192		fatal("rr_list_add");
193
194	TAILQ_INSERT_TAIL(rr_list, le, entry);
195	le->re = rr;
196	rr->refcount++;
197}
198
199void
200rr_list_remove(struct route_report *rr)
201{
202	if (--rr->refcount == 0)
203		free(rr);
204}
205
206void
207rr_list_clr(struct rr_head *rr_list)
208{
209	struct rr_entry		*le;
210
211	while ((le = TAILQ_FIRST(rr_list)) != NULL) {
212		TAILQ_REMOVE(rr_list, le, entry);
213		rr_list_remove(le->re);
214		free(le);
215	}
216}
217
218void
219rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr)
220{
221	struct rr_entry		*le, *le2;
222	struct ibuf		*buf;
223	struct iface		*iface;
224	struct in_addr		 addr;
225	u_int32_t		 netid, netmask;
226	u_int8_t		 metric, netid_len, prefixlen;
227
228	/* set destination */
229	if (xiface == NULL) {
230		/* directly to a nbr */
231		iface = nbr->iface;
232		addr = nbr->addr;
233	} else {
234		/* multicast on interface */
235		iface = xiface;
236		inet_aton(AllDVMRPRouters, &addr);
237	}
238
239	while (!TAILQ_EMPTY(rr_list)) {
240		if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
241			fatal("rr_list_send");
242
243		prefixlen = 0;
244		while (((le = TAILQ_FIRST(rr_list)) != NULL) &&
245		    (ibuf_size(buf) < 1000)) {
246			/* netmask */
247			netmask = le->re->mask.s_addr;
248			if (prefixlen != mask2prefixlen(netmask)) {
249				prefixlen = mask2prefixlen(netmask);
250				netmask = ntohl(netmask) << 8;
251				netmask = htonl(netmask);
252				ibuf_add(buf, &netmask, 3);
253			}
254			netid_len = PREFIX_SIZE(prefixlen);
255
256			/* netid */
257			netid = le->re->net.s_addr;
258			ibuf_add(buf, &netid, netid_len);
259
260			/* metric */
261			if (iface->ifindex == le->re->ifindex)
262				/* poison reverse */
263				metric = le->re->metric + INFINITY_METRIC;
264			else
265				metric = le->re->metric;
266
267			/*
268			 * determine if we need to flag last entry with current
269			 * netmask.
270			 */
271			le2 = TAILQ_NEXT(le, entry);
272			if (le2 != NULL) {
273				if (mask2prefixlen(le2->re->mask.s_addr) !=
274				    prefixlen)
275					metric = metric | LAST_MASK;
276			} else {
277				metric = metric | LAST_MASK;
278			}
279
280			ibuf_add(buf, &metric, sizeof(metric));
281
282			TAILQ_REMOVE(rr_list, le, entry);
283			rr_list_remove(le->re);
284			free(le);
285		}
286		send_report(iface, addr, ibuf_data(buf), ibuf_size(buf));
287		ibuf_free(buf);
288	}
289}
290