xref: /openbsd/usr.sbin/dvmrpd/report.c (revision 2471dd62)
1 /*	$OpenBSD: report.c,v 1.13 2024/08/21 09:18:47 florian 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 
34 extern struct dvmrpd_conf	*deconf;
35 
36 void	 rr_list_remove(struct route_report *);
37 
38 /* DVMRP report packet handling */
39 int
send_report(struct iface * iface,struct in_addr addr,void * data,int len)40 send_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);
68 fail:
69 	log_warn("send_report");
70 	ibuf_free(buf);
71 	return (-1);
72 }
73 
74 void
recv_report(struct nbr * nbr,char * buf,u_int16_t len)75 recv_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 */
152 void
report_timer(int fd,short event,void * arg)153 report_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 
166 int
start_report_timer(void)167 start_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 
176 int
stop_report_timer(void)177 stop_report_timer(void)
178 {
179 	return (evtimer_del(&deconf->report_timer));
180 }
181 
182 /* route report list */
183 void
rr_list_add(struct rr_head * rr_list,struct route_report * rr)184 rr_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 
199 void
rr_list_remove(struct route_report * rr)200 rr_list_remove(struct route_report *rr)
201 {
202 	if (--rr->refcount == 0)
203 		free(rr);
204 }
205 
206 void
rr_list_clr(struct rr_head * rr_list)207 rr_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 
218 void
rr_list_send(struct rr_head * rr_list,struct iface * xiface,struct nbr * nbr)219 rr_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_pton(AF_INET, 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