xref: /openbsd/usr.sbin/mrouted/rsrr.c (revision 8529ddd3)
1 /*	$OpenBSD: rsrr.c,v 1.13 2015/01/16 06:40:18 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
84 rsrr_init(void)
85 {
86     int servlen;
87     struct sockaddr_un serv_addr;
88 
89     if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
90 	logit(LOG_ERR, errno, "Can't create RSRR socket");
91 
92     unlink(RSRR_SERV_PATH);
93     bzero((char *) &serv_addr, sizeof(serv_addr));
94     serv_addr.sun_family = AF_UNIX;
95     strlcpy(serv_addr.sun_path, RSRR_SERV_PATH, sizeof serv_addr.sun_path);
96     servlen = offsetof(struct sockaddr_un, sun_path) +
97 		strlen(serv_addr.sun_path);
98     serv_addr.sun_len = servlen;
99 
100     if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0)
101 	logit(LOG_ERR, errno, "Can't bind RSRR socket");
102 
103     if (register_input_handler(rsrr_socket,rsrr_read) < 0)
104 	logit(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
105 }
106 
107 /* Read a message from the RSRR socket */
108 void
109 rsrr_read(int f)
110 {
111     int rsrr_recvlen;
112     sigset_t mask, omask;
113 
114     bzero((char *) &client_addr, sizeof(client_addr));
115     rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
116 			    0, (struct sockaddr *)&client_addr, &client_length);
117     if (rsrr_recvlen < 0) {
118 	if (errno != EINTR)
119 	    logit(LOG_ERR, errno, "RSRR recvfrom");
120 	return;
121     }
122     /* Use of omask taken from main() */
123     sigemptyset(&mask);
124     sigaddset(&mask, SIGALRM);
125     sigprocmask(SIG_BLOCK, &mask, &omask);
126     rsrr_accept(rsrr_recvlen);
127     sigprocmask(SIG_SETMASK, &omask, NULL);
128 }
129 
130 /* Accept a message from the reservation protocol and take
131  * appropriate action.
132  */
133 static void
134 rsrr_accept(int recvlen)
135 {
136     struct rsrr_header *rsrr;
137     struct rsrr_rq *route_query;
138 
139     if (recvlen < RSRR_HEADER_LEN) {
140 	logit(LOG_WARNING, 0,
141 	    "Received RSRR packet of %d bytes, which is less than min size",
142 	    recvlen);
143 	return;
144     }
145 
146     rsrr = (struct rsrr_header *) rsrr_recv_buf;
147 
148     if (rsrr->version > RSRR_MAX_VERSION) {
149 	logit(LOG_WARNING, 0,
150 	    "Received RSRR packet version %d, which I don't understand",
151 	    rsrr->version);
152 	return;
153     }
154 
155     switch (rsrr->version) {
156       case 1:
157 	switch (rsrr->type) {
158 	  case RSRR_INITIAL_QUERY:
159 	    /* Send Initial Reply to client */
160 	    logit(LOG_INFO, 0, "Received Initial Query\n");
161 	    rsrr_accept_iq();
162 	    break;
163 	  case RSRR_ROUTE_QUERY:
164 	    /* Check size */
165 	    if (recvlen < RSRR_RQ_LEN) {
166 		logit(LOG_WARNING, 0,
167 		    "Received Route Query of %d bytes, which is too small",
168 		    recvlen);
169 		break;
170 	    }
171 	    /* Get the query */
172 	    route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
173 	    logit(LOG_INFO, 0,
174 		"Received Route Query for src %s grp %s notification %d",
175 		inet_fmt(route_query->source_addr.s_addr, s1),
176 		inet_fmt(route_query->dest_addr.s_addr,s2),
177 		BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
178 	    /* Send Route Reply to client */
179 	    rsrr_accept_rq(route_query,rsrr->flags,NULL);
180 	    break;
181 	  default:
182 	    logit(LOG_WARNING, 0,
183 		"Received RSRR packet type %d, which I don't handle",
184 		rsrr->type);
185 	    break;
186 	}
187 	break;
188 
189       default:
190 	logit(LOG_WARNING, 0,
191 	    "Received RSRR packet version %d, which I don't understand",
192 	    rsrr->version);
193 	break;
194     }
195 }
196 
197 /* Send an Initial Reply to the reservation protocol. */
198 static void
199 rsrr_accept_iq(void)
200 {
201     struct rsrr_header *rsrr;
202     struct rsrr_vif *vif_list;
203     struct uvif *v;
204     int vifi, sendlen;
205 
206     /* Check for space.  There should be room for plenty of vifs,
207      * but we should check anyway.
208      */
209     if (numvifs > RSRR_MAX_VIFS) {
210 	logit(LOG_WARNING, 0,
211 	    "Can't send RSRR Route Reply because %d is too many vifs %d",
212 	    numvifs);
213 	return;
214     }
215 
216     /* Set up message */
217     rsrr = (struct rsrr_header *) rsrr_send_buf;
218     rsrr->version = 1;
219     rsrr->type = RSRR_INITIAL_REPLY;
220     rsrr->flags = 0;
221     rsrr->num = numvifs;
222 
223     vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
224 
225     /* Include the vif list. */
226     for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
227 	vif_list[vifi].id = vifi;
228 	vif_list[vifi].status = 0;
229 	if (v->uv_flags & VIFF_DISABLED)
230 	    BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
231 	vif_list[vifi].threshold = v->uv_threshold;
232 	vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
233     }
234 
235     /* Get the size. */
236     sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
237 
238     /* Send it. */
239     logit(LOG_INFO, 0, "Send RSRR Initial Reply");
240     rsrr_send(sendlen);
241 }
242 
243 /* Send a Route Reply to the reservation protocol.  The Route Query
244  * contains the query to which we are responding.  The flags contain
245  * the incoming flags from the query or, for route change
246  * notification, the flags that should be set for the reply.  The
247  * kernel table entry contains the routing info to use for a route
248  * change notification.
249  */
250 static int
251 rsrr_accept_rq(struct rsrr_rq *route_query, int flags, struct gtable *gt_notify)
252 {
253     struct rsrr_header *rsrr;
254     struct rsrr_rr *route_reply;
255     struct gtable *gt,local_g;
256     struct rtentry *r;
257     int sendlen,i;
258     u_long mcastgrp;
259 
260     /* Set up message */
261     rsrr = (struct rsrr_header *) rsrr_send_buf;
262     rsrr->version = 1;
263     rsrr->type = RSRR_ROUTE_REPLY;
264     rsrr->flags = 0;
265     rsrr->num = 0;
266 
267     route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN);
268     route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr;
269     route_reply->source_addr.s_addr = route_query->source_addr.s_addr;
270     route_reply->query_id = route_query->query_id;
271 
272     /* Blank routing entry for error. */
273     route_reply->in_vif = 0;
274     route_reply->reserved = 0;
275     route_reply->out_vif_bm = 0;
276 
277     /* Get the size. */
278     sendlen = RSRR_RR_LEN;
279 
280     /* If kernel table entry is defined, then we are sending a Route Reply
281      * due to a Route Change Notification event.  Use the kernel table entry
282      * to supply the routing info.
283      */
284     if (gt_notify) {
285 	/* Set flags */
286 	rsrr->flags = flags;
287 	/* Include the routing entry. */
288 	route_reply->in_vif = gt_notify->gt_route->rt_parent;
289 	route_reply->out_vif_bm = gt_notify->gt_grpmems;
290 
291     } else if (find_src_grp(route_query->source_addr.s_addr, 0,
292 			    route_query->dest_addr.s_addr)) {
293 
294 	/* Found kernel entry. Code taken from add_table_entry() */
295 	gt = gtp ? gtp->gt_gnext : kernel_table;
296 
297 	/* Include the routing entry. */
298 	route_reply->in_vif = gt->gt_route->rt_parent;
299 	route_reply->out_vif_bm = gt->gt_grpmems;
300 
301 	/* Cache reply if using route change notification. */
302 	if BIT_TST(flags,RSRR_NOTIFICATION_BIT) {
303 	    rsrr_cache(gt,route_query);
304 	    BIT_SET(rsrr->flags,RSRR_NOTIFICATION_BIT);
305 	}
306 
307     } else {
308 	/* No kernel entry; use routing table. */
309 	r = determine_route(route_query->source_addr.s_addr);
310 
311 	if (r != NULL) {
312 	    /* We need to mimic what will happen if a data packet
313 	     * is forwarded by multicast routing -- the kernel will
314 	     * make an upcall and mrouted will install a route in the kernel.
315 	     * Our outgoing vif bitmap should reflect what that table
316 	     * will look like.  Grab code from add_table_entry().
317 	     * This is gross, but it's probably better to be accurate.
318 	     */
319 
320 	    gt = &local_g;
321 	    mcastgrp = route_query->dest_addr.s_addr;
322 
323 	    gt->gt_mcastgrp	= mcastgrp;
324 	    gt->gt_grpmems	= 0;
325 	    gt->gt_scope	= 0;
326 	    gt->gt_route        = r;
327 
328 	    /* obtain the multicast group membership list */
329 	    for (i = 0; i < numvifs; i++) {
330 		if (VIFM_ISSET(i, r->rt_children) &&
331 		    !(VIFM_ISSET(i, r->rt_leaves)))
332 		    VIFM_SET(i, gt->gt_grpmems);
333 
334 		if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
335 		    VIFM_SET(i, gt->gt_grpmems);
336 	    }
337 
338 	    GET_SCOPE(gt);
339 	    gt->gt_grpmems &= ~gt->gt_scope;
340 
341 	    /* Include the routing entry. */
342 	    route_reply->in_vif = gt->gt_route->rt_parent;
343 	    route_reply->out_vif_bm = gt->gt_grpmems;
344 
345 	} else {
346 	    /* Set error bit. */
347 	    BIT_SET(rsrr->flags,RSRR_ERROR_BIT);
348 	}
349     }
350 
351     if (gt_notify)
352 	logit(LOG_INFO, 0, "Route Change: Send RSRR Route Reply");
353 
354     else
355 	logit(LOG_INFO, 0, "Send RSRR Route Reply");
356 
357     logit(LOG_INFO, 0, "for src %s dst %s in vif %d out vif %d\n",
358 	inet_fmt(route_reply->source_addr.s_addr,s1),
359 	inet_fmt(route_reply->dest_addr.s_addr,s2),
360 	route_reply->in_vif,route_reply->out_vif_bm);
361 
362     /* Send it. */
363     return rsrr_send(sendlen);
364 }
365 
366 /* Send an RSRR message. */
367 static int
368 rsrr_send(int sendlen)
369 {
370     int error;
371 
372     /* Send it. */
373     error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
374 		   (struct sockaddr *)&client_addr, client_length);
375 
376     /* Check for errors. */
377     if (error < 0) {
378 	logit(LOG_WARNING, errno, "Failed send on RSRR socket");
379     } else if (error != sendlen) {
380 	logit(LOG_WARNING, 0,
381 	    "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
382     }
383     return error;
384 }
385 
386 /* Cache a message being sent to a client.  Currently only used for
387  * caching Route Reply messages for route change notification.
388  */
389 static void
390 rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query)
391 {
392     struct rsrr_cache *rc, **rcnp;
393     struct rsrr_header *rsrr;
394 
395     rsrr = (struct rsrr_header *) rsrr_send_buf;
396 
397     rcnp = &gt->gt_rsrr_cache;
398     while ((rc = *rcnp) != NULL) {
399 	if ((rc->route_query.source_addr.s_addr ==
400 	     route_query->source_addr.s_addr) &&
401 	    (rc->route_query.dest_addr.s_addr ==
402 	     route_query->dest_addr.s_addr) &&
403 	    (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) {
404 	    /* Cache entry already exists.
405 	     * Check if route notification bit has been cleared.
406 	     */
407 	    if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) {
408 		/* Delete cache entry. */
409 		*rcnp = rc->next;
410 		free(rc);
411 	    } else {
412 		/* Update */
413 		rc->route_query.query_id = route_query->query_id;
414 		logit(LOG_DEBUG, 0,
415 			"Update cached query id %ld from client %s\n",
416 			rc->route_query.query_id, rc->client_addr.sun_path);
417 	    }
418 	    return;
419 	}
420 	rcnp = &rc->next;
421     }
422 
423     /* Cache entry doesn't already exist.  Create one and insert at
424      * front of list.
425      */
426     rc = (struct rsrr_cache *) malloc(sizeof(struct rsrr_cache));
427     if (rc == NULL)
428 	logit(LOG_ERR, 0, "ran out of memory");
429     rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr;
430     rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr;
431     rc->route_query.query_id = route_query->query_id;
432     strlcpy(rc->client_addr.sun_path, client_addr.sun_path,
433 	sizeof rc->client_addr.sun_path);
434     rc->client_length = client_length;
435     rc->next = gt->gt_rsrr_cache;
436     gt->gt_rsrr_cache = rc;
437     logit(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
438 	   rc->route_query.query_id,rc->client_addr.sun_path);
439 }
440 
441 /* Send all the messages in the cache.  Currently this is used to send
442  * all the cached Route Reply messages for route change notification.
443  */
444 void
445 rsrr_cache_send(struct gtable *gt, int notify)
446 {
447     struct rsrr_cache *rc, **rcnp;
448     int flags = 0;
449 
450     if (notify)
451 	BIT_SET(flags,RSRR_NOTIFICATION_BIT);
452 
453     rcnp = &gt->gt_rsrr_cache;
454     while ((rc = *rcnp) != NULL) {
455 	if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
456 	    logit(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
457 		   rc->route_query.query_id,rc->client_addr.sun_path);
458 	    /* Delete cache entry. */
459 	    *rcnp = rc->next;
460 	    free(rc);
461 	} else {
462 	    rcnp = &rc->next;
463 	}
464     }
465 }
466 
467 /* Clean the cache by deleting all entries. */
468 void
469 rsrr_cache_clean(struct gtable *gt)
470 {
471     struct rsrr_cache *rc,*rc_next;
472 
473     printf("cleaning cache for group %s\n",inet_fmt(gt->gt_mcastgrp, s1));
474     rc = gt->gt_rsrr_cache;
475     while (rc) {
476 	rc_next = rc->next;
477 	free(rc);
478 	rc = rc_next;
479     }
480     gt->gt_rsrr_cache = NULL;
481 }
482 
483 void
484 rsrr_clean(void)
485 {
486     unlink(RSRR_SERV_PATH);
487 }
488 
489 #endif /* RSRR */
490