Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / mrouted / rsrr.c
1 /*
2  * Copyright (c) 1993 by the University of Southern California
3  * All rights reserved.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation in source and binary forms for non-commercial purposes
7  * and without fee is hereby granted, provided that the above copyright
8  * notice appear in all copies and that both the copyright notice and
9  * this permission notice appear in supporting documentation. and that
10  * any documentation, advertising materials, and other materials related
11  * to such distribution and use acknowledge that the software was
12  * developed by the University of Southern California, Information
13  * Sciences Institute.  The name of the University may not be used to
14  * endorse or promote products derived from this software without
15  * specific prior written permission.
16  *
17  * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
18  * the suitability of this software for any purpose.  THIS SOFTWARE IS
19  * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  *
23  * Other copyrights might apply to parts of this software and are so
24  * noted when applicable.
25  */
26
27 /* RSRR code written by Daniel Zappala, USC Information Sciences Institute,
28  * April 1995.
29  */
30
31 /* May 1995 -- Added support for Route Change Notification */
32
33 #ifndef lint
34 static const char rcsid[] =
35   "$FreeBSD: src/usr.sbin/mrouted/rsrr.c,v 1.8.2.1 2001/05/12 09:48:20 kris Exp $";
36 #endif /* not lint */
37
38 #ifdef RSRR
39
40 #include "defs.h"
41 #include <sys/param.h>
42 #ifdef HAVE_SA_LEN
43 #include <stddef.h>     /* for offsetof */
44 #endif
45
46 /*
47  * Exported variables.
48  */
49 int rsrr_socket;                        /* interface to reservation protocol */
50
51 /* 
52  * Global RSRR variables.
53  */
54 char rsrr_recv_buf[RSRR_MAX_LEN];       /* RSRR receive buffer */
55 char rsrr_send_buf[RSRR_MAX_LEN];       /* RSRR send buffer */
56
57 struct sockaddr_un client_addr;
58 int client_length = sizeof(client_addr);
59
60
61 /*
62  * Procedure definitions needed internally.
63  */
64 static void     rsrr_accept __P((int recvlen));
65 static void     rsrr_accept_iq __P((void));
66 static int      rsrr_accept_rq __P((struct rsrr_rq *route_query, int flags,
67                                         struct gtable *gt_notify));
68 static void     rsrr_read __P((int, fd_set *));
69 static int      rsrr_send __P((int sendlen));
70 static void     rsrr_cache __P((struct gtable *gt,
71                                         struct rsrr_rq *route_query));
72
73 /* Initialize RSRR socket */
74 void
75 rsrr_init()
76 {
77     int servlen;
78     struct sockaddr_un serv_addr;
79
80     if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
81         log(LOG_ERR, errno, "Can't create RSRR socket");
82
83     unlink(RSRR_SERV_PATH);
84     bzero((char *) &serv_addr, sizeof(serv_addr));
85     serv_addr.sun_family = AF_UNIX;
86     strcpy(serv_addr.sun_path, RSRR_SERV_PATH);
87 #ifdef HAVE_SA_LEN
88     servlen = offsetof(struct sockaddr_un, sun_path) +
89                 strlen(serv_addr.sun_path);
90     serv_addr.sun_len = servlen;
91 #else
92     servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
93 #endif
94  
95     if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0)
96         log(LOG_ERR, errno, "Can't bind RSRR socket");
97
98     if (register_input_handler(rsrr_socket, rsrr_read) < 0)
99         log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
100 }
101
102 /* Read a message from the RSRR socket */
103 static void
104 rsrr_read(f, rfd)
105         int f;
106         fd_set *rfd;
107 {
108     register int rsrr_recvlen;
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 < 0) {     
114         if (errno != EINTR)
115             log(LOG_ERR, errno, "RSRR recvfrom");
116         return;
117     }
118     rsrr_accept(rsrr_recvlen);
119 }
120
121 /* Accept a message from the reservation protocol and take
122  * appropriate action.
123  */
124 static void
125 rsrr_accept(recvlen)
126     int recvlen;
127 {
128     struct rsrr_header *rsrr;
129     struct rsrr_rq *route_query;
130     
131     if (recvlen < RSRR_HEADER_LEN) {
132         log(LOG_WARNING, 0,
133             "Received RSRR packet of %d bytes, which is less than min size",
134             recvlen);
135         return;
136     }
137     
138     rsrr = (struct rsrr_header *) rsrr_recv_buf;
139     
140     if (rsrr->version > RSRR_MAX_VERSION) {
141         log(LOG_WARNING, 0,
142             "Received RSRR packet version %d, which I don't understand",
143             rsrr->version);
144         return;
145     }
146     
147     switch (rsrr->version) {
148       case 1:
149         switch (rsrr->type) {
150           case RSRR_INITIAL_QUERY:
151             /* Send Initial Reply to client */
152             IF_DEBUG(DEBUG_RSRR)
153             log(LOG_DEBUG, 0, "Received Initial Query\n");
154             rsrr_accept_iq();
155             break;
156           case RSRR_ROUTE_QUERY:
157             /* Check size */
158             if (recvlen < RSRR_RQ_LEN) {
159                 log(LOG_WARNING, 0,
160                     "Received Route Query of %d bytes, which is too small",
161                     recvlen);
162                 break;
163             }
164             /* Get the query */
165             route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
166             IF_DEBUG(DEBUG_RSRR)
167             log(LOG_DEBUG, 0,
168                 "Received Route Query for src %s grp %s notification %d",
169                 inet_fmt(route_query->source_addr.s_addr, s1),
170                 inet_fmt(route_query->dest_addr.s_addr,s2),
171                 BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
172             /* Send Route Reply to client */
173             rsrr_accept_rq(route_query,rsrr->flags,NULL);
174             break;
175           default:
176             log(LOG_WARNING, 0,
177                 "Received RSRR packet type %d, which I don't handle",
178                 rsrr->type);
179             break;
180         }
181         break;
182         
183       default:
184         log(LOG_WARNING, 0,
185             "Received RSRR packet version %d, which I don't understand",
186             rsrr->version);
187         break;
188     }
189 }
190
191 /* Send an Initial Reply to the reservation protocol. */
192 static void
193 rsrr_accept_iq()
194 {
195     struct rsrr_header *rsrr;
196     struct rsrr_vif *vif_list;
197     struct uvif *v;
198     int vifi, sendlen;
199     
200     /* Check for space.  There should be room for plenty of vifs,
201      * but we should check anyway.
202      */
203     if (numvifs > RSRR_MAX_VIFS) {
204         log(LOG_WARNING, 0,
205             "Can't send RSRR Route Reply because %d is too many vifs",
206             numvifs);
207         return;
208     }
209     
210     /* Set up message */
211     rsrr = (struct rsrr_header *) rsrr_send_buf;
212     rsrr->version = 1;
213     rsrr->type = RSRR_INITIAL_REPLY;
214     rsrr->flags = 0;
215     rsrr->num = numvifs;
216     
217     vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
218     
219     /* Include the vif list. */
220     for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
221         vif_list[vifi].id = vifi;
222         vif_list[vifi].status = 0;
223         if (v->uv_flags & VIFF_DISABLED)
224             BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
225         vif_list[vifi].threshold = v->uv_threshold;
226         vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
227     }
228     
229     /* Get the size. */
230     sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
231     
232     /* Send it. */
233     IF_DEBUG(DEBUG_RSRR)
234     log(LOG_DEBUG, 0, "Send RSRR Initial Reply");
235     rsrr_send(sendlen);
236 }
237
238 /* Send a Route Reply to the reservation protocol.  The Route Query
239  * contains the query to which we are responding.  The flags contain
240  * the incoming flags from the query or, for route change
241  * notification, the flags that should be set for the reply.  The
242  * kernel table entry contains the routing info to use for a route
243  * change notification.
244  */
245 static int
246 rsrr_accept_rq(route_query,flags,gt_notify)
247     struct rsrr_rq *route_query;
248     int flags;
249     struct gtable *gt_notify;
250 {
251     struct rsrr_header *rsrr;
252     struct rsrr_rr *route_reply;
253     struct gtable *gt,local_g;
254     struct rtentry *r;
255     int sendlen;
256     u_long mcastgrp;
257     
258     /* Set up message */
259     rsrr = (struct rsrr_header *) rsrr_send_buf;
260     rsrr->version = 1;
261     rsrr->type = RSRR_ROUTE_REPLY;
262     rsrr->flags = 0;
263     rsrr->num = 0;
264     
265     route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN);
266     route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr;
267     route_reply->source_addr.s_addr = route_query->source_addr.s_addr;
268     route_reply->query_id = route_query->query_id;
269     
270     /* Blank routing entry for error. */
271     route_reply->in_vif = 0;
272     route_reply->reserved = 0;
273     route_reply->out_vif_bm = 0;
274     
275     /* Get the size. */
276     sendlen = RSRR_RR_LEN;
277
278     /* If kernel table entry is defined, then we are sending a Route Reply
279      * due to a Route Change Notification event.  Use the kernel table entry
280      * to supply the routing info.
281      */
282     if (gt_notify) {
283         /* Set flags */
284         rsrr->flags = flags;
285         /* Include the routing entry. */
286         route_reply->in_vif = gt_notify->gt_route->rt_parent;
287         if (BIT_TST(flags,RSRR_NOTIFICATION_BIT))
288             route_reply->out_vif_bm = gt_notify->gt_grpmems;
289         else
290             route_reply->out_vif_bm = 0;
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             determine_forwvifs(gt);
330             
331             /* Include the routing entry. */
332             route_reply->in_vif = gt->gt_route->rt_parent;
333             route_reply->out_vif_bm = gt->gt_grpmems;
334
335         } else {
336             /* Set error bit. */
337             BIT_SET(rsrr->flags,RSRR_ERROR_BIT);
338         }
339     }
340     
341     IF_DEBUG(DEBUG_RSRR)
342     log(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n",
343         gt_notify ? "Route Change: " : "",
344         inet_fmt(route_reply->source_addr.s_addr,s1),
345         inet_fmt(route_reply->dest_addr.s_addr,s2),
346         route_reply->in_vif,route_reply->out_vif_bm);
347     
348     /* Send it. */
349     return rsrr_send(sendlen);
350 }
351
352 /* Send an RSRR message. */
353 static int
354 rsrr_send(sendlen)
355     int sendlen;
356 {
357     int error;
358     
359     /* Send it. */
360     error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
361                    (struct sockaddr *)&client_addr, client_length);
362     
363     /* Check for errors. */
364     if (error < 0) {
365         log(LOG_WARNING, errno, "Failed send on RSRR socket");
366     } else if (error != sendlen) {
367         log(LOG_WARNING, 0,
368             "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
369     }
370     return error;
371 }
372
373 /* Cache a message being sent to a client.  Currently only used for
374  * caching Route Reply messages for route change notification.
375  */
376 static void
377 rsrr_cache(gt,route_query)
378     struct gtable *gt;
379     struct rsrr_rq *route_query;
380 {
381     struct rsrr_cache *rc, **rcnp;
382     struct rsrr_header *rsrr;
383
384     rsrr = (struct rsrr_header *) rsrr_send_buf;
385
386     rcnp = &gt->gt_rsrr_cache;
387     while ((rc = *rcnp) != NULL) {
388         if ((rc->route_query.source_addr.s_addr == 
389              route_query->source_addr.s_addr) &&
390             (rc->route_query.dest_addr.s_addr == 
391              route_query->dest_addr.s_addr) &&
392             (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) {
393             /* Cache entry already exists.
394              * Check if route notification bit has been cleared.
395              */
396             if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) {
397                 /* Delete cache entry. */
398                 *rcnp = rc->next;
399                 free(rc);
400             } else {
401                 /* Update */
402                 rc->route_query.query_id = route_query->query_id;
403                 IF_DEBUG(DEBUG_RSRR)
404                 log(LOG_DEBUG, 0,
405                         "Update cached query id %ld from client %s\n",
406                         rc->route_query.query_id, rc->client_addr.sun_path);
407             }
408             return;
409         }
410         rcnp = &rc->next;
411     }
412
413     /* Cache entry doesn't already exist.  Create one and insert at
414      * front of list.
415      */
416     rc = (struct rsrr_cache *) malloc(sizeof(struct rsrr_cache));
417     if (rc == NULL)
418         log(LOG_ERR, 0, "ran out of memory");
419     rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr;
420     rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr;
421     rc->route_query.query_id = route_query->query_id;
422     strcpy(rc->client_addr.sun_path, client_addr.sun_path);
423     rc->client_length = client_length;
424     rc->next = gt->gt_rsrr_cache;
425     gt->gt_rsrr_cache = rc;
426     IF_DEBUG(DEBUG_RSRR)
427     log(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
428            rc->route_query.query_id,rc->client_addr.sun_path);
429 }
430
431 /* Send all the messages in the cache.  Currently this is used to send
432  * all the cached Route Reply messages for route change notification.
433  */
434 void
435 rsrr_cache_send(gt,notify)
436     struct gtable *gt;
437     int notify;
438 {
439     struct rsrr_cache *rc, **rcnp;
440     int flags = 0;
441
442     if (notify)
443         BIT_SET(flags,RSRR_NOTIFICATION_BIT);
444
445     rcnp = &gt->gt_rsrr_cache;
446     while ((rc = *rcnp) != NULL) {
447         if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
448             IF_DEBUG(DEBUG_RSRR)
449             log(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
450                    rc->route_query.query_id,rc->client_addr.sun_path);
451             /* Delete cache entry. */
452             *rcnp = rc->next;
453             free(rc);
454         } else {
455             rcnp = &rc->next;
456         }
457     }
458 }
459
460 /* Clean the cache by deleting all entries. */
461 void
462 rsrr_cache_clean(gt)
463     struct gtable *gt;
464 {
465     struct rsrr_cache *rc,*rc_next;
466
467     IF_DEBUG(DEBUG_RSRR)
468     log(LOG_DEBUG, 0, "cleaning cache for group %s\n",
469                         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
480 rsrr_clean()
481 {
482     unlink(RSRR_SERV_PATH);
483 }
484
485 #endif /* RSRR */