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