xref: /openbsd/usr.sbin/mrouted/rsrr.c (revision df69c215)
1 /*	$OpenBSD: rsrr.c,v 1.16 2019/06/28 13:32:48 deraadt Exp $	*/
2 /*	$NetBSD: rsrr.c,v 1.3 1995/12/10 10:07:14 mycroft Exp $	*/
3 
4 /*
5  * Copyright (c) 1993, 1998-2001.
6  * The University of Southern California/Information Sciences Institute.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /* RSRR code written by Daniel Zappala, USC Information Sciences Institute,
35  * April 1995.
36  */
37 
38 /* May 1995 -- Added support for Route Change Notification */
39 
40 #ifdef RSRR
41 
42 #include "defs.h"
43 #include <stddef.h>
44 
45 /* Taken from prune.c */
46 /*
47  * checks for scoped multicast addresses
48  */
49 #define GET_SCOPE(gt) { \
50 	int _i; \
51 	if (((gt)->gt_mcastgrp & 0xff000000) == 0xef000000) \
52 	    for (_i = 0; _i < numvifs; _i++) \
53 		if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
54 		    VIFM_SET(_i, (gt)->gt_scope); \
55 	}
56 
57 /*
58  * Exported variables.
59  */
60 int rsrr_socket;			/* interface to reservation protocol */
61 
62 /*
63  * Global RSRR variables.
64  */
65 char rsrr_recv_buf[RSRR_MAX_LEN];	/* RSRR receive buffer */
66 char rsrr_send_buf[RSRR_MAX_LEN];	/* RSRR send buffer */
67 
68 struct sockaddr_un client_addr;
69 int client_length = sizeof(client_addr);
70 
71 
72 /*
73  * Procedure definitions needed internally.
74  */
75 static void	rsrr_accept(int recvlen);
76 static void	rsrr_accept_iq(void);
77 static int	rsrr_accept_rq(struct rsrr_rq *route_query, int flags,
78 		    struct gtable *gt_notify);
79 static int	rsrr_send(int sendlen);
80 static void	rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query);
81 
82 /* Initialize RSRR socket */
83 void
rsrr_init(void)84 rsrr_init(void)
85 {
86     struct sockaddr_un serv_addr;
87 
88     if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
89 	logit(LOG_ERR, errno, "Can't create RSRR socket");
90 
91     unlink(RSRR_SERV_PATH);
92     bzero((char *) &serv_addr, sizeof(serv_addr));
93     serv_addr.sun_family = AF_UNIX;
94     strlcpy(serv_addr.sun_path, RSRR_SERV_PATH, sizeof serv_addr.sun_path);
95 
96     if (bind(rsrr_socket, (struct sockaddr *)&serv_addr, sizeof serv_addr) == -1)
97 	logit(LOG_ERR, errno, "Can't bind RSRR socket");
98 
99     if (register_input_handler(rsrr_socket,rsrr_read) < 0)
100 	logit(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
101 }
102 
103 /* Read a message from the RSRR socket */
104 void
rsrr_read(int f)105 rsrr_read(int f)
106 {
107     int rsrr_recvlen;
108     sigset_t mask, omask;
109 
110     bzero((char *) &client_addr, sizeof(client_addr));
111     rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
112 			    0, (struct sockaddr *)&client_addr, &client_length);
113     if (rsrr_recvlen == -1) {
114 	if (errno != EINTR)
115 	    logit(LOG_ERR, errno, "RSRR recvfrom");
116 	return;
117     }
118     /* Use of omask taken from main() */
119     sigemptyset(&mask);
120     sigaddset(&mask, SIGALRM);
121     sigprocmask(SIG_BLOCK, &mask, &omask);
122     rsrr_accept(rsrr_recvlen);
123     sigprocmask(SIG_SETMASK, &omask, NULL);
124 }
125 
126 /* Accept a message from the reservation protocol and take
127  * appropriate action.
128  */
129 static void
rsrr_accept(int recvlen)130 rsrr_accept(int recvlen)
131 {
132     struct rsrr_header *rsrr;
133     struct rsrr_rq *route_query;
134 
135     if (recvlen < RSRR_HEADER_LEN) {
136 	logit(LOG_WARNING, 0,
137 	    "Received RSRR packet of %d bytes, which is less than min size",
138 	    recvlen);
139 	return;
140     }
141 
142     rsrr = (struct rsrr_header *) rsrr_recv_buf;
143 
144     if (rsrr->version > RSRR_MAX_VERSION) {
145 	logit(LOG_WARNING, 0,
146 	    "Received RSRR packet version %d, which I don't understand",
147 	    rsrr->version);
148 	return;
149     }
150 
151     switch (rsrr->version) {
152       case 1:
153 	switch (rsrr->type) {
154 	  case RSRR_INITIAL_QUERY:
155 	    /* Send Initial Reply to client */
156 	    logit(LOG_INFO, 0, "Received Initial Query\n");
157 	    rsrr_accept_iq();
158 	    break;
159 	  case RSRR_ROUTE_QUERY:
160 	    /* Check size */
161 	    if (recvlen < RSRR_RQ_LEN) {
162 		logit(LOG_WARNING, 0,
163 		    "Received Route Query of %d bytes, which is too small",
164 		    recvlen);
165 		break;
166 	    }
167 	    /* Get the query */
168 	    route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
169 	    logit(LOG_INFO, 0,
170 		"Received Route Query for src %s grp %s notification %d",
171 		inet_fmt(route_query->source_addr.s_addr, s1),
172 		inet_fmt(route_query->dest_addr.s_addr,s2),
173 		BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
174 	    /* Send Route Reply to client */
175 	    rsrr_accept_rq(route_query,rsrr->flags,NULL);
176 	    break;
177 	  default:
178 	    logit(LOG_WARNING, 0,
179 		"Received RSRR packet type %d, which I don't handle",
180 		rsrr->type);
181 	    break;
182 	}
183 	break;
184 
185       default:
186 	logit(LOG_WARNING, 0,
187 	    "Received RSRR packet version %d, which I don't understand",
188 	    rsrr->version);
189 	break;
190     }
191 }
192 
193 /* Send an Initial Reply to the reservation protocol. */
194 static void
rsrr_accept_iq(void)195 rsrr_accept_iq(void)
196 {
197     struct rsrr_header *rsrr;
198     struct rsrr_vif *vif_list;
199     struct uvif *v;
200     int vifi, sendlen;
201 
202     /* Check for space.  There should be room for plenty of vifs,
203      * but we should check anyway.
204      */
205     if (numvifs > RSRR_MAX_VIFS) {
206 	logit(LOG_WARNING, 0,
207 	    "Can't send RSRR Route Reply because %d is too many vifs %d",
208 	    numvifs);
209 	return;
210     }
211 
212     /* Set up message */
213     rsrr = (struct rsrr_header *) rsrr_send_buf;
214     rsrr->version = 1;
215     rsrr->type = RSRR_INITIAL_REPLY;
216     rsrr->flags = 0;
217     rsrr->num = numvifs;
218 
219     vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
220 
221     /* Include the vif list. */
222     for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
223 	vif_list[vifi].id = vifi;
224 	vif_list[vifi].status = 0;
225 	if (v->uv_flags & VIFF_DISABLED)
226 	    BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
227 	vif_list[vifi].threshold = v->uv_threshold;
228 	vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
229     }
230 
231     /* Get the size. */
232     sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
233 
234     /* Send it. */
235     logit(LOG_INFO, 0, "Send RSRR Initial Reply");
236     rsrr_send(sendlen);
237 }
238 
239 /* Send a Route Reply to the reservation protocol.  The Route Query
240  * contains the query to which we are responding.  The flags contain
241  * the incoming flags from the query or, for route change
242  * notification, the flags that should be set for the reply.  The
243  * kernel table entry contains the routing info to use for a route
244  * change notification.
245  */
246 static int
rsrr_accept_rq(struct rsrr_rq * route_query,int flags,struct gtable * gt_notify)247 rsrr_accept_rq(struct rsrr_rq *route_query, int flags, struct gtable *gt_notify)
248 {
249     struct rsrr_header *rsrr;
250     struct rsrr_rr *route_reply;
251     struct gtable *gt,local_g;
252     struct rtentry *r;
253     int sendlen,i;
254     u_long mcastgrp;
255 
256     /* Set up message */
257     rsrr = (struct rsrr_header *) rsrr_send_buf;
258     rsrr->version = 1;
259     rsrr->type = RSRR_ROUTE_REPLY;
260     rsrr->flags = 0;
261     rsrr->num = 0;
262 
263     route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN);
264     route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr;
265     route_reply->source_addr.s_addr = route_query->source_addr.s_addr;
266     route_reply->query_id = route_query->query_id;
267 
268     /* Blank routing entry for error. */
269     route_reply->in_vif = 0;
270     route_reply->reserved = 0;
271     route_reply->out_vif_bm = 0;
272 
273     /* Get the size. */
274     sendlen = RSRR_RR_LEN;
275 
276     /* If kernel table entry is defined, then we are sending a Route Reply
277      * due to a Route Change Notification event.  Use the kernel table entry
278      * to supply the routing info.
279      */
280     if (gt_notify) {
281 	/* Set flags */
282 	rsrr->flags = flags;
283 	/* Include the routing entry. */
284 	route_reply->in_vif = gt_notify->gt_route->rt_parent;
285 	route_reply->out_vif_bm = gt_notify->gt_grpmems;
286 
287     } else if (find_src_grp(route_query->source_addr.s_addr, 0,
288 			    route_query->dest_addr.s_addr)) {
289 
290 	/* Found kernel entry. Code taken from add_table_entry() */
291 	gt = gtp ? gtp->gt_gnext : kernel_table;
292 
293 	/* Include the routing entry. */
294 	route_reply->in_vif = gt->gt_route->rt_parent;
295 	route_reply->out_vif_bm = gt->gt_grpmems;
296 
297 	/* Cache reply if using route change notification. */
298 	if BIT_TST(flags,RSRR_NOTIFICATION_BIT) {
299 	    rsrr_cache(gt,route_query);
300 	    BIT_SET(rsrr->flags,RSRR_NOTIFICATION_BIT);
301 	}
302 
303     } else {
304 	/* No kernel entry; use routing table. */
305 	r = determine_route(route_query->source_addr.s_addr);
306 
307 	if (r != NULL) {
308 	    /* We need to mimic what will happen if a data packet
309 	     * is forwarded by multicast routing -- the kernel will
310 	     * make an upcall and mrouted will install a route in the kernel.
311 	     * Our outgoing vif bitmap should reflect what that table
312 	     * will look like.  Grab code from add_table_entry().
313 	     * This is gross, but it's probably better to be accurate.
314 	     */
315 
316 	    gt = &local_g;
317 	    mcastgrp = route_query->dest_addr.s_addr;
318 
319 	    gt->gt_mcastgrp	= mcastgrp;
320 	    gt->gt_grpmems	= 0;
321 	    gt->gt_scope	= 0;
322 	    gt->gt_route        = r;
323 
324 	    /* obtain the multicast group membership list */
325 	    for (i = 0; i < numvifs; i++) {
326 		if (VIFM_ISSET(i, r->rt_children) &&
327 		    !(VIFM_ISSET(i, r->rt_leaves)))
328 		    VIFM_SET(i, gt->gt_grpmems);
329 
330 		if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
331 		    VIFM_SET(i, gt->gt_grpmems);
332 	    }
333 
334 	    GET_SCOPE(gt);
335 	    gt->gt_grpmems &= ~gt->gt_scope;
336 
337 	    /* Include the routing entry. */
338 	    route_reply->in_vif = gt->gt_route->rt_parent;
339 	    route_reply->out_vif_bm = gt->gt_grpmems;
340 
341 	} else {
342 	    /* Set error bit. */
343 	    BIT_SET(rsrr->flags,RSRR_ERROR_BIT);
344 	}
345     }
346 
347     if (gt_notify)
348 	logit(LOG_INFO, 0, "Route Change: Send RSRR Route Reply");
349 
350     else
351 	logit(LOG_INFO, 0, "Send RSRR Route Reply");
352 
353     logit(LOG_INFO, 0, "for src %s dst %s in vif %d out vif %d\n",
354 	inet_fmt(route_reply->source_addr.s_addr,s1),
355 	inet_fmt(route_reply->dest_addr.s_addr,s2),
356 	route_reply->in_vif,route_reply->out_vif_bm);
357 
358     /* Send it. */
359     return rsrr_send(sendlen);
360 }
361 
362 /* Send an RSRR message. */
363 static int
rsrr_send(int sendlen)364 rsrr_send(int sendlen)
365 {
366     int error;
367 
368     /* Send it. */
369     error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
370 		   (struct sockaddr *)&client_addr, client_length);
371 
372     /* Check for errors. */
373     if (error == -1) {
374 	logit(LOG_WARNING, errno, "Failed send on RSRR socket");
375     } else if (error != sendlen) {
376 	logit(LOG_WARNING, 0,
377 	    "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
378     }
379     return error;
380 }
381 
382 /* Cache a message being sent to a client.  Currently only used for
383  * caching Route Reply messages for route change notification.
384  */
385 static void
rsrr_cache(struct gtable * gt,struct rsrr_rq * route_query)386 rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query)
387 {
388     struct rsrr_cache *rc, **rcnp;
389     struct rsrr_header *rsrr;
390 
391     rsrr = (struct rsrr_header *) rsrr_send_buf;
392 
393     rcnp = &gt->gt_rsrr_cache;
394     while ((rc = *rcnp) != NULL) {
395 	if ((rc->route_query.source_addr.s_addr ==
396 	     route_query->source_addr.s_addr) &&
397 	    (rc->route_query.dest_addr.s_addr ==
398 	     route_query->dest_addr.s_addr) &&
399 	    (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) {
400 	    /* Cache entry already exists.
401 	     * Check if route notification bit has been cleared.
402 	     */
403 	    if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) {
404 		/* Delete cache entry. */
405 		*rcnp = rc->next;
406 		free(rc);
407 	    } else {
408 		/* Update */
409 		rc->route_query.query_id = route_query->query_id;
410 		logit(LOG_DEBUG, 0,
411 			"Update cached query id %ld from client %s\n",
412 			rc->route_query.query_id, rc->client_addr.sun_path);
413 	    }
414 	    return;
415 	}
416 	rcnp = &rc->next;
417     }
418 
419     /* Cache entry doesn't already exist.  Create one and insert at
420      * front of list.
421      */
422     rc = malloc(sizeof(struct rsrr_cache));
423     if (rc == NULL)
424 	logit(LOG_ERR, 0, "ran out of memory");
425     rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr;
426     rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr;
427     rc->route_query.query_id = route_query->query_id;
428     strlcpy(rc->client_addr.sun_path, client_addr.sun_path,
429 	sizeof rc->client_addr.sun_path);
430     rc->client_length = client_length;
431     rc->next = gt->gt_rsrr_cache;
432     gt->gt_rsrr_cache = rc;
433     logit(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
434 	   rc->route_query.query_id,rc->client_addr.sun_path);
435 }
436 
437 /* Send all the messages in the cache.  Currently this is used to send
438  * all the cached Route Reply messages for route change notification.
439  */
440 void
rsrr_cache_send(struct gtable * gt,int notify)441 rsrr_cache_send(struct gtable *gt, int notify)
442 {
443     struct rsrr_cache *rc, **rcnp;
444     int flags = 0;
445 
446     if (notify)
447 	BIT_SET(flags,RSRR_NOTIFICATION_BIT);
448 
449     rcnp = &gt->gt_rsrr_cache;
450     while ((rc = *rcnp) != NULL) {
451 	if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
452 	    logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
453 		   rc->route_query.query_id,rc->client_addr.sun_path);
454 	    /* Delete cache entry. */
455 	    *rcnp = rc->next;
456 	    free(rc);
457 	} else {
458 	    rcnp = &rc->next;
459 	}
460     }
461 }
462 
463 /* Clean the cache by deleting all entries. */
464 void
rsrr_cache_clean(struct gtable * gt)465 rsrr_cache_clean(struct gtable *gt)
466 {
467     struct rsrr_cache *rc,*rc_next;
468 
469     printf("cleaning cache for group %s\n",inet_fmt(gt->gt_mcastgrp, s1));
470     rc = gt->gt_rsrr_cache;
471     while (rc) {
472 	rc_next = rc->next;
473 	free(rc);
474 	rc = rc_next;
475     }
476     gt->gt_rsrr_cache = NULL;
477 }
478 
479 void
rsrr_clean(void)480 rsrr_clean(void)
481 {
482     unlink(RSRR_SERV_PATH);
483 }
484 
485 #endif /* RSRR */
486