NetBSD/usr.sbin/mrouted/rsrr.c

365 lines
10 KiB
C

/*
* 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.
*/
/* RSRR code written by Daniel Zappala, USC Information Sciences Institute,
* April 1995.
*/
#ifdef RSRR
#include "defs.h"
/* Taken from prune.c */
/*
* checks for scoped multicast addresses
*/
#define GET_SCOPE(gt) { \
register int _i; \
if (((gt)->gt_mcastgrp & 0xff000000) == 0xef000000) \
for (_i = 0; _i < numvifs; _i++) \
if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
VIFM_SET(_i, (gt)->gt_scope); \
}
/*
* 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 */
/*
* Procedure definitions needed internally.
*/
void rsrr_accept();
void rsrr_send();
void rsrr_accept_iq();
void rsrr_accept_rq();
/* Initialize RSRR socket */
void
rsrr_init()
{
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);
servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
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 */
void
rsrr_read()
{
register int rsrr_recvlen;
struct sockaddr_un client_addr;
int client_length = sizeof(client_addr);
register int omask;
bzero((char *) &client_addr, sizeof(client_addr));
rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
0, &client_addr, &client_length);
if (rsrr_recvlen < 0) {
if (errno != EINTR)
log(LOG_ERR, errno, "RSRR recvfrom");
return;
}
/* Use of omask taken from main() */
omask = sigblock(sigmask(SIGALRM));
rsrr_accept(rsrr_recvlen,&client_addr,client_length);
(void)sigsetmask(omask);
}
/* Accept a message from the reservation protocol and take
* appropriate action.
*/
void
rsrr_accept(recvlen,client_addr,client_length)
int recvlen;
struct sockaddr_un *client_addr;
int client_length;
{
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 */
log(LOG_INFO, 0, "Received Initial Query\n");
rsrr_accept_iq(client_addr,client_length);
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);
log(LOG_INFO, 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(rsrr,route_query,client_addr,client_length);
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. */
void
rsrr_accept_iq(client_addr,client_length)
struct sockaddr_un *client_addr;
int client_length;
{
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 %d",
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. */
log(LOG_INFO, 0, "Send RSRR Initial Reply");
rsrr_send(sendlen,client_addr,client_length);
}
/* Send a Route Reply to the reservation protocol. */
void
rsrr_accept_rq(rsrr_in,route_query,client_addr,client_length)
struct rsrr_header *rsrr_in;
struct rsrr_rq *route_query;
struct sockaddr_un *client_addr;
int client_length;
{
struct rsrr_header *rsrr;
struct rsrr_rr *route_reply;
struct gtable *gt,local_g;
struct rtentry *r;
int sendlen,i;
u_long mcastgrp;
/* Set up message */
rsrr = (struct rsrr_header *) rsrr_send_buf;
rsrr->version = 1;
rsrr->type = RSRR_ROUTE_REPLY;
rsrr->flags = rsrr_in->flags;
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;
/* Clear error bit. */
BIT_CLR(rsrr->flags,RSRR_ERROR_BIT);
/* Turn notification off. We don't do it yet. */
BIT_CLR(rsrr->flags,RSRR_NOTIFICATION_BIT);
/* First check kernel. Code taken from add_table_entry() */
if (find_src_grp(route_query->source_addr.s_addr, 0,
route_query->dest_addr.s_addr)) {
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;
} 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 */
for (i = 0; i < numvifs; i++) {
if (VIFM_ISSET(i, r->rt_children) &&
!(VIFM_ISSET(i, r->rt_leaves)))
VIFM_SET(i, gt->gt_grpmems);
if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
VIFM_SET(i, gt->gt_grpmems);
}
GET_SCOPE(gt);
gt->gt_grpmems &= ~gt->gt_scope;
/* 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);
}
}
/* Get the size. */
sendlen = RSRR_RR_LEN;
log(LOG_INFO, 0, "Send RSRR Route Reply for src %s grp %s ",
inet_fmt(route_reply->source_addr.s_addr,s1),
inet_fmt(route_reply->dest_addr.s_addr,s2));
log(LOG_INFO, 0, "in vif %d out vif %d\n",
route_reply->in_vif,route_reply->out_vif_bm);
/* Send it. */
rsrr_send(sendlen,client_addr,client_length);
}
/* Send an RSRR message. */
void
rsrr_send(sendlen,client_addr,client_length)
int sendlen;
struct sockaddr_un *client_addr;
int client_length;
{
int error;
/* Send it. */
error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
*client_addr, client_length);
/* Check for errors. */
if (error < 0) {
log(LOG_WARNING, errno, "Failed send on RSRR socket");
return;
}
if (error != sendlen) {
log(LOG_WARNING, 0,
"Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
return;
}
}
void
rsrr_clean()
{
unlink(RSRR_SERV_PATH);
}
#endif RSRR