xref: /openbsd/usr.sbin/dvmrpd/report.c (revision 35f7e862)
1*35f7e862Sclaudio /*	$OpenBSD: report.c,v 1.12 2023/06/26 10:08:56 claudio Exp $ */
2978e5cffSnorby 
3978e5cffSnorby /*
4978e5cffSnorby  * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org>
5978e5cffSnorby  *
6978e5cffSnorby  * Permission to use, copy, modify, and distribute this software for any
7978e5cffSnorby  * purpose with or without fee is hereby granted, provided that the above
8978e5cffSnorby  * copyright notice and this permission notice appear in all copies.
9978e5cffSnorby  *
10978e5cffSnorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11978e5cffSnorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12978e5cffSnorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13978e5cffSnorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14978e5cffSnorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15978e5cffSnorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16978e5cffSnorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17978e5cffSnorby  */
18978e5cffSnorby 
19978e5cffSnorby #include <sys/types.h>
20978e5cffSnorby #include <sys/socket.h>
21978e5cffSnorby #include <netinet/in.h>
22978e5cffSnorby #include <netinet/ip.h>
23978e5cffSnorby #include <arpa/inet.h>
24978e5cffSnorby 
25978e5cffSnorby #include <stdlib.h>
26b53a5054Smmcc #include <string.h>
27978e5cffSnorby 
28978e5cffSnorby #include "igmp.h"
29978e5cffSnorby #include "dvmrpd.h"
30978e5cffSnorby #include "dvmrp.h"
31978e5cffSnorby #include "dvmrpe.h"
32978e5cffSnorby #include "log.h"
33978e5cffSnorby 
34978e5cffSnorby extern struct dvmrpd_conf	*deconf;
35978e5cffSnorby 
3650b67c3fSmichele void	 rr_list_remove(struct route_report *);
37978e5cffSnorby 
38978e5cffSnorby /* DVMRP report packet handling */
39978e5cffSnorby int
send_report(struct iface * iface,struct in_addr addr,void * data,int len)40978e5cffSnorby send_report(struct iface *iface, struct in_addr addr, void *data, int len)
41978e5cffSnorby {
42978e5cffSnorby 	struct sockaddr_in	 dst;
43e39620e5Snicm 	struct ibuf		*buf;
44978e5cffSnorby 	int			 ret = 0;
45978e5cffSnorby 
46978e5cffSnorby 	log_debug("send_report: interface %s addr %s",
47978e5cffSnorby 	    iface->name, inet_ntoa(addr));
48978e5cffSnorby 
49978e5cffSnorby 	if (iface->passive)
50978e5cffSnorby 		return (0);
51978e5cffSnorby 
52e39620e5Snicm 	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
53978e5cffSnorby 		fatal("send_report");
54978e5cffSnorby 
55978e5cffSnorby 	/* DVMRP header */
56978e5cffSnorby 	if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT))
57978e5cffSnorby 		goto fail;
58978e5cffSnorby 
59e39620e5Snicm 	ibuf_add(buf, data, len);
60978e5cffSnorby 
61978e5cffSnorby 	dst.sin_family = AF_INET;
62978e5cffSnorby 	dst.sin_len = sizeof(struct sockaddr_in);
63978e5cffSnorby 	dst.sin_addr.s_addr = addr.s_addr;
64978e5cffSnorby 
65*35f7e862Sclaudio 	ret = send_packet(iface, buf, &dst);
66e39620e5Snicm 	ibuf_free(buf);
67978e5cffSnorby 	return (ret);
68978e5cffSnorby fail:
69978e5cffSnorby 	log_warn("send_report");
70e39620e5Snicm 	ibuf_free(buf);
71978e5cffSnorby 	return (-1);
72978e5cffSnorby }
73978e5cffSnorby 
74978e5cffSnorby void
recv_report(struct nbr * nbr,char * buf,u_int16_t len)75978e5cffSnorby recv_report(struct nbr *nbr, char *buf, u_int16_t len)
76978e5cffSnorby {
77978e5cffSnorby 	struct route_report	 rr;
78978e5cffSnorby 	u_int32_t		 netid, netmask;
79978e5cffSnorby 	u_int8_t		 metric, netid_len, prefixlen;
80978e5cffSnorby 
81978e5cffSnorby 	log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id));
82978e5cffSnorby 
83978e5cffSnorby 	if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) {
84978e5cffSnorby 		log_warnx("recv_report: neighbor %s not in state %s",
85978e5cffSnorby 		    inet_ntoa(nbr->id), "2-WAY");
86978e5cffSnorby 		return;
87978e5cffSnorby 	}
88978e5cffSnorby 
89978e5cffSnorby 	/* parse route report */
90978e5cffSnorby 	do {
91978e5cffSnorby 		/*
92978e5cffSnorby 		 * get netmask
93978e5cffSnorby 		 *
94978e5cffSnorby 		 * The netmask in a DVMRP report is only represented by 3 bytes,
95978e5cffSnorby 		 * to cope with that we read 4 bytes and shift 8 bits.
96978e5cffSnorby 		 * The most significant part of the mask is always 255.
97978e5cffSnorby 		 */
98978e5cffSnorby 
99978e5cffSnorby 		/* read four bytes */
100978e5cffSnorby 		memcpy(&netmask, buf, sizeof(netmask));
101978e5cffSnorby 		/* ditch one byte, since we only need three */
102acfb6403Snorby 		netmask = ntohl(netmask) >> 8;
103acfb6403Snorby 		netmask = htonl(netmask);
104acfb6403Snorby 
105978e5cffSnorby 		/* set the highest byte to 255 */
1068b293cb2Smichele 		netmask |= htonl(0xff000000);
107978e5cffSnorby 		buf += 3;
108978e5cffSnorby 		len -= 3;
109978e5cffSnorby 
110978e5cffSnorby 		prefixlen = mask2prefixlen(netmask);
11113d89169Snorby 		netid_len = PREFIX_SIZE(prefixlen);
112978e5cffSnorby 
113978e5cffSnorby 		do {
114978e5cffSnorby 			/*
115978e5cffSnorby 			 * get netid
116978e5cffSnorby 			 *
117978e5cffSnorby 			 * The length of the netid is depending on the above
118978e5cffSnorby 			 * netmask.
119978e5cffSnorby 			 * Read 4 bytes and use the netmask from above to
120978e5cffSnorby 			 * determine the netid.
121978e5cffSnorby 			 */
122978e5cffSnorby 			memcpy(&netid, buf, sizeof(netid));
1238b293cb2Smichele 			netid &= netmask;
1248b293cb2Smichele 
125978e5cffSnorby 			buf += netid_len;
126978e5cffSnorby 			len -= netid_len;
127978e5cffSnorby 
128978e5cffSnorby 			/* get metric */
129978e5cffSnorby 			memcpy(&metric, buf, sizeof(metric));
130978e5cffSnorby 			buf += sizeof(metric);
131978e5cffSnorby 			len -= sizeof(metric);
132978e5cffSnorby 
133978e5cffSnorby 			rr.net.s_addr = netid;
134978e5cffSnorby 			rr.mask.s_addr = netmask;
135978e5cffSnorby 			rr.nexthop = nbr->id;
136a2ed222dSmichele 			rr.metric = (metric & METRIC_MASK);
137978e5cffSnorby 
138978e5cffSnorby 			/* ifindex */
139978e5cffSnorby 			rr.ifindex = nbr->iface->ifindex;
140978e5cffSnorby 
141978e5cffSnorby 			/* send route report to RDE */
142978e5cffSnorby 			dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid,
143978e5cffSnorby 			    0, &rr, sizeof(rr));
144978e5cffSnorby 
145978e5cffSnorby 		} while (!(metric & LAST_MASK) && (len > 0));
146978e5cffSnorby 	} while (len > 0);
147978e5cffSnorby 
148978e5cffSnorby 	return;
149978e5cffSnorby }
150978e5cffSnorby 
151978e5cffSnorby /* timers */
152978e5cffSnorby void
report_timer(int fd,short event,void * arg)153978e5cffSnorby report_timer(int fd, short event, void *arg)
154978e5cffSnorby {
155978e5cffSnorby 	struct timeval		 tv;
156978e5cffSnorby 
157978e5cffSnorby 	/* request full route report */
158978e5cffSnorby 	dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0);
159978e5cffSnorby 
160978e5cffSnorby 	/* restart report timer */
161978e5cffSnorby 	timerclear(&tv);
162978e5cffSnorby 	tv.tv_sec = ROUTE_REPORT_INTERVAL;
163978e5cffSnorby 	evtimer_add(&deconf->report_timer, &tv);
164978e5cffSnorby }
165978e5cffSnorby 
166978e5cffSnorby int
start_report_timer(void)167978e5cffSnorby start_report_timer(void)
168978e5cffSnorby {
169978e5cffSnorby 	struct timeval	tv;
170978e5cffSnorby 
171978e5cffSnorby 	timerclear(&tv);
172978e5cffSnorby 	tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL;	/* XXX safe?? */
173978e5cffSnorby 	return (evtimer_add(&deconf->report_timer, &tv));
174978e5cffSnorby }
175978e5cffSnorby 
176978e5cffSnorby int
stop_report_timer(void)177978e5cffSnorby stop_report_timer(void)
178978e5cffSnorby {
179978e5cffSnorby 	return (evtimer_del(&deconf->report_timer));
180978e5cffSnorby }
181978e5cffSnorby 
182978e5cffSnorby /* route report list */
183978e5cffSnorby void
rr_list_add(struct rr_head * rr_list,struct route_report * rr)184978e5cffSnorby rr_list_add(struct rr_head *rr_list, struct route_report *rr)
185978e5cffSnorby {
186978e5cffSnorby 	struct rr_entry		*le;
187978e5cffSnorby 
188978e5cffSnorby 	if (rr == NULL)
189978e5cffSnorby 		fatalx("rr_list_add: no route report");
190978e5cffSnorby 
191978e5cffSnorby 	if ((le = calloc(1, sizeof(*le))) == NULL)
192978e5cffSnorby 		fatal("rr_list_add");
193978e5cffSnorby 
194978e5cffSnorby 	TAILQ_INSERT_TAIL(rr_list, le, entry);
195978e5cffSnorby 	le->re = rr;
19650b67c3fSmichele 	rr->refcount++;
19750b67c3fSmichele }
19850b67c3fSmichele 
19950b67c3fSmichele void
rr_list_remove(struct route_report * rr)20050b67c3fSmichele rr_list_remove(struct route_report *rr)
20150b67c3fSmichele {
20250b67c3fSmichele 	if (--rr->refcount == 0)
20350b67c3fSmichele 		free(rr);
204978e5cffSnorby }
205978e5cffSnorby 
206978e5cffSnorby void
rr_list_clr(struct rr_head * rr_list)207978e5cffSnorby rr_list_clr(struct rr_head *rr_list)
208978e5cffSnorby {
209978e5cffSnorby 	struct rr_entry		*le;
210978e5cffSnorby 
211978e5cffSnorby 	while ((le = TAILQ_FIRST(rr_list)) != NULL) {
212978e5cffSnorby 		TAILQ_REMOVE(rr_list, le, entry);
21350b67c3fSmichele 		rr_list_remove(le->re);
214978e5cffSnorby 		free(le);
215978e5cffSnorby 	}
216978e5cffSnorby }
217978e5cffSnorby 
218978e5cffSnorby void
rr_list_send(struct rr_head * rr_list,struct iface * xiface,struct nbr * nbr)219978e5cffSnorby rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr)
220978e5cffSnorby {
221978e5cffSnorby 	struct rr_entry		*le, *le2;
222e39620e5Snicm 	struct ibuf		*buf;
223978e5cffSnorby 	struct iface		*iface;
224978e5cffSnorby 	struct in_addr		 addr;
225978e5cffSnorby 	u_int32_t		 netid, netmask;
226978e5cffSnorby 	u_int8_t		 metric, netid_len, prefixlen;
227978e5cffSnorby 
228978e5cffSnorby 	/* set destination */
229978e5cffSnorby 	if (xiface == NULL) {
230978e5cffSnorby 		/* directly to a nbr */
231978e5cffSnorby 		iface = nbr->iface;
232978e5cffSnorby 		addr = nbr->addr;
233978e5cffSnorby 	} else {
234978e5cffSnorby 		/* multicast on interface */
235978e5cffSnorby 		iface = xiface;
236978e5cffSnorby 		inet_aton(AllDVMRPRouters, &addr);
237978e5cffSnorby 	}
238978e5cffSnorby 
239978e5cffSnorby 	while (!TAILQ_EMPTY(rr_list)) {
240e39620e5Snicm 		if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
241978e5cffSnorby 			fatal("rr_list_send");
242978e5cffSnorby 
243978e5cffSnorby 		prefixlen = 0;
244978e5cffSnorby 		while (((le = TAILQ_FIRST(rr_list)) != NULL) &&
245*35f7e862Sclaudio 		    (ibuf_size(buf) < 1000)) {
246978e5cffSnorby 			/* netmask */
247978e5cffSnorby 			netmask = le->re->mask.s_addr;
248978e5cffSnorby 			if (prefixlen != mask2prefixlen(netmask)) {
249978e5cffSnorby 				prefixlen = mask2prefixlen(netmask);
250acfb6403Snorby 				netmask = ntohl(netmask) << 8;
251acfb6403Snorby 				netmask = htonl(netmask);
252e39620e5Snicm 				ibuf_add(buf, &netmask, 3);
253978e5cffSnorby 			}
25413d89169Snorby 			netid_len = PREFIX_SIZE(prefixlen);
255978e5cffSnorby 
256978e5cffSnorby 			/* netid */
257978e5cffSnorby 			netid = le->re->net.s_addr;
258e39620e5Snicm 			ibuf_add(buf, &netid, netid_len);
259978e5cffSnorby 
260978e5cffSnorby 			/* metric */
261978e5cffSnorby 			if (iface->ifindex == le->re->ifindex)
262978e5cffSnorby 				/* poison reverse */
263978e5cffSnorby 				metric = le->re->metric + INFINITY_METRIC;
264978e5cffSnorby 			else
265978e5cffSnorby 				metric = le->re->metric;
266978e5cffSnorby 
267978e5cffSnorby 			/*
268978e5cffSnorby 			 * determine if we need to flag last entry with current
269978e5cffSnorby 			 * netmask.
270978e5cffSnorby 			 */
271978e5cffSnorby 			le2 = TAILQ_NEXT(le, entry);
272978e5cffSnorby 			if (le2 != NULL) {
273978e5cffSnorby 				if (mask2prefixlen(le2->re->mask.s_addr) !=
274978e5cffSnorby 				    prefixlen)
275978e5cffSnorby 					metric = metric | LAST_MASK;
276978e5cffSnorby 			} else {
277978e5cffSnorby 				metric = metric | LAST_MASK;
278978e5cffSnorby 			}
279978e5cffSnorby 
280e39620e5Snicm 			ibuf_add(buf, &metric, sizeof(metric));
281978e5cffSnorby 
282978e5cffSnorby 			TAILQ_REMOVE(rr_list, le, entry);
28350b67c3fSmichele 			rr_list_remove(le->re);
284978e5cffSnorby 			free(le);
285978e5cffSnorby 		}
286*35f7e862Sclaudio 		send_report(iface, addr, ibuf_data(buf), ibuf_size(buf));
287e39620e5Snicm 		ibuf_free(buf);
288978e5cffSnorby 	}
289978e5cffSnorby }
290