xref: /openbsd/usr.sbin/dvmrpd/report.c (revision 73471bf0)
1 /*	$OpenBSD: report.c,v 1.11 2015/12/07 18:59:31 mmcc 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
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 	struct dvmrp_hdr	*dvmrp_hdr;
45 	int			 ret = 0;
46 
47 	log_debug("send_report: interface %s addr %s",
48 	    iface->name, inet_ntoa(addr));
49 
50 	if (iface->passive)
51 		return (0);
52 
53 	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
54 		fatal("send_report");
55 
56 	/* DVMRP header */
57 	if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT))
58 		goto fail;
59 
60 	ibuf_add(buf, data, len);
61 
62 	dst.sin_family = AF_INET;
63 	dst.sin_len = sizeof(struct sockaddr_in);
64 	dst.sin_addr.s_addr = addr.s_addr;
65 
66 	/* update chksum */
67 	dvmrp_hdr = ibuf_seek(buf, 0, sizeof(*dvmrp_hdr));
68 	dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);
69 
70 	ret = send_packet(iface, buf->buf, buf->wpos, &dst);
71 	ibuf_free(buf);
72 	return (ret);
73 fail:
74 	log_warn("send_report");
75 	ibuf_free(buf);
76 	return (-1);
77 }
78 
79 void
80 recv_report(struct nbr *nbr, char *buf, u_int16_t len)
81 {
82 	struct route_report	 rr;
83 	u_int32_t		 netid, netmask;
84 	u_int8_t		 metric, netid_len, prefixlen;
85 
86 	log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id));
87 
88 	if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) {
89 		log_warnx("recv_report: neighbor %s not in state %s",
90 		    inet_ntoa(nbr->id), "2-WAY");
91 		return;
92 	}
93 
94 	/* parse route report */
95 	do {
96 		/*
97 		 * get netmask
98 		 *
99 		 * The netmask in a DVMRP report is only represented by 3 bytes,
100 		 * to cope with that we read 4 bytes and shift 8 bits.
101 		 * The most significant part of the mask is always 255.
102 		 */
103 
104 		/* read four bytes */
105 		memcpy(&netmask, buf, sizeof(netmask));
106 		/* ditch one byte, since we only need three */
107 		netmask = ntohl(netmask) >> 8;
108 		netmask = htonl(netmask);
109 
110 		/* set the highest byte to 255 */
111 		netmask |= htonl(0xff000000);
112 		buf += 3;
113 		len -= 3;
114 
115 		prefixlen = mask2prefixlen(netmask);
116 		netid_len = PREFIX_SIZE(prefixlen);
117 
118 		do {
119 			/*
120 			 * get netid
121 			 *
122 			 * The length of the netid is depending on the above
123 			 * netmask.
124 			 * Read 4 bytes and use the netmask from above to
125 			 * determine the netid.
126 			 */
127 			memcpy(&netid, buf, sizeof(netid));
128 			netid &= netmask;
129 
130 			buf += netid_len;
131 			len -= netid_len;
132 
133 			/* get metric */
134 			memcpy(&metric, buf, sizeof(metric));
135 			buf += sizeof(metric);
136 			len -= sizeof(metric);
137 
138 			rr.net.s_addr = netid;
139 			rr.mask.s_addr = netmask;
140 			rr.nexthop = nbr->id;
141 			rr.metric = (metric & METRIC_MASK);
142 
143 			/* ifindex */
144 			rr.ifindex = nbr->iface->ifindex;
145 
146 			/* send route report to RDE */
147 			dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid,
148 			    0, &rr, sizeof(rr));
149 
150 		} while (!(metric & LAST_MASK) && (len > 0));
151 	} while (len > 0);
152 
153 	return;
154 }
155 
156 /* timers */
157 void
158 report_timer(int fd, short event, void *arg)
159 {
160 	struct timeval		 tv;
161 
162 	/* request full route report */
163 	dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0);
164 
165 	/* restart report timer */
166 	timerclear(&tv);
167 	tv.tv_sec = ROUTE_REPORT_INTERVAL;
168 	evtimer_add(&deconf->report_timer, &tv);
169 }
170 
171 int
172 start_report_timer(void)
173 {
174 	struct timeval	tv;
175 
176 	timerclear(&tv);
177 	tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL;	/* XXX safe?? */
178 	return (evtimer_add(&deconf->report_timer, &tv));
179 }
180 
181 int
182 stop_report_timer(void)
183 {
184 	return (evtimer_del(&deconf->report_timer));
185 }
186 
187 /* route report list */
188 void
189 rr_list_add(struct rr_head *rr_list, struct route_report *rr)
190 {
191 	struct rr_entry		*le;
192 
193 	if (rr == NULL)
194 		fatalx("rr_list_add: no route report");
195 
196 	if ((le = calloc(1, sizeof(*le))) == NULL)
197 		fatal("rr_list_add");
198 
199 	TAILQ_INSERT_TAIL(rr_list, le, entry);
200 	le->re = rr;
201 	rr->refcount++;
202 }
203 
204 void
205 rr_list_remove(struct route_report *rr)
206 {
207 	if (--rr->refcount == 0)
208 		free(rr);
209 }
210 
211 void
212 rr_list_clr(struct rr_head *rr_list)
213 {
214 	struct rr_entry		*le;
215 
216 	while ((le = TAILQ_FIRST(rr_list)) != NULL) {
217 		TAILQ_REMOVE(rr_list, le, entry);
218 		rr_list_remove(le->re);
219 		free(le);
220 	}
221 }
222 
223 void
224 rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr)
225 {
226 	struct rr_entry		*le, *le2;
227 	struct ibuf		*buf;
228 	struct iface		*iface;
229 	struct in_addr		 addr;
230 	u_int32_t		 netid, netmask;
231 	u_int8_t		 metric, netid_len, prefixlen;
232 
233 	/* set destination */
234 	if (xiface == NULL) {
235 		/* directly to a nbr */
236 		iface = nbr->iface;
237 		addr = nbr->addr;
238 	} else {
239 		/* multicast on interface */
240 		iface = xiface;
241 		inet_aton(AllDVMRPRouters, &addr);
242 	}
243 
244 	while (!TAILQ_EMPTY(rr_list)) {
245 		if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
246 			fatal("rr_list_send");
247 
248 		prefixlen = 0;
249 		while (((le = TAILQ_FIRST(rr_list)) != NULL) &&
250 		    (buf->wpos < 1000)) {
251 			/* netmask */
252 			netmask = le->re->mask.s_addr;
253 			if (prefixlen != mask2prefixlen(netmask)) {
254 				prefixlen = mask2prefixlen(netmask);
255 				netmask = ntohl(netmask) << 8;
256 				netmask = htonl(netmask);
257 				ibuf_add(buf, &netmask, 3);
258 			}
259 			netid_len = PREFIX_SIZE(prefixlen);
260 
261 			/* netid */
262 			netid = le->re->net.s_addr;
263 			ibuf_add(buf, &netid, netid_len);
264 
265 			/* metric */
266 			if (iface->ifindex == le->re->ifindex)
267 				/* poison reverse */
268 				metric = le->re->metric + INFINITY_METRIC;
269 			else
270 				metric = le->re->metric;
271 
272 			/*
273 			 * determine if we need to flag last entry with current
274 			 * netmask.
275 			 */
276 			le2 = TAILQ_NEXT(le, entry);
277 			if (le2 != NULL) {
278 				if (mask2prefixlen(le2->re->mask.s_addr) !=
279 				    prefixlen)
280 					metric = metric | LAST_MASK;
281 			} else {
282 				metric = metric | LAST_MASK;
283 			}
284 
285 			ibuf_add(buf, &metric, sizeof(metric));
286 
287 			TAILQ_REMOVE(rr_list, le, entry);
288 			rr_list_remove(le->re);
289 			free(le);
290 		}
291 		send_report(iface, addr, buf->buf, buf->wpos);
292 		ibuf_free(buf);
293 	}
294 }
295