/* * Copyright (c) 1993 by the University of Southern California * All rights reserved. * * Permission to use, copy, modify, and distribute this software and its * documentation in source and binary forms for non-commercial purposes * and without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both the copyright notice and * this permission notice appear in supporting documentation. and that * any documentation, advertising materials, and other materials related * to such distribution and use acknowledge that the software was * developed by the University of Southern California, Information * Sciences Institute. The name of the University may not be used to * endorse or promote products derived from this software without * specific prior written permission. * * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about * the suitability of this software for any purpose. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Other copyrights might apply to parts of this software and are so * noted when applicable. * * $FreeBSD: src/usr.sbin/mrouted/rsrr.c,v 1.8.2.1 2001/05/12 09:48:20 kris Exp $ * $DragonFly: src/usr.sbin/mrouted/rsrr.c,v 1.4 2004/03/15 18:10:28 dillon Exp $ */ /* RSRR code written by Daniel Zappala, USC Information Sciences Institute, * April 1995. */ /* May 1995 -- Added support for Route Change Notification */ #ifdef RSRR #include "defs.h" #include #ifdef HAVE_SA_LEN #include /* for offsetof */ #endif /* * Exported variables. */ int rsrr_socket; /* interface to reservation protocol */ /* * Global RSRR variables. */ char rsrr_recv_buf[RSRR_MAX_LEN]; /* RSRR receive buffer */ char rsrr_send_buf[RSRR_MAX_LEN]; /* RSRR send buffer */ struct sockaddr_un client_addr; int client_length = sizeof(client_addr); /* * Procedure definitions needed internally. */ static void rsrr_accept(int recvlen); static void rsrr_accept_iq(void); static int rsrr_accept_rq(struct rsrr_rq *route_query, int flags, struct gtable *gt_notify); static void rsrr_read(int, fd_set *); static int rsrr_send(int sendlen); static void rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query); /* Initialize RSRR socket */ void rsrr_init(void) { int servlen; struct sockaddr_un serv_addr; if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) log(LOG_ERR, errno, "Can't create RSRR socket"); unlink(RSRR_SERV_PATH); bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sun_family = AF_UNIX; strcpy(serv_addr.sun_path, RSRR_SERV_PATH); #ifdef HAVE_SA_LEN servlen = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path); serv_addr.sun_len = servlen; #else servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path); #endif if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0) log(LOG_ERR, errno, "Can't bind RSRR socket"); if (register_input_handler(rsrr_socket, rsrr_read) < 0) log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler"); } /* Read a message from the RSRR socket */ static void rsrr_read(int f, fd_set *rfd) { int rsrr_recvlen; bzero((char *) &client_addr, sizeof(client_addr)); rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf), 0, (struct sockaddr *)&client_addr, &client_length); if (rsrr_recvlen < 0) { if (errno != EINTR) log(LOG_ERR, errno, "RSRR recvfrom"); return; } rsrr_accept(rsrr_recvlen); } /* Accept a message from the reservation protocol and take * appropriate action. */ static void rsrr_accept(int recvlen) { struct rsrr_header *rsrr; struct rsrr_rq *route_query; if (recvlen < RSRR_HEADER_LEN) { log(LOG_WARNING, 0, "Received RSRR packet of %d bytes, which is less than min size", recvlen); return; } rsrr = (struct rsrr_header *) rsrr_recv_buf; if (rsrr->version > RSRR_MAX_VERSION) { log(LOG_WARNING, 0, "Received RSRR packet version %d, which I don't understand", rsrr->version); return; } switch (rsrr->version) { case 1: switch (rsrr->type) { case RSRR_INITIAL_QUERY: /* Send Initial Reply to client */ IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Received Initial Query\n"); rsrr_accept_iq(); break; case RSRR_ROUTE_QUERY: /* Check size */ if (recvlen < RSRR_RQ_LEN) { log(LOG_WARNING, 0, "Received Route Query of %d bytes, which is too small", recvlen); break; } /* Get the query */ route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN); IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Received Route Query for src %s grp %s notification %d", inet_fmt(route_query->source_addr.s_addr, s1), inet_fmt(route_query->dest_addr.s_addr,s2), BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)); /* Send Route Reply to client */ rsrr_accept_rq(route_query,rsrr->flags,NULL); break; default: log(LOG_WARNING, 0, "Received RSRR packet type %d, which I don't handle", rsrr->type); break; } break; default: log(LOG_WARNING, 0, "Received RSRR packet version %d, which I don't understand", rsrr->version); break; } } /* Send an Initial Reply to the reservation protocol. */ static void rsrr_accept_iq(void) { struct rsrr_header *rsrr; struct rsrr_vif *vif_list; struct uvif *v; int vifi, sendlen; /* Check for space. There should be room for plenty of vifs, * but we should check anyway. */ if (numvifs > RSRR_MAX_VIFS) { log(LOG_WARNING, 0, "Can't send RSRR Route Reply because %d is too many vifs", numvifs); return; } /* Set up message */ rsrr = (struct rsrr_header *) rsrr_send_buf; rsrr->version = 1; rsrr->type = RSRR_INITIAL_REPLY; rsrr->flags = 0; rsrr->num = numvifs; vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN); /* Include the vif list. */ for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) { vif_list[vifi].id = vifi; vif_list[vifi].status = 0; if (v->uv_flags & VIFF_DISABLED) BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT); vif_list[vifi].threshold = v->uv_threshold; vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr; } /* Get the size. */ sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN; /* Send it. */ IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Send RSRR Initial Reply"); rsrr_send(sendlen); } /* Send a Route Reply to the reservation protocol. The Route Query * contains the query to which we are responding. The flags contain * the incoming flags from the query or, for route change * notification, the flags that should be set for the reply. The * kernel table entry contains the routing info to use for a route * change notification. */ static int rsrr_accept_rq(struct rsrr_rq *route_query, int flags, struct gtable *gt_notify) { struct rsrr_header *rsrr; struct rsrr_rr *route_reply; struct gtable *gt,local_g; struct rtentry *r; int sendlen; u_long mcastgrp; /* Set up message */ rsrr = (struct rsrr_header *) rsrr_send_buf; rsrr->version = 1; rsrr->type = RSRR_ROUTE_REPLY; rsrr->flags = 0; rsrr->num = 0; route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN); route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr; route_reply->source_addr.s_addr = route_query->source_addr.s_addr; route_reply->query_id = route_query->query_id; /* Blank routing entry for error. */ route_reply->in_vif = 0; route_reply->reserved = 0; route_reply->out_vif_bm = 0; /* Get the size. */ sendlen = RSRR_RR_LEN; /* If kernel table entry is defined, then we are sending a Route Reply * due to a Route Change Notification event. Use the kernel table entry * to supply the routing info. */ if (gt_notify) { /* Set flags */ rsrr->flags = flags; /* Include the routing entry. */ route_reply->in_vif = gt_notify->gt_route->rt_parent; if (BIT_TST(flags,RSRR_NOTIFICATION_BIT)) route_reply->out_vif_bm = gt_notify->gt_grpmems; else route_reply->out_vif_bm = 0; } else if (find_src_grp(route_query->source_addr.s_addr, 0, route_query->dest_addr.s_addr)) { /* Found kernel entry. Code taken from add_table_entry() */ gt = gtp ? gtp->gt_gnext : kernel_table; /* Include the routing entry. */ route_reply->in_vif = gt->gt_route->rt_parent; route_reply->out_vif_bm = gt->gt_grpmems; /* Cache reply if using route change notification. */ if BIT_TST(flags,RSRR_NOTIFICATION_BIT) { rsrr_cache(gt,route_query); BIT_SET(rsrr->flags,RSRR_NOTIFICATION_BIT); } } else { /* No kernel entry; use routing table. */ r = determine_route(route_query->source_addr.s_addr); if (r != NULL) { /* We need to mimic what will happen if a data packet * is forwarded by multicast routing -- the kernel will * make an upcall and mrouted will install a route in the kernel. * Our outgoing vif bitmap should reflect what that table * will look like. Grab code from add_table_entry(). * This is gross, but it's probably better to be accurate. */ gt = &local_g; mcastgrp = route_query->dest_addr.s_addr; gt->gt_mcastgrp = mcastgrp; gt->gt_grpmems = 0; gt->gt_scope = 0; gt->gt_route = r; /* obtain the multicast group membership list */ determine_forwvifs(gt); /* Include the routing entry. */ route_reply->in_vif = gt->gt_route->rt_parent; route_reply->out_vif_bm = gt->gt_grpmems; } else { /* Set error bit. */ BIT_SET(rsrr->flags,RSRR_ERROR_BIT); } } IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n", gt_notify ? "Route Change: " : "", inet_fmt(route_reply->source_addr.s_addr,s1), inet_fmt(route_reply->dest_addr.s_addr,s2), route_reply->in_vif,route_reply->out_vif_bm); /* Send it. */ return rsrr_send(sendlen); } /* Send an RSRR message. */ static int rsrr_send(int sendlen) { int error; /* Send it. */ error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0, (struct sockaddr *)&client_addr, client_length); /* Check for errors. */ if (error < 0) { log(LOG_WARNING, errno, "Failed send on RSRR socket"); } else if (error != sendlen) { log(LOG_WARNING, 0, "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen); } return error; } /* Cache a message being sent to a client. Currently only used for * caching Route Reply messages for route change notification. */ static void rsrr_cache(struct gtable *gt, struct rsrr_rq *route_query) { struct rsrr_cache *rc, **rcnp; struct rsrr_header *rsrr; rsrr = (struct rsrr_header *) rsrr_send_buf; rcnp = >->gt_rsrr_cache; while ((rc = *rcnp) != NULL) { if ((rc->route_query.source_addr.s_addr == route_query->source_addr.s_addr) && (rc->route_query.dest_addr.s_addr == route_query->dest_addr.s_addr) && (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) { /* Cache entry already exists. * Check if route notification bit has been cleared. */ if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) { /* Delete cache entry. */ *rcnp = rc->next; free(rc); } else { /* Update */ rc->route_query.query_id = route_query->query_id; IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Update cached query id %ld from client %s\n", rc->route_query.query_id, rc->client_addr.sun_path); } return; } rcnp = &rc->next; } /* Cache entry doesn't already exist. Create one and insert at * front of list. */ rc = (struct rsrr_cache *) malloc(sizeof(struct rsrr_cache)); if (rc == NULL) log(LOG_ERR, 0, "ran out of memory"); rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr; rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr; rc->route_query.query_id = route_query->query_id; strcpy(rc->client_addr.sun_path, client_addr.sun_path); rc->client_length = client_length; rc->next = gt->gt_rsrr_cache; gt->gt_rsrr_cache = rc; IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Cached query id %ld from client %s\n", rc->route_query.query_id,rc->client_addr.sun_path); } /* Send all the messages in the cache. Currently this is used to send * all the cached Route Reply messages for route change notification. */ void rsrr_cache_send(struct gtable *gt, int notify) { struct rsrr_cache *rc, **rcnp; int flags = 0; if (notify) BIT_SET(flags,RSRR_NOTIFICATION_BIT); rcnp = >->gt_rsrr_cache; while ((rc = *rcnp) != NULL) { if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) { IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n", rc->route_query.query_id,rc->client_addr.sun_path); /* Delete cache entry. */ *rcnp = rc->next; free(rc); } else { rcnp = &rc->next; } } } /* Clean the cache by deleting all entries. */ void rsrr_cache_clean(struct gtable *gt) { struct rsrr_cache *rc, *rc_next; IF_DEBUG(DEBUG_RSRR) log(LOG_DEBUG, 0, "cleaning cache for group %s\n", inet_fmt(gt->gt_mcastgrp, s1)); rc = gt->gt_rsrr_cache; while (rc) { rc_next = rc->next; free(rc); rc = rc_next; } gt->gt_rsrr_cache = NULL; } void rsrr_clean(void) { unlink(RSRR_SERV_PATH); } #endif /* RSRR */