xref: /openbsd/usr.sbin/ripd/message.c (revision 28b8bcf0)
1 /*	$OpenBSD: message.c,v 1.18 2024/08/21 14:58:14 florian Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
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 #include <netinet/udp.h>
25 
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "ripd.h"
30 #include "rip.h"
31 #include "ripe.h"
32 #include "log.h"
33 
34 extern struct ripd_conf	*oeconf;
35 
36 void	 delete_entry(struct rip_route *);
37 
38 /* timers */
39 void
report_timer(int fd,short event,void * arg)40 report_timer(int fd, short event, void *arg)
41 {
42 	struct timeval	 tv;
43 
44 	ripe_imsg_compose_rde(IMSG_FULL_RESPONSE, 0, 0, NULL, 0);
45 
46 	/* restart report timer */
47 	timerclear(&tv);
48 	tv.tv_sec = KEEPALIVE + arc4random_uniform(OFFSET);
49 	evtimer_add(&oeconf->report_timer, &tv);
50 }
51 
52 int
start_report_timer(void)53 start_report_timer(void)
54 {
55 	struct timeval	 tv;
56 
57 	timerclear(&tv);
58 	tv.tv_sec = KEEPALIVE + arc4random_uniform(OFFSET);
59 	return (evtimer_add(&oeconf->report_timer, &tv));
60 }
61 
62 /* list handlers */
63 void
add_entry(struct packet_head * r_list,struct rip_route * rr)64 add_entry(struct packet_head *r_list, struct rip_route *rr)
65 {
66 	struct packet_entry	*re;
67 
68 	if (rr == NULL)
69 		fatalx("add_entry: no route report");
70 
71 	if ((re = calloc(1, sizeof(*re))) == NULL)
72 		fatal("add_entry");
73 
74 	TAILQ_INSERT_TAIL(r_list, re, entry);
75 	re->rr = rr;
76 	rr->refcount++;
77 }
78 
79 void
delete_entry(struct rip_route * rr)80 delete_entry(struct rip_route *rr)
81 {
82 	if (--rr->refcount == 0)
83 		free(rr);
84 }
85 
86 void
clear_list(struct packet_head * r_list)87 clear_list(struct packet_head *r_list)
88 {
89 	struct packet_entry	*re;
90 
91 	while ((re = TAILQ_FIRST(r_list)) != NULL) {
92 		TAILQ_REMOVE(r_list, re, entry);
93 		delete_entry(re->rr);
94 		free(re);
95 	}
96 }
97 
98 /* communications */
99 int
send_triggered_update(struct iface * iface,struct rip_route * rr)100 send_triggered_update(struct iface *iface, struct rip_route *rr)
101 {
102 	struct sockaddr_in	 dst;
103 	struct ibuf		*buf;
104 	u_int16_t		 afi, route_tag;
105 	u_int32_t		 address, netmask, nexthop, metric;
106 
107 	if (iface->passive)
108 		return (0);
109 
110 	inet_pton(AF_INET, ALL_RIP_ROUTERS, &dst.sin_addr);
111 
112 	dst.sin_port = htons(RIP_PORT);
113 	dst.sin_family = AF_INET;
114 	dst.sin_len = sizeof(struct sockaddr_in);
115 
116 	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip) -
117 	    sizeof(struct udphdr))) == NULL)
118 		fatal("send_triggered_update");
119 
120 	gen_rip_hdr(buf, COMMAND_RESPONSE);
121 
122 	afi = htons(AF_INET);
123 	route_tag = 0;
124 
125 	address = rr->address.s_addr;
126 	netmask = rr->mask.s_addr;
127 	nexthop = rr->nexthop.s_addr;
128 	metric = htonl(rr->metric);
129 
130 	ibuf_add(buf, &afi, sizeof(afi));
131 	ibuf_add(buf, &route_tag, sizeof(route_tag));
132 	ibuf_add(buf, &address, sizeof(address));
133 	ibuf_add(buf, &netmask, sizeof(netmask));
134 	ibuf_add(buf, &nexthop, sizeof(nexthop));
135 	ibuf_add(buf, &metric, sizeof(metric));
136 
137 	send_packet(iface, ibuf_data(buf), ibuf_size(buf), &dst);
138 	ibuf_free(buf);
139 
140 	return (0);
141 }
142 
143 int
send_request(struct packet_head * r_list,struct iface * i,struct nbr * nbr)144 send_request(struct packet_head *r_list, struct iface *i, struct nbr *nbr)
145 {
146 	struct ibuf		*buf;
147 	struct iface		*iface;
148 	struct packet_entry	*entry;
149 	struct sockaddr_in	 dst;
150 	u_int8_t		 nentries;
151 	u_int8_t		 single_entry = 0;
152 	u_int32_t		 address, netmask, nexthop;
153 	u_int16_t		 port, afi, route_tag;
154 	u_int32_t		 metric;
155 
156 	if (i == NULL) {
157 		/* directly to a nbr */
158 		iface = nbr->iface;
159 		dst.sin_addr = nbr->addr;
160 		port = htons(nbr->port);
161 	} else {
162 		/* multicast on interface */
163 		iface = i;
164 		inet_pton(AF_INET, ALL_RIP_ROUTERS, &dst.sin_addr);
165 		port = htons(RIP_PORT);
166 	}
167 
168 	if (iface->passive) {
169 		clear_list(r_list);
170 		return (0);
171 	}
172 
173 	dst.sin_port = port;
174 	dst.sin_family = AF_INET;
175 	dst.sin_len = sizeof(struct sockaddr_in);
176 
177 	while (!TAILQ_EMPTY(r_list)) {
178 		if ((buf = ibuf_open(iface->mtu - sizeof(struct ip) -
179 		    sizeof(struct udphdr))) == NULL)
180 			fatal("send_request");
181 
182 		gen_rip_hdr(buf, COMMAND_REQUEST);
183 
184 		route_tag = 0;
185 		nentries = 0;
186 
187 		if (TAILQ_FIRST(r_list) == TAILQ_LAST(r_list, packet_head))
188 			single_entry = 1;
189 		while (((entry = TAILQ_FIRST(r_list)) != NULL) &&
190 		    nentries < MAX_RIP_ENTRIES) {
191 			afi = htons(AF_INET);
192 
193 			address = entry->rr->address.s_addr;
194 			netmask = entry->rr->mask.s_addr;
195 			nexthop = entry->rr->nexthop.s_addr;
196 			metric = htonl(entry->rr->metric);
197 
198 			if (metric == htonl(INFINITY) && single_entry)
199 				afi = AF_UNSPEC;
200 
201 			ibuf_add(buf, &afi, sizeof(afi));
202 			ibuf_add(buf, &route_tag, sizeof(route_tag));
203 			ibuf_add(buf, &address, sizeof(address));
204 			ibuf_add(buf, &netmask, sizeof(netmask));
205 			ibuf_add(buf, &nexthop, sizeof(nexthop));
206 			ibuf_add(buf, &metric, sizeof(metric));
207 			nentries++;
208 
209 			TAILQ_REMOVE(r_list, entry, entry);
210 			delete_entry(entry->rr);
211 			free(entry);
212 		}
213 		send_packet(iface, ibuf_data(buf), ibuf_size(buf), &dst);
214 		ibuf_free(buf);
215 	}
216 
217 	return (0);
218 }
219 
220 int
send_response(struct packet_head * r_list,struct iface * i,struct nbr * nbr)221 send_response(struct packet_head *r_list, struct iface *i, struct nbr *nbr)
222 {
223 	struct ibuf		*buf;
224 	struct iface		*iface;
225 	struct packet_entry	*entry;
226 	struct sockaddr_in	 dst;
227 	u_int8_t		 nentries;
228 	u_int16_t		 port, afi, route_tag;
229 	u_int32_t		 address, netmask, nexthop;
230 	u_int32_t		 metric;
231 
232 	if (i == NULL) {
233 		/* directly to a nbr */
234 		iface = nbr->iface;
235 		dst.sin_addr = nbr->addr;
236 		port = htons(nbr->port);
237 	} else {
238 		/* multicast on interface */
239 		iface = i;
240 		inet_pton(AF_INET, ALL_RIP_ROUTERS, &dst.sin_addr);
241 		port = htons(RIP_PORT);
242 	}
243 
244 	if (iface->passive) {
245 		clear_list(r_list);
246 		return (0);
247 	}
248 
249 	dst.sin_port = port;
250 	dst.sin_family = AF_INET;
251 	dst.sin_len = sizeof(struct sockaddr_in);
252 
253 	while (!TAILQ_EMPTY(r_list)) {
254 		if ((buf = ibuf_open(iface->mtu - sizeof(struct ip) -
255 		    sizeof(struct udphdr))) == NULL)
256 			fatal("send_response");
257 
258 		gen_rip_hdr(buf, COMMAND_RESPONSE);
259 
260 		afi = htons(AF_INET);
261 		route_tag = 0;
262 		nentries = 0;
263 
264 		if (iface->auth_type != AUTH_NONE) {
265 			if (auth_gen(buf, iface) == -1) {
266 				ibuf_free(buf);
267 				return (-1);
268 			}
269 			nentries++;
270 		}
271 
272 		while ((entry = TAILQ_FIRST(r_list)) != NULL &&
273 		    nentries < MAX_RIP_ENTRIES) {
274 			address = entry->rr->address.s_addr;
275 			netmask = entry->rr->mask.s_addr;
276 			nexthop = entry->rr->nexthop.s_addr;
277 			metric = htonl(entry->rr->metric);
278 
279 			if (entry->rr->ifindex == iface->ifindex) {
280 				if (oeconf->options & OPT_SPLIT_HORIZON)
281 					goto free;
282 				else if (oeconf->options & OPT_SPLIT_POISONED)
283 					metric = htonl(INFINITY);
284 			}
285 
286 			/* If the nexthop is not reachable through the
287 			 * outgoing interface set it to INADDR_ANY */
288 			if ((nexthop & iface->mask.s_addr) !=
289 			    (iface->addr.s_addr & iface->mask.s_addr))
290 				nexthop = INADDR_ANY;
291 
292 			ibuf_add(buf, &afi, sizeof(afi));
293 			ibuf_add(buf, &route_tag, sizeof(route_tag));
294 			ibuf_add(buf, &address, sizeof(address));
295 			ibuf_add(buf, &netmask, sizeof(netmask));
296 			ibuf_add(buf, &nexthop, sizeof(nexthop));
297 			ibuf_add(buf, &metric, sizeof(metric));
298 			nentries++;
299 free:
300 			TAILQ_REMOVE(r_list, entry, entry);
301 			delete_entry(entry->rr);
302 			free(entry);
303 		}
304 
305 		if (iface->auth_type == AUTH_CRYPT)
306 			auth_add_trailer(buf, iface);
307 
308 		send_packet(iface, ibuf_data(buf), ibuf_size(buf), &dst);
309 		ibuf_free(buf);
310 	}
311 
312 	return (0);
313 }
314 
315 void
recv_request(struct iface * i,struct nbr * nbr,u_int8_t * buf,u_int16_t len)316 recv_request(struct iface *i, struct nbr *nbr, u_int8_t *buf, u_int16_t len)
317 {
318 	struct rip_entry	*e;
319 	struct rip_route	 rr;
320 	int			 l = len;
321 
322 	bzero(&rr, sizeof(rr));
323 
324 	if (len < RIP_ENTRY_LEN) {
325 		log_debug("recv_request: bad packet size, interface %s",
326 		    i->name);
327 		return;
328 	}
329 
330 	/*
331 	 * XXX is it guaranteed that bus is properly aligned.
332 	 * If not this will bomb on strict alignment archs.
333 	 * */
334 	e = (struct rip_entry *)buf;
335 
336 	if (len > RIP_ENTRY_LEN * MAX_RIP_ENTRIES) {
337 		log_debug("recv_request: packet too long\n");
338 		return;
339 	}
340 
341 	l -= RIP_ENTRY_LEN;
342 
343 	/*
344 	 * If there is exactly one entry in the request, and it has
345 	 * an address family identifier of zero and a metric of
346 	 * infinity (i.e., 16), then this is a request to send the
347 	 * entire routing table.
348 	 */
349 	if (e->AFI == 0 && e->metric == ntohl(INFINITY) && l == 0) {
350 		ripe_imsg_compose_rde(IMSG_FULL_RESPONSE, nbr->peerid,
351 		    0, NULL, 0);
352 		return;
353 	}
354 
355 	for ( ; l >= 0; l -= RIP_ENTRY_LEN) {
356 		if (e->AFI != AF_INET) {
357 			log_debug("recv_request: AFI %d not supported\n",
358 			    e->AFI);
359 			return;
360 		}
361 		rr.address.s_addr = e->address;
362 		rr.mask.s_addr = e->mask;
363 		rr.nexthop.s_addr = e->nexthop;
364 		rr.metric = e->metric;
365 		rr.ifindex = i->ifindex;
366 
367 		ripe_imsg_compose_rde(IMSG_ROUTE_REQUEST, nbr->peerid,
368 		    0, &rr, sizeof(rr));
369 
370 		e++;
371 	}
372 
373 	ripe_imsg_compose_rde(IMSG_ROUTE_REQUEST_END, nbr->peerid,
374 	    0, NULL, 0);
375 }
376 
377 void
recv_response(struct iface * i,struct nbr * nbr,u_int8_t * buf,u_int16_t len)378 recv_response(struct iface *i, struct nbr *nbr, u_int8_t *buf, u_int16_t len)
379 {
380 	struct rip_route	 r;
381 	struct rip_entry	*e;
382 	int			 l;
383 
384 	if (len < RIP_ENTRY_LEN) {
385 		log_debug("recv_response: bad packet size, interface %s",
386 		    i->name);
387 		return;
388 	}
389 
390 	/* We must double check the length, because the only entry
391 	 * can be stripped off by authentication code
392 	 */
393 	if (len < RIP_ENTRY_LEN) {
394 		/* If there are no entries, our work is finished here */
395 		return;
396 	}
397 
398 	/* XXX again */
399 	e = (struct rip_entry *)buf;
400 
401 	if (len > RIP_ENTRY_LEN * MAX_RIP_ENTRIES) {
402 		log_debug("recv_response: packet too long\n");
403 		return;
404 	}
405 
406 	l = len - sizeof(*e);
407 
408 	for ( ; l >= 0; l -= RIP_ENTRY_LEN) {
409 		if (ntohs(e->AFI) != AF_INET) {
410 			log_debug("recv_response: AFI %d not supported\n",
411 			    e->AFI);
412 			return;
413 		}
414 
415 		r.address.s_addr = e->address;
416 		r.mask.s_addr = e->mask;
417 
418 		if (e->nexthop == INADDR_ANY ||
419 		    ((i->addr.s_addr & i->mask.s_addr) !=
420 		    (e->nexthop & i->mask.s_addr)))
421 			r.nexthop.s_addr = nbr->addr.s_addr;
422 		else
423 			r.nexthop.s_addr = e->nexthop;
424 
425 		r.metric = ntohl(e->metric);
426 		r.ifindex = i->ifindex;
427 
428 		ripe_imsg_compose_rde(IMSG_ROUTE_FEED, 0, 0, &r, sizeof(r));
429 
430 		e++;
431 	}
432 }
433