This commit was generated by cvs2svn to compensate for changes in r2555,
which included commits to RCS files with non-trunk default branches.
This commit is contained in:
commit
3ce94772cc
|
@ -0,0 +1,48 @@
|
||||||
|
|
||||||
|
The mrouted program is covered by the following license. Use of the
|
||||||
|
mrouted program represents acceptance of these terms and conditions.
|
||||||
|
|
||||||
|
1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
|
||||||
|
to use, copy and modify the computer software ``mrouted'' (hereinafter
|
||||||
|
called the ``Program''), upon the terms and conditions hereinafter set
|
||||||
|
out and until Licensee discontinues use of the Licensed Program.
|
||||||
|
|
||||||
|
2. LICENSEE acknowledges that the Program is a research tool still in
|
||||||
|
the development state, that it is being supplied ``as is,'' without any
|
||||||
|
accompanying services from STANFORD, and that this license is entered
|
||||||
|
into in order to encourage scientific collaboration aimed at further
|
||||||
|
development and application of the Program.
|
||||||
|
|
||||||
|
3. LICENSEE may copy the Program and may sublicense others to use object
|
||||||
|
code copies of the Program or any derivative version of the Program.
|
||||||
|
All copies must contain all copyright and other proprietary notices found
|
||||||
|
in the Program as provided by STANFORD. Title to copyright to the
|
||||||
|
Program remains with STANFORD.
|
||||||
|
|
||||||
|
4. LICENSEE may create derivative versions of the Program. LICENSEE
|
||||||
|
hereby grants STANFORD a royalty-free license to use, copy, modify,
|
||||||
|
distribute and sublicense any such derivative works. At the time
|
||||||
|
LICENSEE provides a copy of a derivative version of the Program to a
|
||||||
|
third party, LICENSEE shall provide STANFORD with one copy of the source
|
||||||
|
code of the derivative version at no charge to STANFORD.
|
||||||
|
|
||||||
|
5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
|
||||||
|
By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
|
||||||
|
OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||||
|
THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
|
||||||
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
|
||||||
|
for any liability nor for any direct, indirect or consequential damages
|
||||||
|
with respect to any claim by LICENSEE or any third party on account of or
|
||||||
|
arising from this Agreement or use of the Program.
|
||||||
|
|
||||||
|
6. This agreement shall be construed, interpreted and applied in
|
||||||
|
accordance with the State of California and any legal action arising
|
||||||
|
out of this Agreement or use of the Program shall be filed in a court
|
||||||
|
in the State of California.
|
||||||
|
|
||||||
|
7. Nothing in this Agreement shall be construed as conferring rights to
|
||||||
|
use in advertising, publicity or otherwise any trademark or the name
|
||||||
|
of ``Stanford''.
|
||||||
|
|
||||||
|
The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
Leland Stanford Junior University.
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: callout.c,v 1.1 1994/08/24 23:52:49 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
/* the code below implements a callout queue */
|
||||||
|
static int id = 0;
|
||||||
|
static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
|
||||||
|
|
||||||
|
static int in_callout= 0;
|
||||||
|
|
||||||
|
typedef void (* cfunc_t)();
|
||||||
|
|
||||||
|
struct timeout_q {
|
||||||
|
struct timeout_q *next; /* next event */
|
||||||
|
int id;
|
||||||
|
cfunc_t func ; /* function to call */
|
||||||
|
char *data; /* func's data */
|
||||||
|
int time; /* time offset to next event*/
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
callout_init()
|
||||||
|
{
|
||||||
|
Q = (struct timeout_q *) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* signal handler for SIGALARM that is called once every second
|
||||||
|
*/
|
||||||
|
age_callout_queue()
|
||||||
|
{
|
||||||
|
struct timeout_q *ptr;
|
||||||
|
|
||||||
|
if (in_callout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
in_callout = 1;
|
||||||
|
ptr = Q;
|
||||||
|
|
||||||
|
while (ptr){
|
||||||
|
if (!ptr->time ) {
|
||||||
|
/* timeout has happened */
|
||||||
|
if(ptr->func)
|
||||||
|
ptr->func(ptr->data);
|
||||||
|
Q = Q->next;
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
ptr = Q;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ptr->time --;
|
||||||
|
#ifdef IGMP_DEBUG
|
||||||
|
log(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time);
|
||||||
|
#endif IGMP_DEBUG
|
||||||
|
in_callout = 0; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_callout = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sets the timer
|
||||||
|
*/
|
||||||
|
int timer_setTimer(delay, action, data)
|
||||||
|
int delay; /* number of units for timeout */
|
||||||
|
cfunc_t action; /* function to be called on timeout */
|
||||||
|
char *data; /* what to call the timeout function with */
|
||||||
|
{
|
||||||
|
struct timeout_q *ptr, *node, *prev;
|
||||||
|
|
||||||
|
if (in_callout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
in_callout = 1;
|
||||||
|
|
||||||
|
/* create a node */
|
||||||
|
node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
|
||||||
|
if ((int) node <= 0) {
|
||||||
|
log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
|
||||||
|
in_callout = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
node->func = action;
|
||||||
|
node->data = data;
|
||||||
|
node->time = delay;
|
||||||
|
node->next = 0;
|
||||||
|
node->id = ++id;
|
||||||
|
|
||||||
|
prev = ptr = Q;
|
||||||
|
|
||||||
|
/* insert node in the queue */
|
||||||
|
|
||||||
|
/* if the queue is empty, insert the node and return */
|
||||||
|
if (!Q)
|
||||||
|
Q = node;
|
||||||
|
else {
|
||||||
|
/* chase the pointer looking for the right place */
|
||||||
|
while (ptr){
|
||||||
|
|
||||||
|
if (delay < ptr->time){
|
||||||
|
/* right place */
|
||||||
|
|
||||||
|
node->next = ptr;
|
||||||
|
if (ptr == Q)
|
||||||
|
Q = node;
|
||||||
|
else
|
||||||
|
prev->next = node;
|
||||||
|
ptr->time -= node->time;
|
||||||
|
print_Q();
|
||||||
|
in_callout = 0;
|
||||||
|
return node->id;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* keep moving */
|
||||||
|
|
||||||
|
delay -= ptr->time; node->time = delay;
|
||||||
|
prev = ptr;
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev->next = node;
|
||||||
|
}
|
||||||
|
print_Q();
|
||||||
|
in_callout = 0;
|
||||||
|
return node->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* clears the associated timer */
|
||||||
|
void timer_clearTimer( id)
|
||||||
|
int id;
|
||||||
|
{
|
||||||
|
struct timeout_q *ptr, *prev;
|
||||||
|
|
||||||
|
if (in_callout) return;
|
||||||
|
in_callout = 1;
|
||||||
|
|
||||||
|
|
||||||
|
if ( !id ) {in_callout = 0; return;}
|
||||||
|
|
||||||
|
prev = ptr = Q;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find the right node, delete it. the subsequent node's time
|
||||||
|
* gets bumped up
|
||||||
|
*/
|
||||||
|
|
||||||
|
print_Q();
|
||||||
|
while (ptr){
|
||||||
|
if (ptr->id == id){
|
||||||
|
/* got the right node */
|
||||||
|
|
||||||
|
/* unlink it from the queue */
|
||||||
|
if ( ptr == Q)
|
||||||
|
Q = Q->next;
|
||||||
|
else
|
||||||
|
prev->next = ptr->next;
|
||||||
|
|
||||||
|
/* increment next node if any */
|
||||||
|
if (ptr->next != 0)
|
||||||
|
(ptr->next)->time += ptr->time;
|
||||||
|
|
||||||
|
free(ptr->data);
|
||||||
|
free(ptr);
|
||||||
|
print_Q();
|
||||||
|
in_callout = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prev = ptr;
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
print_Q();
|
||||||
|
in_callout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* debugging utility
|
||||||
|
*/
|
||||||
|
print_Q()
|
||||||
|
{
|
||||||
|
struct timeout_q *ptr;
|
||||||
|
|
||||||
|
#ifdef IGMP_DEBUG
|
||||||
|
for(ptr = Q; ptr; ptr = ptr->next)
|
||||||
|
log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time);
|
||||||
|
#endif IGMP_DEBUG
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,796 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: config.c,v 1.6 1994/08/24 23:52:54 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
|
||||||
|
char *configfilename = "/etc/mrouted.conf";
|
||||||
|
|
||||||
|
extern int cache_lifetime;
|
||||||
|
extern int max_prune_lifetime;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forward declarations.
|
||||||
|
*/
|
||||||
|
static char *next_word();
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Query the kernel to find network interfaces that are multicast-capable
|
||||||
|
* and install them in the uvifs array.
|
||||||
|
*/
|
||||||
|
void config_vifs_from_kernel()
|
||||||
|
{
|
||||||
|
struct ifreq ifbuf[32];
|
||||||
|
struct ifreq *ifrp, *ifend, *mp;
|
||||||
|
struct ifconf ifc;
|
||||||
|
register struct uvif *v;
|
||||||
|
register vifi_t vifi;
|
||||||
|
int i, n;
|
||||||
|
u_long addr, mask, subnet;
|
||||||
|
short flags;
|
||||||
|
|
||||||
|
ifc.ifc_buf = (char *)ifbuf;
|
||||||
|
ifc.ifc_len = sizeof(ifbuf);
|
||||||
|
if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
|
||||||
|
log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
|
||||||
|
|
||||||
|
ifrp = (struct ifreq *)ifbuf;
|
||||||
|
ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len);
|
||||||
|
/*
|
||||||
|
* Loop through all of the interfaces.
|
||||||
|
*/
|
||||||
|
for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
|
||||||
|
struct ifreq ifr;
|
||||||
|
#if BSD >= 199006
|
||||||
|
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
|
||||||
|
if (n < sizeof(*ifrp))
|
||||||
|
n = sizeof(*ifrp);
|
||||||
|
#else
|
||||||
|
n = sizeof(*ifrp);
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
* Ignore any interface for an address family other than IP.
|
||||||
|
*/
|
||||||
|
addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
|
||||||
|
if (ifrp->ifr_addr.sa_family != AF_INET)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need a template to preserve address info that is
|
||||||
|
* used below to locate the next entry. (Otherwise,
|
||||||
|
* SIOCGIFFLAGS stomps over it because the requests
|
||||||
|
* are returned in a union.)
|
||||||
|
*/
|
||||||
|
bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore loopback interfaces and interfaces that do not support
|
||||||
|
* multicast.
|
||||||
|
*/
|
||||||
|
if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
|
||||||
|
log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
|
||||||
|
flags = ifr.ifr_flags;
|
||||||
|
if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore any interface whose address and mask do not define a
|
||||||
|
* valid subnet number, or whose address is of the form {subnet,0}
|
||||||
|
* or {subnet,-1}.
|
||||||
|
*/
|
||||||
|
if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0)
|
||||||
|
log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name);
|
||||||
|
mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
|
||||||
|
subnet = addr & mask;
|
||||||
|
if (!inet_valid_subnet(subnet, mask) ||
|
||||||
|
addr == subnet ||
|
||||||
|
addr == (subnet | ~mask)) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"ignoring %s, has invalid address (%s) and/or mask (%08x)",
|
||||||
|
ifr.ifr_name, inet_fmt(addr, s1), ntohl(mask));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore any interface that is connected to the same subnet as
|
||||||
|
* one already installed in the uvifs array.
|
||||||
|
*/
|
||||||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||||||
|
if ((addr & v->uv_subnetmask) == v->uv_subnet ||
|
||||||
|
(v->uv_subnet & mask) == subnet) {
|
||||||
|
log(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
|
||||||
|
ifr.ifr_name, v->uv_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vifi != numvifs) continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is room in the uvifs array, install this interface.
|
||||||
|
*/
|
||||||
|
if (numvifs == MAXVIFS) {
|
||||||
|
log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
v = &uvifs[numvifs];
|
||||||
|
v->uv_flags = 0;
|
||||||
|
v->uv_metric = DEFAULT_METRIC;
|
||||||
|
v->uv_rate_limit = DEFAULT_RATE_LIMIT;
|
||||||
|
v->uv_threshold = DEFAULT_THRESHOLD;
|
||||||
|
v->uv_lcl_addr = addr;
|
||||||
|
v->uv_rmt_addr = 0;
|
||||||
|
v->uv_subnet = subnet;
|
||||||
|
v->uv_subnetmask = mask;
|
||||||
|
v->uv_subnetbcast = subnet | ~mask;
|
||||||
|
strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
|
||||||
|
v->uv_groups = NULL;
|
||||||
|
v->uv_neighbors = NULL;
|
||||||
|
v->uv_acl = NULL;
|
||||||
|
|
||||||
|
log(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d",
|
||||||
|
v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
|
||||||
|
numvifs, v->uv_rate_limit);
|
||||||
|
|
||||||
|
++numvifs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the interface is not yet up, set the vifs_down flag to
|
||||||
|
* remind us to check again later.
|
||||||
|
*/
|
||||||
|
if (!(flags & IFF_UP)) {
|
||||||
|
v->uv_flags |= VIFF_DOWN;
|
||||||
|
vifs_down = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ifreq *
|
||||||
|
ifconfaddr(ifcp, a)
|
||||||
|
struct ifconf *ifcp;
|
||||||
|
u_long a;
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf;
|
||||||
|
struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len);
|
||||||
|
|
||||||
|
while (ifrp < ifend) {
|
||||||
|
if (ifrp->ifr_addr.sa_family == AF_INET &&
|
||||||
|
((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
|
||||||
|
return (ifrp);
|
||||||
|
#if BSD >= 199006
|
||||||
|
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
|
||||||
|
if (n < sizeof(*ifrp))
|
||||||
|
++ifrp;
|
||||||
|
else
|
||||||
|
ifrp = (struct ifreq *)((char *)ifrp + n);
|
||||||
|
#else
|
||||||
|
++ifrp;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks if the string constitutes a valid interface name
|
||||||
|
*/
|
||||||
|
static u_long valid_if(w)
|
||||||
|
char *w;
|
||||||
|
{
|
||||||
|
register vifi_t vifi;
|
||||||
|
register struct uvif *v;
|
||||||
|
|
||||||
|
for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
|
||||||
|
if (EQUAL(v->uv_name, w))
|
||||||
|
return v->uv_lcl_addr;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the config file to learn about tunnel vifs and
|
||||||
|
* non-default phyint parameters.
|
||||||
|
*/
|
||||||
|
void config_vifs_from_file()
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char linebuf[100];
|
||||||
|
char *w, *s, c;
|
||||||
|
u_long lcl_addr, rmt_addr;
|
||||||
|
struct ifconf ifc;
|
||||||
|
struct ifreq *ifr;
|
||||||
|
struct ifreq ffr;
|
||||||
|
int i;
|
||||||
|
u_int n;
|
||||||
|
struct ifreq ifbuf[32];
|
||||||
|
vifi_t vifi;
|
||||||
|
struct uvif *v;
|
||||||
|
u_char order = 0;
|
||||||
|
vifi_t prev_vif = NO_VIF;
|
||||||
|
|
||||||
|
f = fopen(configfilename, "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
if (errno != ENOENT)
|
||||||
|
log(LOG_ERR, errno, "can't open %s", configfilename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc.ifc_buf = (char *)ifbuf;
|
||||||
|
ifc.ifc_len = sizeof(ifbuf);
|
||||||
|
if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
|
||||||
|
log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
|
||||||
|
|
||||||
|
while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
|
||||||
|
|
||||||
|
s = linebuf;
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
/*
|
||||||
|
* blank or comment line; ignore
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the cache_lifetime for kernel entries */
|
||||||
|
else if (EQUAL(w, "cache_lifetime")) {
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing cache_lifetime value in %s",
|
||||||
|
configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 300 || n > 86400 ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid cache_lifetime '%s' (300<n>86400) in %s",
|
||||||
|
w, configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_vif = NO_VIF;
|
||||||
|
cache_lifetime = n;
|
||||||
|
max_prune_lifetime = cache_lifetime * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if pruning is to be turned off */
|
||||||
|
else if (EQUAL(w, "pruning")) {
|
||||||
|
if (!EQUAL((w = next_word(&s)), "off") &&
|
||||||
|
!EQUAL(w, "on")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid word '%s' in %s",
|
||||||
|
w, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (EQUAL(w, "off"))
|
||||||
|
pruning = 0;
|
||||||
|
|
||||||
|
prev_vif = NO_VIF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for boundary statements (as continuation of a prev. line) */
|
||||||
|
else if (EQUAL(w, "boundary") && prev_vif != NO_VIF) {
|
||||||
|
register struct vif_acl *v_acl;
|
||||||
|
register u_long baddr;
|
||||||
|
|
||||||
|
v = &uvifs[prev_vif];
|
||||||
|
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing group address for boundary %s in %s",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
|
||||||
|
n < 0 || n> 32) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect boundary format %s in %s",
|
||||||
|
w, configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((baddr = inet_parse(s1)) == 0xffffffff ||
|
||||||
|
(baddr & 0xff000000) != 0xef000000) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect boundary address %s in %s",
|
||||||
|
s1, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
|
||||||
|
if (v_acl == NULL)
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"out of memory");
|
||||||
|
VAL_TO_MASK(v_acl->acl_mask, n);
|
||||||
|
v_acl->acl_addr = baddr & v_acl->acl_mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* link into data structure
|
||||||
|
*/
|
||||||
|
v_acl->acl_next = v->uv_acl;
|
||||||
|
v->uv_acl = v_acl;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (EQUAL(w, "phyint")) {
|
||||||
|
/*
|
||||||
|
* phyint <local-addr> [disable] [metric <m>] [threshold <t>]
|
||||||
|
* [rate_limit <b>]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if phyint was the first line - scream if not
|
||||||
|
*/
|
||||||
|
if (order) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"phyint stmnts should occur before tunnel stmnts in %s",
|
||||||
|
configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the local address.
|
||||||
|
*/
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing phyint address in %s",
|
||||||
|
configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isalpha(*w) && !(lcl_addr = valid_if(w))) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid phyint name '%s' in %s",
|
||||||
|
w, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit(*w)) {
|
||||||
|
if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
|
||||||
|
!inet_valid_host(lcl_addr)) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid phyint address '%s' in %s",
|
||||||
|
w, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up the vif with the specified local address.
|
||||||
|
*/
|
||||||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||||||
|
if (!(v->uv_flags & VIFF_TUNNEL) &&
|
||||||
|
lcl_addr == v->uv_lcl_addr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vifi == numvifs) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"phyint %s in %s is not a configured interface",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for "disable", "metric", "threshold", "rate_limit"
|
||||||
|
* and "boundary" options.
|
||||||
|
*/
|
||||||
|
prev_vif = vifi;
|
||||||
|
|
||||||
|
while (!EQUAL((w = next_word(&s)), "")) {
|
||||||
|
if (EQUAL(w, "disable")) {
|
||||||
|
v->uv_flags |= VIFF_DISABLED;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "metric")) {
|
||||||
|
if(EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing metric for phyint %s in %s",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 1 || n >= UNREACHABLE ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid metric '%s' for phyint %s in %s",
|
||||||
|
w, inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_metric = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "threshold")) {
|
||||||
|
if(EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing threshold for phyint %s in %s",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 1 || n > 255 ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid threshold '%s' for phyint %s in %s",
|
||||||
|
w, inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_threshold = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "rate_limit")) {
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing rate_limit for phyint %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 0 || n > MAX_RATE_LIMIT ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid rate limit '%s' for phyint %s in %s",
|
||||||
|
w, inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_rate_limit = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "boundary")) {
|
||||||
|
register struct vif_acl *v_acl;
|
||||||
|
register u_long baddr;
|
||||||
|
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing group address for boundary %s in %s",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
|
||||||
|
n < 0 || n> 32) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect boundary format %s in %s",
|
||||||
|
w, configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((baddr = inet_parse(s1)) == 0xffffffff ||
|
||||||
|
(baddr & 0xef000000) != 0xef000000) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect boundary address %s in %s",
|
||||||
|
s1, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
|
||||||
|
if (v_acl == NULL)
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"out of memory");
|
||||||
|
VAL_TO_MASK(v_acl->acl_mask, n);
|
||||||
|
v_acl->acl_addr = baddr & v_acl->acl_mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* link into data structure
|
||||||
|
*/
|
||||||
|
v_acl->acl_next = v->uv_acl;
|
||||||
|
v->uv_acl = v_acl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid keyword (%s) in %s",
|
||||||
|
w, configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!EQUAL(w, "")) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (EQUAL(w, "tunnel")) {
|
||||||
|
/*
|
||||||
|
* tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
|
||||||
|
* [threshold <t>] [rate_limit <b>]
|
||||||
|
*/
|
||||||
|
|
||||||
|
order++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the local address.
|
||||||
|
*/
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing tunnel local address in %s",
|
||||||
|
configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((lcl_addr = inet_parse(w)) == 0xffffffff ||
|
||||||
|
!inet_valid_host(lcl_addr)) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid tunnel local address '%s' in %s",
|
||||||
|
w, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the local address is one of ours.
|
||||||
|
*/
|
||||||
|
ifr = ifconfaddr(&ifc, lcl_addr);
|
||||||
|
if (ifr == 0) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"tunnel local address %s in %s is not one of ours",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the local address doesn't name a loopback interface..
|
||||||
|
*/
|
||||||
|
strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
|
||||||
|
if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr) < 0) {
|
||||||
|
log(LOG_ERR, errno,
|
||||||
|
"ioctl SIOCGIFFLAGS for %s", ffr.ifr_name);
|
||||||
|
}
|
||||||
|
if (ffr.ifr_flags & IFF_LOOPBACK) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"tunnel local address %s in %s is a loopback interface",
|
||||||
|
inet_fmt(lcl_addr, s1), configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the remote address.
|
||||||
|
*/
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing tunnel remote address in %s",
|
||||||
|
configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((rmt_addr = inet_parse(w)) == 0xffffffff ||
|
||||||
|
!inet_valid_host(rmt_addr)) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid tunnel remote address %s in %s",
|
||||||
|
w, configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the remote address is not one of ours.
|
||||||
|
*/
|
||||||
|
if (ifconfaddr(&ifc, rmt_addr) != 0) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"tunnel remote address %s in %s is one of ours",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the remote address has not been used for another
|
||||||
|
* tunnel and does not belong to a subnet to which we have direct
|
||||||
|
* access on an enabled phyint.
|
||||||
|
*/
|
||||||
|
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
|
||||||
|
if (v->uv_flags & VIFF_TUNNEL) {
|
||||||
|
if (rmt_addr == v->uv_rmt_addr) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"duplicate tunnel remote address %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!(v->uv_flags & VIFF_DISABLED)) {
|
||||||
|
if ((rmt_addr & v->uv_subnetmask) == v->uv_subnet) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"unnecessary tunnel remote address %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vifi != numvifs) continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, let's initialize a uvif structure for the tunnel.
|
||||||
|
*/
|
||||||
|
if (numvifs == MAXVIFS) {
|
||||||
|
log(LOG_ERR, 0, "too many vifs, ignoring tunnel to %s",
|
||||||
|
inet_fmt(rmt_addr, s1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
v = &uvifs[numvifs];
|
||||||
|
v->uv_flags = VIFF_TUNNEL;
|
||||||
|
v->uv_metric = DEFAULT_METRIC;
|
||||||
|
v->uv_rate_limit = DEFAULT_RATE_LIMIT;
|
||||||
|
v->uv_threshold = DEFAULT_THRESHOLD;
|
||||||
|
v->uv_lcl_addr = lcl_addr;
|
||||||
|
v->uv_rmt_addr = rmt_addr;
|
||||||
|
v->uv_subnet = 0;
|
||||||
|
v->uv_subnetmask = 0;
|
||||||
|
v->uv_subnetbcast = 0;
|
||||||
|
strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
|
||||||
|
v->uv_groups = NULL;
|
||||||
|
v->uv_neighbors = NULL;
|
||||||
|
v->uv_acl = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set variable to define which interface
|
||||||
|
*/
|
||||||
|
prev_vif = numvifs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for "metric", "threshold", "srcrt", "rate_limit"
|
||||||
|
* and "boundary" options.
|
||||||
|
*/
|
||||||
|
while (!EQUAL((w = next_word(&s)), "")) {
|
||||||
|
if (EQUAL(w, "metric")) {
|
||||||
|
if(EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing metric for tunnel to %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 1 || n >= UNREACHABLE ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid metric '%s' for tunnel to %s in %s",
|
||||||
|
w, inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_metric = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "threshold")) {
|
||||||
|
if(EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing threshold for tunnel to %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 1 || n > 255 ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid threshold '%s' for tunnel to %s in %s",
|
||||||
|
w, inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_threshold = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "srcrt") || EQUAL(w, "sourceroute")) {
|
||||||
|
v->uv_flags |= VIFF_SRCRT;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "rate_limit")) {
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing rate_limit for tunnel to %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(sscanf(w, "%u%c", &n, &c) != 1 ||
|
||||||
|
n < 0 || n > MAX_RATE_LIMIT ) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid rate_limit '%s' for tunnel to %s in %s",
|
||||||
|
w, inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v->uv_rate_limit = n;
|
||||||
|
}
|
||||||
|
else if (EQUAL(w, "boundary")) {
|
||||||
|
register struct vif_acl *v_acl;
|
||||||
|
register u_long baddr;
|
||||||
|
|
||||||
|
if (EQUAL((w = next_word(&s)), "")) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"missing group address for tunnel to %s in %s",
|
||||||
|
inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
w = "garbage";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sscanf(w, "%[0-9.]/%d", s1, &n) != 2) ||
|
||||||
|
n < 0 || n> 32) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect format '%s' for tunnel to %s in %s",
|
||||||
|
w, inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((baddr = inet_parse(s1)) == 0xffffffff ||
|
||||||
|
(baddr & 0xef000000) != 0xef000000) {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"incorrect address %s for tunnel to %s in %s",
|
||||||
|
s1, inet_fmt(rmt_addr, s1), configfilename);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
|
||||||
|
if (v_acl == NULL)
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"out of memory");
|
||||||
|
VAL_TO_MASK(v_acl->acl_mask, n);
|
||||||
|
v_acl->acl_addr = baddr & v_acl->acl_mask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* link into data structure
|
||||||
|
*/
|
||||||
|
v_acl->acl_next = v->uv_acl;
|
||||||
|
v->uv_acl = v_acl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"invalid keyword (%s) in %s",
|
||||||
|
w, configfilename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!EQUAL(w, "")) continue;
|
||||||
|
|
||||||
|
log(LOG_INFO, 0,
|
||||||
|
"installing %stunnel from %s to %s as vif #%u - rate=%d",
|
||||||
|
v->uv_flags & VIFF_SRCRT? "srcrt " : "",
|
||||||
|
inet_fmt(lcl_addr, s1), inet_fmt(rmt_addr, s2),
|
||||||
|
numvifs, v->uv_rate_limit);
|
||||||
|
|
||||||
|
++numvifs;
|
||||||
|
|
||||||
|
if (!(ffr.ifr_flags & IFF_UP)) {
|
||||||
|
v->uv_flags |= VIFF_DOWN;
|
||||||
|
vifs_down = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
log(LOG_ERR, 0,
|
||||||
|
"unknown command '%s' in %s", w, configfilename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a pointer to the next "word" in the string to which '*s' points,
|
||||||
|
* lower-cased and null terminated, and advance '*s' to point beyond the word.
|
||||||
|
* Words are separated by blanks and/or tabs, and the input string is
|
||||||
|
* considered to terminate at a newline, '#' (comment), or null character.
|
||||||
|
* If no words remain, a pointer to a null string ("") is returned.
|
||||||
|
* Warning: This function clobbers the input string.
|
||||||
|
*/
|
||||||
|
static char *next_word(s)
|
||||||
|
char **s;
|
||||||
|
{
|
||||||
|
char *w;
|
||||||
|
|
||||||
|
w = *s;
|
||||||
|
while (*w == ' ' || *w == '\t')
|
||||||
|
++w;
|
||||||
|
|
||||||
|
*s = w;
|
||||||
|
for(;;) {
|
||||||
|
switch (**s) {
|
||||||
|
|
||||||
|
case ' ' :
|
||||||
|
case '\t' : **s = '\0';
|
||||||
|
++*s;
|
||||||
|
return (w);
|
||||||
|
|
||||||
|
case '\n' :
|
||||||
|
case '#' : **s = '\0';
|
||||||
|
return (w);
|
||||||
|
|
||||||
|
case '\0' : return (w);
|
||||||
|
|
||||||
|
default : if (isascii(**s) && isupper(**s))
|
||||||
|
**s = tolower(**s);
|
||||||
|
++*s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: defs.h,v 1.8 1994/08/24 23:53:23 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/in_systm.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/igmp.h>
|
||||||
|
#include <netinet/ip_mroute.h>
|
||||||
|
|
||||||
|
#include "dvmrp.h"
|
||||||
|
#include "vif.h"
|
||||||
|
#include "route.h"
|
||||||
|
#include "prune.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Miscellaneous constants and macros.
|
||||||
|
*/
|
||||||
|
#define FALSE 0
|
||||||
|
#define TRUE 1
|
||||||
|
|
||||||
|
#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
|
||||||
|
|
||||||
|
#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
|
||||||
|
|
||||||
|
#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
|
||||||
|
|
||||||
|
#define MROUTED_VERSION 3 /* increment on local changes or bug fixes, */
|
||||||
|
/* reset to 0 whever PROTOCOL_VERSION increments */
|
||||||
|
|
||||||
|
#define MROUTED_LEVEL ( (MROUTED_VERSION << 8) | PROTOCOL_VERSION )
|
||||||
|
/* for IGMP 'group' field of DVMRP messages */
|
||||||
|
|
||||||
|
#define DEL_RTE_GROUP 0
|
||||||
|
#define DEL_ALL_ROUTES 1
|
||||||
|
/* for Deleting kernel table entries */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* External declarations for global variables and functions.
|
||||||
|
*/
|
||||||
|
extern char recv_buf[MAX_IP_PACKET_LEN];
|
||||||
|
extern char send_buf[MAX_IP_PACKET_LEN];
|
||||||
|
extern int igmp_socket;
|
||||||
|
extern u_long allhosts_group;
|
||||||
|
extern u_long dvmrp_group;
|
||||||
|
extern u_long dvmrp_genid;
|
||||||
|
|
||||||
|
#define DEFAULT_DEBUG 2 /* default if "-d" given without value */
|
||||||
|
|
||||||
|
extern int debug;
|
||||||
|
extern u_char pruning;
|
||||||
|
|
||||||
|
extern int routes_changed;
|
||||||
|
extern int delay_change_reports;
|
||||||
|
extern unsigned nroutes;
|
||||||
|
|
||||||
|
extern struct uvif uvifs[MAXVIFS];
|
||||||
|
extern vifi_t numvifs;
|
||||||
|
extern int vifs_down;
|
||||||
|
extern int udp_socket;
|
||||||
|
|
||||||
|
extern char s1[];
|
||||||
|
extern char s2[];
|
||||||
|
extern char s3[];
|
||||||
|
|
||||||
|
extern int errno;
|
||||||
|
extern int sys_nerr;
|
||||||
|
extern char * sys_errlist[];
|
||||||
|
|
||||||
|
extern void log();
|
||||||
|
|
||||||
|
extern void init_igmp();
|
||||||
|
extern void accept_igmp();
|
||||||
|
extern void send_igmp();
|
||||||
|
|
||||||
|
extern void init_routes();
|
||||||
|
extern void start_route_updates();
|
||||||
|
extern void update_route();
|
||||||
|
extern void age_routes();
|
||||||
|
extern void expire_all_routes();
|
||||||
|
extern void free_all_routes();
|
||||||
|
|
||||||
|
extern void accept_probe();
|
||||||
|
extern void accept_report();
|
||||||
|
extern void report();
|
||||||
|
extern void report_to_all_neighbors();
|
||||||
|
extern int report_next_chunk();
|
||||||
|
extern void add_vif_to_routes();
|
||||||
|
extern void delete_vif_from_routes();
|
||||||
|
extern void delete_neighbor_from_routes();
|
||||||
|
extern void dump_routes();
|
||||||
|
|
||||||
|
extern void init_vifs();
|
||||||
|
extern void check_vif_state();
|
||||||
|
extern vifi_t find_vif();
|
||||||
|
extern void age_vifs();
|
||||||
|
extern void dump_vifs();
|
||||||
|
extern void stop_all_vifs();
|
||||||
|
|
||||||
|
extern void accept_group_report();
|
||||||
|
extern void query_groups();
|
||||||
|
extern void probe_for_neighbors();
|
||||||
|
extern int update_neighbor();
|
||||||
|
extern void accept_neighbor_request();
|
||||||
|
|
||||||
|
extern void config_vifs_from_kernel();
|
||||||
|
extern void config_vifs_from_file();
|
||||||
|
|
||||||
|
extern int inet_valid_host();
|
||||||
|
extern int inet_valid_subnet();
|
||||||
|
extern char * inet_fmt();
|
||||||
|
extern char * inet_fmts();
|
||||||
|
extern u_long inet_parse();
|
||||||
|
extern int inet_cksum();
|
||||||
|
|
||||||
|
extern struct rtentry * determine_route();
|
||||||
|
|
||||||
|
extern void init_ktable();
|
||||||
|
extern int grplst_mem();
|
||||||
|
extern void add_table_entry();
|
||||||
|
extern void del_table_entry();
|
||||||
|
extern void update_table_entry();
|
||||||
|
extern void update_lclgrp();
|
||||||
|
extern void delete_lclgrp();
|
||||||
|
|
||||||
|
extern unsigned kroutes;
|
||||||
|
extern void send_prune();
|
||||||
|
extern void accept_prune();
|
||||||
|
extern int no_entry_exists();
|
||||||
|
extern struct ktable * find_src_grp();
|
||||||
|
extern int rtr_cnt();
|
||||||
|
extern void free_all_prunes();
|
||||||
|
extern void age_table_entry();
|
||||||
|
extern void dump_cache();
|
||||||
|
|
||||||
|
extern void chkgrp_graft();
|
||||||
|
extern void accept_graft();
|
||||||
|
extern void send_graft_ack();
|
||||||
|
extern void send_graft();
|
||||||
|
extern void accept_g_ack();
|
||||||
|
extern void mtrace();
|
||||||
|
|
||||||
|
extern char * malloc();
|
||||||
|
extern char * fgets();
|
||||||
|
extern FILE * fopen();
|
||||||
|
|
||||||
|
#ifndef htonl
|
||||||
|
extern u_long htonl();
|
||||||
|
extern u_long ntohl();
|
||||||
|
#endif
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: dvmrp.h,v 1.6 1994/08/24 23:53:30 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A DVMRP message consists of an IP header + an IGMP header + (for some types)
|
||||||
|
* zero or more bytes of data.
|
||||||
|
*
|
||||||
|
* For REPORT messages, the data is route information; the route information
|
||||||
|
* consists of one or more lists of the following form:
|
||||||
|
*
|
||||||
|
* (mask, (origin, metric), (origin, metric), ...)
|
||||||
|
*
|
||||||
|
* where:
|
||||||
|
*
|
||||||
|
* "mask" is the subnet mask for all the origins in the list.
|
||||||
|
* It is always THREE bytes long, containing the low-order
|
||||||
|
* three bytes of the mask (the high-order byte is always
|
||||||
|
* 0xff and therefore need not be transmitted).
|
||||||
|
*
|
||||||
|
* "origin" is the number of a subnet from which multicast datagrams
|
||||||
|
* may originate. It is from one to four bytes long,
|
||||||
|
* depending on the value of "mask":
|
||||||
|
* if all bytes of the mask are zero
|
||||||
|
* the subnet number is one byte long
|
||||||
|
* else if the low-order two bytes of the mask are zero
|
||||||
|
* the subnet number is two bytes long
|
||||||
|
* else if the lowest-order byte of the mask is zero
|
||||||
|
* the subnet number is three bytes long,
|
||||||
|
* else
|
||||||
|
* the subnet number is four bytes long.
|
||||||
|
*
|
||||||
|
* "metric" is a one-byte value consisting of two subfields:
|
||||||
|
* - the high-order bit is a flag which, when set, indicates
|
||||||
|
* the last (origin, metric) pair of a list.
|
||||||
|
* - the low-order seven bits contain the routing metric for
|
||||||
|
* the corresponding origin, relative to the sender of the
|
||||||
|
* DVMRP report. The metric may have the value of UNREACHABLE
|
||||||
|
* added to it as a "split horizon" indication (so called
|
||||||
|
* "poisoned reverse").
|
||||||
|
*
|
||||||
|
* Within a list, the origin subnet numbers must be in ascending order, and
|
||||||
|
* the lists themselves are in order of increasing mask value. A message may
|
||||||
|
* not exceed 576 bytes, the default maximum IP reassembly size, including
|
||||||
|
* the IP and IGMP headers; the route information may be split across more
|
||||||
|
* than one message if necessary, by terminating a list in one message and
|
||||||
|
* starting a new list in the next message (repeating the same mask value,
|
||||||
|
* if necessary).
|
||||||
|
*
|
||||||
|
* For NEIGHBORS messages, the data is neighboring-router information
|
||||||
|
* consisting of one or more lists of the following form:
|
||||||
|
*
|
||||||
|
* (local-addr, metric, threshold, ncount, neighbor, neighbor, ...)
|
||||||
|
*
|
||||||
|
* where:
|
||||||
|
*
|
||||||
|
* "local-addr" is the sending router's address as seen by the neighbors
|
||||||
|
* in this list; it is always four bytes long.
|
||||||
|
* "metric" is a one-byte unsigned value, the TTL `cost' of forwarding
|
||||||
|
* packets to any of the neighbors on this list.
|
||||||
|
* "threshold" is a one-byte unsigned value, a lower bound on the TTL a
|
||||||
|
* packet must have to be forwarded to any of the neighbors on
|
||||||
|
* this list.
|
||||||
|
* "ncount" is the number of neighbors in this list.
|
||||||
|
* "neighbor" is the address of a neighboring router, four bytes long.
|
||||||
|
*
|
||||||
|
* As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes,
|
||||||
|
* including the IP and IGMP headers; split longer messages by terminating the
|
||||||
|
* list in one and continuing in another, repeating the local-addr, etc., if
|
||||||
|
* necessary.
|
||||||
|
*
|
||||||
|
* For NEIGHBORS2 messages, the data is identical to NEIGHBORS except
|
||||||
|
* there is a flags byte before the neighbor count:
|
||||||
|
*
|
||||||
|
* (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DVMRP message types (carried in the "code" field of an IGMP header)
|
||||||
|
*/
|
||||||
|
#define DVMRP_PROBE 1 /* for finding neighbors */
|
||||||
|
#define DVMRP_REPORT 2 /* for reporting some or all routes */
|
||||||
|
#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */
|
||||||
|
/* of this router's neighbors. */
|
||||||
|
#define DVMRP_NEIGHBORS 4 /* response to such a request */
|
||||||
|
#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */
|
||||||
|
#define DVMRP_NEIGHBORS2 6
|
||||||
|
#define DVMRP_PRUNE 7 /* prune message */
|
||||||
|
#define DVMRP_GRAFT 8 /* graft message */
|
||||||
|
#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'flags' byte values in DVMRP_NEIGHBORS2 reply.
|
||||||
|
*/
|
||||||
|
#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */
|
||||||
|
#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */
|
||||||
|
#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
|
||||||
|
#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
|
||||||
|
#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Limit on length of route data
|
||||||
|
*/
|
||||||
|
#define MAX_IP_PACKET_LEN 576
|
||||||
|
#define MIN_IP_HEADER_LEN 20
|
||||||
|
#define MAX_IP_HEADER_LEN 60
|
||||||
|
#define MAX_DVMRP_DATA_LEN \
|
||||||
|
( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Various protocol constants (all times in seconds)
|
||||||
|
*/
|
||||||
|
/* address for multicast DVMRP msgs */
|
||||||
|
#define INADDR_DVMRP_GROUP (u_long)0xe0000004 /* 224.0.0.4 */
|
||||||
|
|
||||||
|
#define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */
|
||||||
|
/* (This is the timer interrupt */
|
||||||
|
/* interval; all times must be */
|
||||||
|
/* multiples of this value.) */
|
||||||
|
|
||||||
|
#define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */
|
||||||
|
#define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */
|
||||||
|
#define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */
|
||||||
|
#define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */
|
||||||
|
|
||||||
|
#define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */
|
||||||
|
|
||||||
|
#define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */
|
||||||
|
#define NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */
|
||||||
|
|
||||||
|
#define GROUP_QUERY_INTERVAL 125 /* periodic group query interval */
|
||||||
|
#define GROUP_EXPIRE_TIME 270 /* time to consider group gone */
|
||||||
|
|
||||||
|
#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */
|
||||||
|
#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
|
||||||
|
#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */
|
||||||
|
|
||||||
|
#define MAX_RATE_LIMIT 100000 /* max rate limit */
|
||||||
|
#define DEFAULT_RATE_LIMIT 0 /* default rate limit */
|
||||||
|
|
||||||
|
#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
|
||||||
|
#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
|
||||||
|
|
||||||
|
#define OLD_AGE_THRESHOLD 2
|
|
@ -0,0 +1,289 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: igmp.c,v 1.8 1994/08/24 23:53:32 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exported variables.
|
||||||
|
*/
|
||||||
|
char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */
|
||||||
|
char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */
|
||||||
|
int igmp_socket; /* socket for all network I/O */
|
||||||
|
u_long allhosts_group; /* allhosts addr in net order */
|
||||||
|
u_long dvmrp_group; /* DVMRP grp addr in net order */
|
||||||
|
u_long dvmrp_genid; /* IGMP generation id */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open and initialize the igmp socket, and fill in the non-changing
|
||||||
|
* IP header fields in the output packet buffer.
|
||||||
|
*/
|
||||||
|
void init_igmp()
|
||||||
|
{
|
||||||
|
struct ip *ip;
|
||||||
|
|
||||||
|
if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
|
||||||
|
log(LOG_ERR, errno, "IGMP socket");
|
||||||
|
|
||||||
|
k_hdr_include(TRUE); /* include IP header when sending */
|
||||||
|
k_set_rcvbuf(48*1024); /* lots of input buffering */
|
||||||
|
k_set_ttl(1); /* restrict multicasts to one hop */
|
||||||
|
k_set_loop(FALSE); /* disable multicast loopback */
|
||||||
|
|
||||||
|
ip = (struct ip *)send_buf;
|
||||||
|
ip->ip_tos = 0;
|
||||||
|
ip->ip_off = 0;
|
||||||
|
ip->ip_p = IPPROTO_IGMP;
|
||||||
|
ip->ip_ttl = MAXTTL; /* applies to unicasts only */
|
||||||
|
|
||||||
|
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
|
||||||
|
dvmrp_group = htonl(INADDR_DVMRP_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* %%% hack for PIM %%% */
|
||||||
|
#define IGMP_PIM 0x14
|
||||||
|
#define PIM_QUERY 0
|
||||||
|
#define PIM_REGISTER 1
|
||||||
|
#define PIM_REGISTER_STOP 2
|
||||||
|
#define PIM_JOIN_PRUNE 3
|
||||||
|
#define PIM_RP_REACHABLE 4
|
||||||
|
#define PIM_ASSERT 5
|
||||||
|
#define PIM_GRAFT 6
|
||||||
|
#define PIM_GRAFT_ACK 7
|
||||||
|
|
||||||
|
static char *packet_kind(type, code)
|
||||||
|
u_char type, code;
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query ";
|
||||||
|
case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report ";
|
||||||
|
case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "new membership report ";
|
||||||
|
case IGMP_HOST_LEAVE_MESSAGE: return "leave message";
|
||||||
|
case IGMP_DVMRP:
|
||||||
|
switch (code) {
|
||||||
|
case DVMRP_PROBE: return "neighbor probe ";
|
||||||
|
case DVMRP_REPORT: return "route report ";
|
||||||
|
case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
|
||||||
|
case DVMRP_NEIGHBORS: return "neighbor list ";
|
||||||
|
case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
|
||||||
|
case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
|
||||||
|
case DVMRP_PRUNE: return "prune message ";
|
||||||
|
case DVMRP_GRAFT: return "graft message ";
|
||||||
|
case DVMRP_GRAFT_ACK: return "graft message ack ";
|
||||||
|
default: return "unknown DVMRP msg ";
|
||||||
|
}
|
||||||
|
case IGMP_PIM: /* %%% hack for PIM %%% */
|
||||||
|
switch (code) {
|
||||||
|
case PIM_QUERY: return "PIM Router-Query ";
|
||||||
|
case PIM_REGISTER: return "PIM Register ";
|
||||||
|
case PIM_REGISTER_STOP: return "PIM Register-Stop ";
|
||||||
|
case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
|
||||||
|
case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
|
||||||
|
case PIM_ASSERT: return "PIM Assert ";
|
||||||
|
case PIM_GRAFT: return "PIM Graft ";
|
||||||
|
case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
|
||||||
|
default: return "unknown PIM msg ";
|
||||||
|
}
|
||||||
|
case IGMP_MTRACE: return "IGMP trace query ";
|
||||||
|
case IGMP_MTRACE_RESP: return "IGMP trace reply ";
|
||||||
|
default: return "unknown IGMP msg ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process a newly received IGMP packet that is sitting in the input
|
||||||
|
* packet buffer.
|
||||||
|
*/
|
||||||
|
void accept_igmp(recvlen)
|
||||||
|
int recvlen;
|
||||||
|
{
|
||||||
|
register vifi_t vifi;
|
||||||
|
register u_long src, dst, group;
|
||||||
|
struct ip *ip;
|
||||||
|
struct igmp *igmp;
|
||||||
|
int ipdatalen, iphdrlen, igmpdatalen;
|
||||||
|
|
||||||
|
if (recvlen < sizeof(struct ip)) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"received packet too short (%u bytes) for IP header", recvlen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = (struct ip *)recv_buf;
|
||||||
|
src = ip->ip_src.s_addr;
|
||||||
|
dst = ip->ip_dst.s_addr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this is most likely a message from the kernel indicating that
|
||||||
|
* a new src grp pair message has arrived and so, it would be
|
||||||
|
* necessary to install a route into the kernel for this.
|
||||||
|
*/
|
||||||
|
if (ip->ip_p == 0) {
|
||||||
|
if (src == NULL || dst == NULL)
|
||||||
|
log(LOG_WARNING, 0, "kernel request not accurate");
|
||||||
|
else
|
||||||
|
add_table_entry(src, dst);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iphdrlen = ip->ip_hl << 2;
|
||||||
|
ipdatalen = ip->ip_len;
|
||||||
|
if (iphdrlen + ipdatalen != recvlen) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"received packet shorter (%u bytes) than hdr+data length (%u+%u)",
|
||||||
|
recvlen, iphdrlen, ipdatalen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
igmp = (struct igmp *)(recv_buf + iphdrlen);
|
||||||
|
group = igmp->igmp_group.s_addr;
|
||||||
|
igmpdatalen = ipdatalen - IGMP_MINLEN;
|
||||||
|
if (igmpdatalen < 0) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"received IP data field too short (%u bytes) for IGMP, from %s",
|
||||||
|
ipdatalen, inet_fmt(src, s1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
|
||||||
|
packet_kind(igmp->igmp_type, igmp->igmp_code),
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
|
||||||
|
switch (igmp->igmp_type) {
|
||||||
|
|
||||||
|
case IGMP_HOST_MEMBERSHIP_QUERY:
|
||||||
|
/* we have to do the determination of the querrier router here */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case IGMP_HOST_MEMBERSHIP_REPORT:
|
||||||
|
case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
|
||||||
|
accept_group_report(src, dst, group,igmp->igmp_type);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case IGMP_HOST_LEAVE_MESSAGE:
|
||||||
|
leave_group_message(src, dst, group);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case IGMP_DVMRP:
|
||||||
|
switch (igmp->igmp_code) {
|
||||||
|
|
||||||
|
case DVMRP_PROBE:
|
||||||
|
accept_probe(src, dst,
|
||||||
|
(char *)(igmp+1), igmpdatalen, group);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_REPORT:
|
||||||
|
accept_report(src, dst,
|
||||||
|
(char *)(igmp+1), igmpdatalen, group);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_ASK_NEIGHBORS:
|
||||||
|
accept_neighbor_request(src, dst);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_ASK_NEIGHBORS2:
|
||||||
|
accept_neighbor_request2(src, dst);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_NEIGHBORS:
|
||||||
|
accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen,
|
||||||
|
group);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_NEIGHBORS2:
|
||||||
|
accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen,
|
||||||
|
group);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_PRUNE:
|
||||||
|
accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_GRAFT:
|
||||||
|
accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DVMRP_GRAFT_ACK:
|
||||||
|
accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log(LOG_INFO, 0,
|
||||||
|
"ignoring unknown DVMRP message code %u from %s to %s",
|
||||||
|
igmp->igmp_code, inet_fmt(src, s1),
|
||||||
|
inet_fmt(dst, s2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case IGMP_PIM: /* %%% hack for PIM %%% */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case IGMP_MTRACE:
|
||||||
|
mtrace(src, dst, group, (char *)(igmp+1),
|
||||||
|
igmp->igmp_code, igmpdatalen);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log(LOG_INFO, 0,
|
||||||
|
"ignoring unknown IGMP message type %u from %s to %s",
|
||||||
|
igmp->igmp_type, inet_fmt(src, s1),
|
||||||
|
inet_fmt(dst, s2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct an IGMP message in the output packet buffer. The caller may
|
||||||
|
* have already placed data in that buffer, of length 'datalen'. Then send
|
||||||
|
* the message from the interface with IP address 'src' to destination 'dst'.
|
||||||
|
*/
|
||||||
|
void send_igmp(src, dst, type, code, group, datalen)
|
||||||
|
u_long src, dst;
|
||||||
|
int type, code;
|
||||||
|
u_long group;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
static struct sockaddr_in sdst = {AF_INET};
|
||||||
|
struct ip *ip;
|
||||||
|
struct igmp *igmp;
|
||||||
|
|
||||||
|
ip = (struct ip *)send_buf;
|
||||||
|
ip->ip_src.s_addr = src;
|
||||||
|
ip->ip_dst.s_addr = dst;
|
||||||
|
ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
|
||||||
|
|
||||||
|
igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
|
||||||
|
igmp->igmp_type = type;
|
||||||
|
igmp->igmp_code = code;
|
||||||
|
igmp->igmp_group.s_addr = group;
|
||||||
|
igmp->igmp_cksum = 0;
|
||||||
|
igmp->igmp_cksum = inet_cksum((u_short *)igmp,
|
||||||
|
IGMP_MINLEN + datalen);
|
||||||
|
|
||||||
|
if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
|
||||||
|
if (dst == allhosts_group) k_set_loop(TRUE);
|
||||||
|
|
||||||
|
sdst.sin_addr.s_addr = dst;
|
||||||
|
if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
|
||||||
|
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
|
||||||
|
if (errno == ENETDOWN) check_vif_state();
|
||||||
|
else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst == allhosts_group) k_set_loop(FALSE);
|
||||||
|
|
||||||
|
log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
|
||||||
|
packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: inet.c,v 1.4 1993/05/30 01:36:38 deering Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exported variables.
|
||||||
|
*/
|
||||||
|
char s1[16]; /* buffers to hold the string representations */
|
||||||
|
char s2[16]; /* of IP addresses, to be passed to inet_fmt() */
|
||||||
|
char s3[16]; /* or inet_fmts(). */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify that a given IP address is credible as a host address.
|
||||||
|
* (Without a mask, cannot detect addresses of the form {subnet,0} or
|
||||||
|
* {subnet,-1}.)
|
||||||
|
*/
|
||||||
|
int inet_valid_host(naddr)
|
||||||
|
u_long naddr;
|
||||||
|
{
|
||||||
|
register u_long addr;
|
||||||
|
|
||||||
|
addr = ntohl(naddr);
|
||||||
|
|
||||||
|
return (!(IN_MULTICAST(addr) ||
|
||||||
|
IN_BADCLASS (addr) ||
|
||||||
|
(addr & 0xff000000) == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify that a given subnet number and mask pair are credible.
|
||||||
|
*/
|
||||||
|
int inet_valid_subnet(nsubnet, nmask)
|
||||||
|
u_long nsubnet, nmask;
|
||||||
|
{
|
||||||
|
register u_long subnet, mask;
|
||||||
|
|
||||||
|
subnet = ntohl(nsubnet);
|
||||||
|
mask = ntohl(nmask);
|
||||||
|
|
||||||
|
if ((subnet & mask) != subnet) return (FALSE);
|
||||||
|
|
||||||
|
if (IN_CLASSA(subnet)) {
|
||||||
|
if (mask < 0xff000000 ||
|
||||||
|
(subnet & 0xff000000) == 0 ||
|
||||||
|
(subnet & 0xff000000) == 0x7f000000) return (FALSE);
|
||||||
|
}
|
||||||
|
else if (IN_CLASSB(subnet)) {
|
||||||
|
if (mask < 0xffff0000) return (FALSE);
|
||||||
|
}
|
||||||
|
else if (IN_CLASSC(subnet)) {
|
||||||
|
if (mask < 0xffffff00) return (FALSE);
|
||||||
|
}
|
||||||
|
else return (FALSE);
|
||||||
|
|
||||||
|
return (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an IP address in u_long (network) format into a printable string.
|
||||||
|
*/
|
||||||
|
char *inet_fmt(addr, s)
|
||||||
|
u_long addr;
|
||||||
|
char *s;
|
||||||
|
{
|
||||||
|
register u_char *a;
|
||||||
|
|
||||||
|
a = (u_char *)&addr;
|
||||||
|
sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an IP subnet number in u_long (network) format into a printable
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
char *inet_fmts(addr, mask, s)
|
||||||
|
u_long addr, mask;
|
||||||
|
char *s;
|
||||||
|
{
|
||||||
|
register u_char *a, *m;
|
||||||
|
|
||||||
|
a = (u_char *)&addr;
|
||||||
|
m = (u_char *)&mask;
|
||||||
|
|
||||||
|
if (m[3] != 0) sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
|
||||||
|
else if (m[2] != 0) sprintf(s, "%u.%u.%u", a[0], a[1], a[2]);
|
||||||
|
else if (m[1] != 0) sprintf(s, "%u.%u", a[0], a[1]);
|
||||||
|
else sprintf(s, "%u", a[0]);
|
||||||
|
|
||||||
|
return (s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the printable string representation of an IP address into the
|
||||||
|
* u_long (network) format. Return 0xffffffff on error. (To detect the
|
||||||
|
* legal address with that value, you must explicitly compare the string
|
||||||
|
* with "255.255.255.255".)
|
||||||
|
*/
|
||||||
|
u_long inet_parse(s)
|
||||||
|
char *s;
|
||||||
|
{
|
||||||
|
u_long a;
|
||||||
|
u_int a0, a1, a2, a3;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c) != 4 ||
|
||||||
|
a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
|
||||||
|
return (0xffffffff);
|
||||||
|
|
||||||
|
((u_char *)&a)[0] = a0;
|
||||||
|
((u_char *)&a)[1] = a1;
|
||||||
|
((u_char *)&a)[2] = a2;
|
||||||
|
((u_char *)&a)[3] = a3;
|
||||||
|
|
||||||
|
return (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inet_cksum extracted from:
|
||||||
|
* P I N G . C
|
||||||
|
*
|
||||||
|
* Author -
|
||||||
|
* Mike Muuss
|
||||||
|
* U. S. Army Ballistic Research Laboratory
|
||||||
|
* December, 1983
|
||||||
|
* Modified at Uc Berkeley
|
||||||
|
*
|
||||||
|
* (ping.c) Status -
|
||||||
|
* Public Domain. Distribution Unlimited.
|
||||||
|
*
|
||||||
|
* I N _ C K S U M
|
||||||
|
*
|
||||||
|
* Checksum routine for Internet Protocol family headers (C Version)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int inet_cksum(addr, len)
|
||||||
|
u_short *addr;
|
||||||
|
u_int len;
|
||||||
|
{
|
||||||
|
register int nleft = (int)len;
|
||||||
|
register u_short *w = addr;
|
||||||
|
u_short answer = 0;
|
||||||
|
register int sum = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Our algorithm is simple, using a 32 bit accumulator (sum),
|
||||||
|
* we add sequential 16 bit words to it, and at the end, fold
|
||||||
|
* back all the carry bits from the top 16 bits into the lower
|
||||||
|
* 16 bits.
|
||||||
|
*/
|
||||||
|
while( nleft > 1 ) {
|
||||||
|
sum += *w++;
|
||||||
|
nleft -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mop up an odd byte, if necessary */
|
||||||
|
if( nleft == 1 ) {
|
||||||
|
*(u_char *) (&answer) = *(u_char *)w ;
|
||||||
|
sum += answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add back carry outs from top 16 bits to low 16 bits
|
||||||
|
*/
|
||||||
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
||||||
|
sum += (sum >> 16); /* add carry */
|
||||||
|
answer = ~sum; /* truncate to 16 bits */
|
||||||
|
return (answer);
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: kern.c,v 1.6 1994/08/24 23:53:37 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
|
||||||
|
void k_set_rcvbuf(bufsize)
|
||||||
|
int bufsize;
|
||||||
|
{
|
||||||
|
if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
|
||||||
|
(char *)&bufsize, sizeof(bufsize)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_hdr_include(bool)
|
||||||
|
int bool;
|
||||||
|
{
|
||||||
|
#ifdef IP_HDRINCL
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
|
||||||
|
(char *)&bool, sizeof(bool)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_set_ttl(t)
|
||||||
|
int t;
|
||||||
|
{
|
||||||
|
u_char ttl;
|
||||||
|
|
||||||
|
ttl = t;
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||||
|
(char *)&ttl, sizeof(ttl)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_set_loop(l)
|
||||||
|
int l;
|
||||||
|
{
|
||||||
|
u_char loop;
|
||||||
|
|
||||||
|
loop = l;
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||||
|
(char *)&loop, sizeof(loop)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_set_if(ifa)
|
||||||
|
u_long ifa;
|
||||||
|
{
|
||||||
|
struct in_addr adr;
|
||||||
|
|
||||||
|
adr.s_addr = ifa;
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
|
||||||
|
(char *)&adr, sizeof(adr)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
|
||||||
|
inet_fmt(ifa, s1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_join(grp, ifa)
|
||||||
|
u_long grp;
|
||||||
|
u_long ifa;
|
||||||
|
{
|
||||||
|
struct ip_mreq mreq;
|
||||||
|
|
||||||
|
mreq.imr_multiaddr.s_addr = grp;
|
||||||
|
mreq.imr_interface.s_addr = ifa;
|
||||||
|
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||||
|
(char *)&mreq, sizeof(mreq)) < 0)
|
||||||
|
log(LOG_WARNING, errno, "can't join group %s on interface %s",
|
||||||
|
inet_fmt(grp, s1), inet_fmt(ifa, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_leave(grp, ifa)
|
||||||
|
u_long grp;
|
||||||
|
u_long ifa;
|
||||||
|
{
|
||||||
|
struct ip_mreq mreq;
|
||||||
|
|
||||||
|
mreq.imr_multiaddr.s_addr = grp;
|
||||||
|
mreq.imr_interface.s_addr = ifa;
|
||||||
|
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
|
||||||
|
(char *)&mreq, sizeof(mreq)) < 0)
|
||||||
|
log(LOG_WARNING, errno, "can't leave group %s on interface %s",
|
||||||
|
inet_fmt(grp, s1), inet_fmt(ifa, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_init_dvmrp()
|
||||||
|
{
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_INIT,
|
||||||
|
(char *)NULL, 0) < 0)
|
||||||
|
log(LOG_ERR, errno, "can't enable DVMRP routing in kernel");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_stop_dvmrp()
|
||||||
|
{
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DONE,
|
||||||
|
(char *)NULL, 0) < 0)
|
||||||
|
log(LOG_WARNING, errno, "can't disable DVMRP routing in kernel");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_add_vif(vifi, v)
|
||||||
|
vifi_t vifi;
|
||||||
|
struct uvif *v;
|
||||||
|
{
|
||||||
|
struct vifctl vc;
|
||||||
|
|
||||||
|
vc.vifc_vifi = vifi;
|
||||||
|
vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
|
||||||
|
vc.vifc_threshold = v->uv_threshold;
|
||||||
|
vc.vifc_rate_limit = v->uv_rate_limit;
|
||||||
|
vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr;
|
||||||
|
vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
|
||||||
|
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_VIF,
|
||||||
|
(char *)&vc, sizeof(vc)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt DVMRP_ADD_VIF");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void k_del_vif(vifi)
|
||||||
|
vifi_t vifi;
|
||||||
|
{
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_VIF,
|
||||||
|
(char *)&vifi, sizeof(vifi)) < 0)
|
||||||
|
log(LOG_ERR, errno, "setsockopt DVMRP_DEL_VIF");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds a (source, mcastgrp) entry to the kernel
|
||||||
|
*/
|
||||||
|
void k_add_rg(kt)
|
||||||
|
struct ktable *kt;
|
||||||
|
{
|
||||||
|
struct mfcctl mc;
|
||||||
|
|
||||||
|
/* copy table values so that setsockopt can process it */
|
||||||
|
COPY_TABLES(kt, mc);
|
||||||
|
|
||||||
|
/* write to kernel space */
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_ADD_MFC,
|
||||||
|
(char *)&mc, sizeof(mc)) < 0)
|
||||||
|
log(LOG_WARNING, errno, "setsockopt DVMRP_ADD_MFC");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deletes a (source, mcastgrp) entry from the kernel
|
||||||
|
*/
|
||||||
|
void k_del_rg(kt)
|
||||||
|
struct ktable *kt;
|
||||||
|
{
|
||||||
|
struct mfcctl mc;
|
||||||
|
|
||||||
|
/* copy table values so that setsockopt can process it */
|
||||||
|
COPY_TABLES(kt, mc);
|
||||||
|
|
||||||
|
/* write to kernel space */
|
||||||
|
if (setsockopt(igmp_socket, IPPROTO_IP, DVMRP_DEL_MFC,
|
||||||
|
(char *)&mc, sizeof(mc)) < 0)
|
||||||
|
log(LOG_WARNING, errno, "setsockopt DVMRP_DEL_MFC");
|
||||||
|
}
|
|
@ -0,0 +1,439 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: main.c,v 1.8 1994/08/24 23:53:42 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Written by Steve Deering, Stanford University, February 1989.
|
||||||
|
*
|
||||||
|
* (An earlier version of DVMRP was implemented by David Waitzman of
|
||||||
|
* BBN STC by extending Berkeley's routed program. Some of Waitzman's
|
||||||
|
* extensions have been incorporated into mrouted, but none of the
|
||||||
|
* original routed code has been adopted.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
extern char *configfilename;
|
||||||
|
|
||||||
|
static char pidfilename[] = "/etc/mrouted.pid";
|
||||||
|
static char dumpfilename[] = "/usr/tmp/mrouted.dump";
|
||||||
|
static char cachefilename[] = "/usr/tmp/mrouted.cache";
|
||||||
|
static char genidfilename[] = "/usr/tmp/mrouted.genid";
|
||||||
|
|
||||||
|
int cache_lifetime = DEFAULT_CACHE_LIFETIME;
|
||||||
|
int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2;
|
||||||
|
|
||||||
|
int debug = 0;
|
||||||
|
u_char pruning = 1; /* Enable pruning by default */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forward declarations.
|
||||||
|
*/
|
||||||
|
static void fasttimer();
|
||||||
|
static void timer();
|
||||||
|
static void hup();
|
||||||
|
static void dump();
|
||||||
|
static void fdump();
|
||||||
|
static void cdump();
|
||||||
|
static void restart();
|
||||||
|
|
||||||
|
main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
register int recvlen;
|
||||||
|
register int omask;
|
||||||
|
int dummy;
|
||||||
|
FILE *fp;
|
||||||
|
extern uid_t geteuid();
|
||||||
|
struct timeval tv;
|
||||||
|
struct timezone tzp;
|
||||||
|
u_long prev_genid;
|
||||||
|
|
||||||
|
setlinebuf(stderr);
|
||||||
|
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
fprintf(stderr, "must be root\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
argv++, argc--;
|
||||||
|
while (argc > 0 && *argv[0] == '-') {
|
||||||
|
if (strcmp(*argv, "-d") == 0) {
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
debug = atoi(*argv);
|
||||||
|
} else
|
||||||
|
debug = DEFAULT_DEBUG;
|
||||||
|
} else if (strcmp(*argv, "-c") == 0) {
|
||||||
|
if (argc > 1) {
|
||||||
|
argv++, argc--;
|
||||||
|
configfilename = *argv;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
} else if (strcmp(*argv, "-p") == 0) {
|
||||||
|
pruning = 0;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
argv++, argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
usage: fprintf(stderr,
|
||||||
|
"usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug == 0) {
|
||||||
|
/*
|
||||||
|
* Detach from the terminal
|
||||||
|
*/
|
||||||
|
int t;
|
||||||
|
|
||||||
|
if (fork()) exit(0);
|
||||||
|
(void)close(0);
|
||||||
|
(void)close(1);
|
||||||
|
(void)close(2);
|
||||||
|
(void)open("/", 0);
|
||||||
|
(void)dup2(0, 1);
|
||||||
|
(void)dup2(0, 2);
|
||||||
|
t = open("/dev/tty", 2);
|
||||||
|
if (t >= 0) {
|
||||||
|
(void)ioctl(t, TIOCNOTTY, (char *)0);
|
||||||
|
(void)close(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "debug level %u\n", debug);
|
||||||
|
|
||||||
|
#ifdef LOG_DAEMON
|
||||||
|
(void)openlog("mrouted", LOG_PID, LOG_DAEMON);
|
||||||
|
(void)setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||||
|
#else
|
||||||
|
(void)openlog("mrouted", LOG_PID);
|
||||||
|
#endif
|
||||||
|
log(LOG_NOTICE, 0, "mrouted version %d.%d",
|
||||||
|
PROTOCOL_VERSION, MROUTED_VERSION);
|
||||||
|
|
||||||
|
srandom(gethostid());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get generation id
|
||||||
|
*/
|
||||||
|
gettimeofday(&tv, &tzp);
|
||||||
|
dvmrp_genid = tv.tv_sec;
|
||||||
|
|
||||||
|
fp = fopen(genidfilename, "r");
|
||||||
|
if (fp != NULL) {
|
||||||
|
fscanf(fp, "%d", &prev_genid);
|
||||||
|
if (prev_genid == dvmrp_genid)
|
||||||
|
dvmrp_genid++;
|
||||||
|
(void) fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(genidfilename, "w");
|
||||||
|
if (fp != NULL) {
|
||||||
|
fprintf(fp, "%d", dvmrp_genid);
|
||||||
|
(void) fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
callout_init();
|
||||||
|
init_igmp();
|
||||||
|
k_init_dvmrp(); /* enable DVMRP routing in kernel */
|
||||||
|
init_routes();
|
||||||
|
init_ktable();
|
||||||
|
init_vifs();
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
|
||||||
|
|
||||||
|
fp = fopen(pidfilename, "w");
|
||||||
|
if (fp != NULL) {
|
||||||
|
fprintf(fp, "%d\n", getpid());
|
||||||
|
(void) fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug >= 2) dump();
|
||||||
|
|
||||||
|
(void)signal(SIGALRM, fasttimer);
|
||||||
|
|
||||||
|
(void)signal(SIGHUP, restart);
|
||||||
|
(void)signal(SIGTERM, hup);
|
||||||
|
(void)signal(SIGINT, hup);
|
||||||
|
(void)signal(SIGUSR1, fdump);
|
||||||
|
(void)signal(SIGUSR2, cdump);
|
||||||
|
if (debug != 0)
|
||||||
|
(void)signal(SIGQUIT, dump);
|
||||||
|
|
||||||
|
(void)alarm(1); /* schedule first timer interrupt */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main receive loop.
|
||||||
|
*/
|
||||||
|
dummy = 0;
|
||||||
|
for(;;) {
|
||||||
|
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
|
||||||
|
0, NULL, &dummy);
|
||||||
|
if (recvlen < 0) {
|
||||||
|
if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
omask = sigblock(sigmask(SIGALRM));
|
||||||
|
accept_igmp(recvlen);
|
||||||
|
(void)sigsetmask(omask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* routine invoked every second. It's main goal is to cycle through
|
||||||
|
* the routing table and send partial updates to all neighbors at a
|
||||||
|
* rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
|
||||||
|
* seconds. Also, every TIMER_INTERVAL seconds it calls timer() to
|
||||||
|
* do all the other time-based processing.
|
||||||
|
*/
|
||||||
|
static void fasttimer()
|
||||||
|
{
|
||||||
|
static unsigned int tlast;
|
||||||
|
static unsigned int nsent;
|
||||||
|
register unsigned int t = tlast + 1;
|
||||||
|
register int n;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we're in the last second, send everything that's left.
|
||||||
|
* otherwise send at least the fraction we should have sent by now.
|
||||||
|
*/
|
||||||
|
if (t >= ROUTE_REPORT_INTERVAL) {
|
||||||
|
register int nleft = nroutes - nsent;
|
||||||
|
while (nleft > 0) {
|
||||||
|
if ((n = report_next_chunk()) <= 0)
|
||||||
|
break;
|
||||||
|
nleft -= n;
|
||||||
|
}
|
||||||
|
tlast = 0;
|
||||||
|
nsent = 0;
|
||||||
|
} else {
|
||||||
|
register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
|
||||||
|
while (nsent < ncum) {
|
||||||
|
if ((n = report_next_chunk()) <= 0)
|
||||||
|
break;
|
||||||
|
nsent += n;
|
||||||
|
}
|
||||||
|
tlast = t;
|
||||||
|
}
|
||||||
|
if ((t % TIMER_INTERVAL) == 0)
|
||||||
|
timer();
|
||||||
|
|
||||||
|
age_callout_queue();/* Advance the timer for the callout queue
|
||||||
|
for groups */
|
||||||
|
alarm(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The 'virtual_time' variable is initialized to a value that will cause the
|
||||||
|
* first invocation of timer() to send a probe or route report to all vifs
|
||||||
|
* and send group membership queries to all subnets for which this router is
|
||||||
|
* querier. This first invocation occurs approximately TIMER_INTERVAL seconds
|
||||||
|
* after the router starts up. Note that probes for neighbors and queries
|
||||||
|
* for group memberships are also sent at start-up time, as part of initial-
|
||||||
|
* ization. This repetition after a short interval is desirable for quickly
|
||||||
|
* building up topology and membership information in the presence of possible
|
||||||
|
* packet loss.
|
||||||
|
*
|
||||||
|
* 'virtual_time' advances at a rate that is only a crude approximation of
|
||||||
|
* real time, because it does not take into account any time spent processing,
|
||||||
|
* and because the timer intervals are sometimes shrunk by a random amount to
|
||||||
|
* avoid unwanted synchronization with other routers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static u_long virtual_time = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timer routine. Performs periodic neighbor probing, route reporting, and
|
||||||
|
* group querying duties, and drives various timers in routing entries and
|
||||||
|
* virtual interface data structures.
|
||||||
|
*/
|
||||||
|
static void timer()
|
||||||
|
{
|
||||||
|
age_routes(); /* Advance the timers in the route entries */
|
||||||
|
age_vifs(); /* Advance the timers for neighbors */
|
||||||
|
age_table_entry(); /* Advance the timers for the cache entries */
|
||||||
|
|
||||||
|
if (virtual_time % GROUP_QUERY_INTERVAL == 0) {
|
||||||
|
/*
|
||||||
|
* Time to query the local group memberships on all subnets
|
||||||
|
* for which this router is the elected querier.
|
||||||
|
*/
|
||||||
|
query_groups();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
|
||||||
|
/*
|
||||||
|
* Time to send a probe on all vifs from which no neighbors have
|
||||||
|
* been heard. Also, check if any inoperative interfaces have now
|
||||||
|
* come up. (If they have, they will also be probed as part of
|
||||||
|
* their initialization.)
|
||||||
|
*/
|
||||||
|
probe_for_neighbors();
|
||||||
|
|
||||||
|
if (vifs_down)
|
||||||
|
check_vif_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_change_reports = FALSE;
|
||||||
|
if (routes_changed) {
|
||||||
|
/*
|
||||||
|
* Some routes have changed since the last timer interrupt, but
|
||||||
|
* have not been reported yet. Report the changed routes to all
|
||||||
|
* neighbors.
|
||||||
|
*/
|
||||||
|
report_to_all_neighbors(CHANGED_ROUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Advance virtual time
|
||||||
|
*/
|
||||||
|
virtual_time += TIMER_INTERVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On hangup signal, let everyone know we're going away.
|
||||||
|
*/
|
||||||
|
static void hup()
|
||||||
|
{
|
||||||
|
log(LOG_INFO, 0, "hup");
|
||||||
|
expire_all_routes();
|
||||||
|
report_to_all_neighbors(ALL_ROUTES);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dump internal data structures to stderr.
|
||||||
|
*/
|
||||||
|
static void dump()
|
||||||
|
{
|
||||||
|
dump_vifs(stderr);
|
||||||
|
dump_routes(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dump internal data structures to a file.
|
||||||
|
*/
|
||||||
|
static void fdump()
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(dumpfilename, "w");
|
||||||
|
if (fp != NULL) {
|
||||||
|
dump_vifs(fp);
|
||||||
|
dump_routes(fp);
|
||||||
|
(void) fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dump local cache contents to a file.
|
||||||
|
*/
|
||||||
|
static void cdump()
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(cachefilename, "w");
|
||||||
|
if (fp != NULL) {
|
||||||
|
dump_cache(fp);
|
||||||
|
(void) fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restart mrouted
|
||||||
|
*/
|
||||||
|
static void restart()
|
||||||
|
{
|
||||||
|
register int omask;
|
||||||
|
|
||||||
|
log(LOG_INFO, 0, "restart");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reset all the entries
|
||||||
|
*/
|
||||||
|
omask = sigblock(sigmask(SIGALRM));
|
||||||
|
free_all_prunes();
|
||||||
|
free_all_routes();
|
||||||
|
stop_all_vifs();
|
||||||
|
k_stop_dvmrp();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* start processing again
|
||||||
|
*/
|
||||||
|
dvmrp_genid++;
|
||||||
|
pruning = 1;
|
||||||
|
|
||||||
|
init_igmp();
|
||||||
|
k_init_dvmrp(); /* enable DVMRP routing in kernel */
|
||||||
|
init_routes();
|
||||||
|
init_ktable();
|
||||||
|
init_vifs();
|
||||||
|
|
||||||
|
(void)sigsetmask(omask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log errors and other messages to the system log daemon and to stderr,
|
||||||
|
* according to the severity of the message and the current debug level.
|
||||||
|
* For errors of severity LOG_ERR or worse, terminate the program.
|
||||||
|
*/
|
||||||
|
void log(severity, syserr, format, a, b, c, d, e)
|
||||||
|
int severity, syserr;
|
||||||
|
char *format;
|
||||||
|
int a, b, c, d, e;
|
||||||
|
{
|
||||||
|
char fmt[100];
|
||||||
|
|
||||||
|
switch (debug) {
|
||||||
|
case 0: break;
|
||||||
|
case 1: if (severity > LOG_NOTICE) break;
|
||||||
|
case 2: if (severity > LOG_INFO ) break;
|
||||||
|
default:
|
||||||
|
fmt[0] = '\0';
|
||||||
|
if (severity == LOG_WARNING) strcat(fmt, "warning - ");
|
||||||
|
strncat(fmt, format, 80);
|
||||||
|
fprintf(stderr, fmt, a, b, c, d, e);
|
||||||
|
if (syserr == 0)
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
else if(syserr < sys_nerr)
|
||||||
|
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
|
||||||
|
else
|
||||||
|
fprintf(stderr, ": errno %d\n", syserr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (severity <= LOG_NOTICE) {
|
||||||
|
fmt[0] = '\0';
|
||||||
|
if (severity == LOG_WARNING) strcat(fmt, "warning - ");
|
||||||
|
strncat(fmt, format, 80);
|
||||||
|
if (syserr != 0) {
|
||||||
|
strcat(fmt, ": %m");
|
||||||
|
errno = syserr;
|
||||||
|
}
|
||||||
|
syslog(severity, fmt, a, b, c, d, e);
|
||||||
|
|
||||||
|
if (severity <= LOG_ERR) exit(-1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,953 @@
|
||||||
|
/* Mapper for connections between MRouteD multicast routers.
|
||||||
|
* Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
|
||||||
|
*
|
||||||
|
* $Id: mapper.c,v 1.8 1994/08/24 23:53:54 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) Xerox Corporation 1992. All rights reserved.
|
||||||
|
*
|
||||||
|
* License is granted to copy, to use, and to make and to use derivative
|
||||||
|
* works for research and evaluation purposes, provided that Xerox is
|
||||||
|
* acknowledged in all documentation pertaining to any such copy or derivative
|
||||||
|
* work. Xerox grants no other licenses expressed or implied. The Xerox trade
|
||||||
|
* name should not be used in any advertising without its written permission.
|
||||||
|
*
|
||||||
|
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
|
||||||
|
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
|
||||||
|
* FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
|
||||||
|
* express or implied warranty of any kind.
|
||||||
|
*
|
||||||
|
* These notices must be retained in any copies of any part of this software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
|
||||||
|
#define DEFAULT_RETRIES 1 /* How many times to ask each router */
|
||||||
|
|
||||||
|
|
||||||
|
/* All IP addresses are stored in the data structure in NET order. */
|
||||||
|
|
||||||
|
typedef struct neighbor {
|
||||||
|
struct neighbor *next;
|
||||||
|
u_long addr; /* IP address in NET order */
|
||||||
|
u_char metric; /* TTL cost of forwarding */
|
||||||
|
u_char threshold; /* TTL threshold to forward */
|
||||||
|
u_short flags; /* flags on connection */
|
||||||
|
#define NF_PRESENT 0x8000 /* True if flags are meaningful */
|
||||||
|
} Neighbor;
|
||||||
|
|
||||||
|
typedef struct interface {
|
||||||
|
struct interface *next;
|
||||||
|
u_long addr; /* IP address of the interface in NET order */
|
||||||
|
Neighbor *neighbors; /* List of neighbors' IP addresses */
|
||||||
|
} Interface;
|
||||||
|
|
||||||
|
typedef struct node {
|
||||||
|
u_long addr; /* IP address of this entry in NET order */
|
||||||
|
u_long version; /* which mrouted version is running */
|
||||||
|
int tries; /* How many requests sent? -1 for aliases */
|
||||||
|
union {
|
||||||
|
struct node *alias; /* If alias, to what? */
|
||||||
|
struct interface *interfaces; /* Else, neighbor data */
|
||||||
|
} u;
|
||||||
|
struct node *left, *right;
|
||||||
|
} Node;
|
||||||
|
|
||||||
|
|
||||||
|
Node *routers = 0;
|
||||||
|
u_long our_addr, target_addr = 0; /* in NET order */
|
||||||
|
int debug = 0;
|
||||||
|
int retries = DEFAULT_RETRIES;
|
||||||
|
int timeout = DEFAULT_TIMEOUT;
|
||||||
|
int show_names = TRUE;
|
||||||
|
vifi_t numvifs; /* to keep loader happy */
|
||||||
|
/* (see COPY_TABLES macro called in kern.c) */
|
||||||
|
|
||||||
|
|
||||||
|
Node *find_node(addr, ptr)
|
||||||
|
u_long addr;
|
||||||
|
Node **ptr;
|
||||||
|
{
|
||||||
|
Node *n = *ptr;
|
||||||
|
|
||||||
|
if (!n) {
|
||||||
|
*ptr = n = (Node *) malloc(sizeof(Node));
|
||||||
|
n->addr = addr;
|
||||||
|
n->version = 0;
|
||||||
|
n->tries = 0;
|
||||||
|
n->u.interfaces = 0;
|
||||||
|
n->left = n->right = 0;
|
||||||
|
return n;
|
||||||
|
} else if (addr == n->addr)
|
||||||
|
return n;
|
||||||
|
else if (addr < n->addr)
|
||||||
|
return find_node(addr, &(n->left));
|
||||||
|
else
|
||||||
|
return find_node(addr, &(n->right));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Interface *find_interface(addr, node)
|
||||||
|
u_long addr;
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Interface *ifc;
|
||||||
|
|
||||||
|
for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
|
||||||
|
if (ifc->addr == addr)
|
||||||
|
return ifc;
|
||||||
|
|
||||||
|
ifc = (Interface *) malloc(sizeof(Interface));
|
||||||
|
ifc->addr = addr;
|
||||||
|
ifc->next = node->u.interfaces;
|
||||||
|
node->u.interfaces = ifc;
|
||||||
|
ifc->neighbors = 0;
|
||||||
|
|
||||||
|
return ifc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Neighbor *find_neighbor(addr, node)
|
||||||
|
u_long addr;
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Interface *ifc;
|
||||||
|
|
||||||
|
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
|
||||||
|
Neighbor *nb;
|
||||||
|
|
||||||
|
for (nb = ifc->neighbors; nb; nb = nb->next)
|
||||||
|
if (nb->addr == addr)
|
||||||
|
return nb;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log errors and other messages to stderr, according to the severity of the
|
||||||
|
* message and the current debug level. For errors of severity LOG_ERR or
|
||||||
|
* worse, terminate the program.
|
||||||
|
*/
|
||||||
|
void log(severity, syserr, format, a, b, c, d, e)
|
||||||
|
int severity, syserr;
|
||||||
|
char *format;
|
||||||
|
int a, b, c, d, e;
|
||||||
|
{
|
||||||
|
char fmt[100];
|
||||||
|
|
||||||
|
switch (debug) {
|
||||||
|
case 0: if (severity > LOG_WARNING) return;
|
||||||
|
case 1: if (severity > LOG_NOTICE ) return;
|
||||||
|
case 2: if (severity > LOG_INFO ) return;
|
||||||
|
default:
|
||||||
|
fmt[0] = '\0';
|
||||||
|
if (severity == LOG_WARNING)
|
||||||
|
strcat(fmt, "warning - ");
|
||||||
|
strncat(fmt, format, 80);
|
||||||
|
fprintf(stderr, fmt, a, b, c, d, e);
|
||||||
|
if (syserr == 0)
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
else if (syserr < sys_nerr)
|
||||||
|
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
|
||||||
|
else
|
||||||
|
fprintf(stderr, ": errno %d\n", syserr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (severity <= LOG_ERR)
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a neighbors-list request.
|
||||||
|
*/
|
||||||
|
void ask(dst)
|
||||||
|
u_long dst;
|
||||||
|
{
|
||||||
|
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
|
||||||
|
htonl(MROUTED_LEVEL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ask2(dst)
|
||||||
|
u_long dst;
|
||||||
|
{
|
||||||
|
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
|
||||||
|
htonl(MROUTED_LEVEL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming group membership report.
|
||||||
|
*/
|
||||||
|
void accept_group_report(src, dst, group)
|
||||||
|
u_long src, dst, group;
|
||||||
|
{
|
||||||
|
log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming neighbor probe message.
|
||||||
|
*/
|
||||||
|
void accept_probe(src, dst)
|
||||||
|
u_long src, dst;
|
||||||
|
{
|
||||||
|
log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming route report message.
|
||||||
|
*/
|
||||||
|
void accept_report(src, dst, p, datalen)
|
||||||
|
u_long src, dst;
|
||||||
|
char *p;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming neighbor-list request message.
|
||||||
|
*/
|
||||||
|
void accept_neighbor_request(src, dst)
|
||||||
|
u_long src, dst;
|
||||||
|
{
|
||||||
|
if (src != our_addr)
|
||||||
|
log(LOG_INFO, 0,
|
||||||
|
"ignoring spurious DVMRP neighbor request from %s to %s",
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_neighbor_request2(src, dst)
|
||||||
|
u_long src, dst;
|
||||||
|
{
|
||||||
|
if (src != our_addr)
|
||||||
|
log(LOG_INFO, 0,
|
||||||
|
"ignoring spurious DVMRP neighbor request2 from %s to %s",
|
||||||
|
inet_fmt(src, s1), inet_fmt(dst, s2));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming neighbor-list message.
|
||||||
|
*/
|
||||||
|
void accept_neighbors(src, dst, p, datalen, level)
|
||||||
|
u_long src, dst, level;
|
||||||
|
u_char *p;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
Node *node = find_node(src, &routers);
|
||||||
|
|
||||||
|
if (node->tries == 0) /* Never heard of 'em; must have hit them at */
|
||||||
|
node->tries = 1; /* least once, though...*/
|
||||||
|
else if (node->tries == -1) /* follow alias link */
|
||||||
|
node = node->u.alias;
|
||||||
|
|
||||||
|
#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
|
||||||
|
a += ((u_long)*p++ << 8), a += *p++)
|
||||||
|
|
||||||
|
/* if node is running a recent mrouted, ask for additional info */
|
||||||
|
if (level != 0) {
|
||||||
|
node->version = ntohl(level);
|
||||||
|
node->tries = 0;
|
||||||
|
ask2(src);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug > 3) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stderr, " datalen = %d\n", datalen);
|
||||||
|
for (i = 0; i < datalen; i++) {
|
||||||
|
if ((i & 0xF) == 0)
|
||||||
|
fprintf(stderr, " ");
|
||||||
|
fprintf(stderr, " %02x", p[i]);
|
||||||
|
if ((i & 0xF) == 0xF)
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
if ((datalen & 0xF) != 0xF)
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (datalen > 0) { /* loop through interfaces */
|
||||||
|
u_long ifc_addr;
|
||||||
|
u_char metric, threshold, ncount;
|
||||||
|
Node *ifc_node;
|
||||||
|
Interface *ifc;
|
||||||
|
Neighbor *old_neighbors;
|
||||||
|
|
||||||
|
if (datalen < 4 + 3) {
|
||||||
|
log(LOG_WARNING, 0, "received truncated interface record from %s",
|
||||||
|
inet_fmt(src, s1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_ADDR(ifc_addr);
|
||||||
|
ifc_addr = htonl(ifc_addr);
|
||||||
|
metric = *p++;
|
||||||
|
threshold = *p++;
|
||||||
|
ncount = *p++;
|
||||||
|
datalen -= 4 + 3;
|
||||||
|
|
||||||
|
/* Fix up any alias information */
|
||||||
|
ifc_node = find_node(ifc_addr, &routers);
|
||||||
|
if (ifc_node->tries == 0) { /* new node */
|
||||||
|
ifc_node->tries = -1;
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
} else if (ifc_node != node
|
||||||
|
&& (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
|
||||||
|
/* must merge two hosts' nodes */
|
||||||
|
Interface *ifc_i, *next_ifc_i;
|
||||||
|
|
||||||
|
if (ifc_node->tries == -1) {
|
||||||
|
Node *tmp = ifc_node->u.alias;
|
||||||
|
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
ifc_node = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Merge ifc_node (foo_i) into node (foo_n) */
|
||||||
|
|
||||||
|
if (ifc_node->tries > node->tries)
|
||||||
|
node->tries = ifc_node->tries;
|
||||||
|
|
||||||
|
for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
|
||||||
|
Neighbor *nb_i, *next_nb_i, *nb_n;
|
||||||
|
Interface *ifc_n = find_interface(ifc_i->addr, node);
|
||||||
|
|
||||||
|
old_neighbors = ifc_n->neighbors;
|
||||||
|
for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
|
||||||
|
next_nb_i = nb_i->next;
|
||||||
|
for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
|
||||||
|
if (nb_i->addr == nb_n->addr) {
|
||||||
|
if (nb_i->metric != nb_n->metric
|
||||||
|
|| nb_i->threshold != nb_i->threshold)
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"inconsistent %s for neighbor %s of %s",
|
||||||
|
"metric/threshold",
|
||||||
|
inet_fmt(nb_i->addr, s1),
|
||||||
|
inet_fmt(node->addr, s2));
|
||||||
|
free(nb_i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!nb_n) { /* no match for this neighbor yet */
|
||||||
|
nb_i->next = ifc_n->neighbors;
|
||||||
|
ifc_n->neighbors = nb_i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_ifc_i = ifc_i->next;
|
||||||
|
free(ifc_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc_node->tries = -1;
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc = find_interface(ifc_addr, node);
|
||||||
|
old_neighbors = ifc->neighbors;
|
||||||
|
|
||||||
|
/* Add the neighbors for this interface */
|
||||||
|
while (ncount--) {
|
||||||
|
u_long neighbor;
|
||||||
|
Neighbor *nb;
|
||||||
|
Node *n_node;
|
||||||
|
|
||||||
|
if (datalen < 4) {
|
||||||
|
log(LOG_WARNING, 0, "received truncated neighbor list from %s",
|
||||||
|
inet_fmt(src, s1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GET_ADDR(neighbor);
|
||||||
|
neighbor = htonl(neighbor);
|
||||||
|
datalen -= 4;
|
||||||
|
|
||||||
|
for (nb = old_neighbors; nb; nb = nb->next)
|
||||||
|
if (nb->addr == neighbor) {
|
||||||
|
if (metric != nb->metric || threshold != nb->threshold)
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"inconsistent %s for neighbor %s of %s",
|
||||||
|
"metric/threshold",
|
||||||
|
inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
|
||||||
|
goto next_neighbor;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb = (Neighbor *) malloc(sizeof(Neighbor));
|
||||||
|
nb->next = ifc->neighbors;
|
||||||
|
ifc->neighbors = nb;
|
||||||
|
nb->addr = neighbor;
|
||||||
|
nb->metric = metric;
|
||||||
|
nb->threshold = threshold;
|
||||||
|
nb->flags = 0;
|
||||||
|
|
||||||
|
n_node = find_node(neighbor, &routers);
|
||||||
|
if (n_node->tries == 0 && !target_addr) { /* it's a new router */
|
||||||
|
ask(neighbor);
|
||||||
|
n_node->tries = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_neighbor: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_neighbors2(src, dst, p, datalen)
|
||||||
|
u_long src, dst;
|
||||||
|
u_char *p;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
Node *node = find_node(src, &routers);
|
||||||
|
|
||||||
|
if (node->tries == 0) /* Never heard of 'em; must have hit them at */
|
||||||
|
node->tries = 1; /* least once, though...*/
|
||||||
|
else if (node->tries == -1) /* follow alias link */
|
||||||
|
node = node->u.alias;
|
||||||
|
|
||||||
|
while (datalen > 0) { /* loop through interfaces */
|
||||||
|
u_long ifc_addr;
|
||||||
|
u_char metric, threshold, ncount, flags;
|
||||||
|
Node *ifc_node;
|
||||||
|
Interface *ifc;
|
||||||
|
Neighbor *old_neighbors;
|
||||||
|
|
||||||
|
if (datalen < 4 + 4) {
|
||||||
|
log(LOG_WARNING, 0, "received truncated interface record from %s",
|
||||||
|
inet_fmt(src, s1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc_addr = *(u_long*)p;
|
||||||
|
p += 4;
|
||||||
|
metric = *p++;
|
||||||
|
threshold = *p++;
|
||||||
|
flags = *p++;
|
||||||
|
ncount = *p++;
|
||||||
|
datalen -= 4 + 4;
|
||||||
|
|
||||||
|
/* Fix up any alias information */
|
||||||
|
ifc_node = find_node(ifc_addr, &routers);
|
||||||
|
if (ifc_node->tries == 0) { /* new node */
|
||||||
|
ifc_node->tries = -1;
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
} else if (ifc_node != node
|
||||||
|
&& (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
|
||||||
|
/* must merge two hosts' nodes */
|
||||||
|
Interface *ifc_i, *next_ifc_i;
|
||||||
|
|
||||||
|
if (ifc_node->tries == -1) {
|
||||||
|
Node *tmp = ifc_node->u.alias;
|
||||||
|
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
ifc_node = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Merge ifc_node (foo_i) into node (foo_n) */
|
||||||
|
|
||||||
|
if (ifc_node->tries > node->tries)
|
||||||
|
node->tries = ifc_node->tries;
|
||||||
|
|
||||||
|
for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
|
||||||
|
Neighbor *nb_i, *next_nb_i, *nb_n;
|
||||||
|
Interface *ifc_n = find_interface(ifc_i->addr, node);
|
||||||
|
|
||||||
|
old_neighbors = ifc_n->neighbors;
|
||||||
|
for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
|
||||||
|
next_nb_i = nb_i->next;
|
||||||
|
for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
|
||||||
|
if (nb_i->addr == nb_n->addr) {
|
||||||
|
if (nb_i->metric != nb_n->metric
|
||||||
|
|| nb_i->threshold != nb_i->threshold)
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"inconsistent %s for neighbor %s of %s",
|
||||||
|
"metric/threshold",
|
||||||
|
inet_fmt(nb_i->addr, s1),
|
||||||
|
inet_fmt(node->addr, s2));
|
||||||
|
free(nb_i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!nb_n) { /* no match for this neighbor yet */
|
||||||
|
nb_i->next = ifc_n->neighbors;
|
||||||
|
ifc_n->neighbors = nb_i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_ifc_i = ifc_i->next;
|
||||||
|
free(ifc_i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc_node->tries = -1;
|
||||||
|
ifc_node->u.alias = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ifc = find_interface(ifc_addr, node);
|
||||||
|
old_neighbors = ifc->neighbors;
|
||||||
|
|
||||||
|
/* Add the neighbors for this interface */
|
||||||
|
while (ncount--) {
|
||||||
|
u_long neighbor;
|
||||||
|
Neighbor *nb;
|
||||||
|
Node *n_node;
|
||||||
|
|
||||||
|
if (datalen < 4) {
|
||||||
|
log(LOG_WARNING, 0, "received truncated neighbor list from %s",
|
||||||
|
inet_fmt(src, s1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
neighbor = *(u_long*)p;
|
||||||
|
p += 4;
|
||||||
|
datalen -= 4;
|
||||||
|
if (neighbor == 0)
|
||||||
|
/* make leaf nets point to themselves */
|
||||||
|
neighbor = ifc_addr;
|
||||||
|
|
||||||
|
for (nb = old_neighbors; nb; nb = nb->next)
|
||||||
|
if (nb->addr == neighbor) {
|
||||||
|
if (metric != nb->metric || threshold != nb->threshold)
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"inconsistent %s for neighbor %s of %s",
|
||||||
|
"metric/threshold",
|
||||||
|
inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
|
||||||
|
goto next_neighbor;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb = (Neighbor *) malloc(sizeof(Neighbor));
|
||||||
|
nb->next = ifc->neighbors;
|
||||||
|
ifc->neighbors = nb;
|
||||||
|
nb->addr = neighbor;
|
||||||
|
nb->metric = metric;
|
||||||
|
nb->threshold = threshold;
|
||||||
|
nb->flags = flags | NF_PRESENT;
|
||||||
|
|
||||||
|
n_node = find_node(neighbor, &routers);
|
||||||
|
if (n_node->tries == 0 && !target_addr) { /* it's a new router */
|
||||||
|
ask(neighbor);
|
||||||
|
n_node->tries = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_neighbor: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void check_vif_state()
|
||||||
|
{
|
||||||
|
log(LOG_NOTICE, 0, "network marked down...");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int retry_requests(node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
result = retry_requests(node->left);
|
||||||
|
if (node->tries > 0 && node->tries < retries) {
|
||||||
|
if (node->version)
|
||||||
|
ask2(node->addr);
|
||||||
|
else
|
||||||
|
ask(node->addr);
|
||||||
|
node->tries++;
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
return retry_requests(node->right) || result;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *inet_name(addr)
|
||||||
|
u_long addr;
|
||||||
|
{
|
||||||
|
struct hostent *e;
|
||||||
|
|
||||||
|
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
|
||||||
|
|
||||||
|
return e ? e->h_name : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void print_map(node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
if (node) {
|
||||||
|
char *name, *addr;
|
||||||
|
|
||||||
|
print_map(node->left);
|
||||||
|
|
||||||
|
addr = inet_fmt(node->addr, s1);
|
||||||
|
if (!target_addr
|
||||||
|
|| (node->tries >= 0 && node->u.interfaces)
|
||||||
|
|| (node->tries == -1
|
||||||
|
&& node->u.alias->tries >= 0
|
||||||
|
&& node->u.alias->u.interfaces)) {
|
||||||
|
if (show_names && (name = inet_name(node->addr)))
|
||||||
|
printf("%s (%s):", addr, name);
|
||||||
|
else
|
||||||
|
printf("%s:", addr);
|
||||||
|
if (node->tries < 0)
|
||||||
|
printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
|
||||||
|
else if (!node->u.interfaces)
|
||||||
|
printf(" no response to query\n\n");
|
||||||
|
else {
|
||||||
|
Interface *ifc;
|
||||||
|
|
||||||
|
if (node->version)
|
||||||
|
printf(" <v%d.%d>", node->version & 0xff,
|
||||||
|
(node->version >> 8) & 0xff);
|
||||||
|
printf("\n");
|
||||||
|
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
|
||||||
|
Neighbor *nb;
|
||||||
|
char *ifc_name = inet_fmt(ifc->addr, s1);
|
||||||
|
int ifc_len = strlen(ifc_name);
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
printf(" %s:", ifc_name);
|
||||||
|
for (nb = ifc->neighbors; nb; nb = nb->next) {
|
||||||
|
if (count > 0)
|
||||||
|
printf("%*s", ifc_len + 5, "");
|
||||||
|
printf(" %s", inet_fmt(nb->addr, s1));
|
||||||
|
if (show_names && (name = inet_name(nb->addr)))
|
||||||
|
printf(" (%s)", name);
|
||||||
|
printf(" [%d/%d", nb->metric, nb->threshold);
|
||||||
|
if (nb->flags) {
|
||||||
|
u_short flags = nb->flags;
|
||||||
|
if (flags & DVMRP_NF_TUNNEL)
|
||||||
|
printf("/tunnel");
|
||||||
|
if (flags & DVMRP_NF_SRCRT)
|
||||||
|
printf("/srcrt");
|
||||||
|
if (flags & DVMRP_NF_QUERIER)
|
||||||
|
printf("/querier");
|
||||||
|
if (flags & DVMRP_NF_DISABLED)
|
||||||
|
printf("/disabled");
|
||||||
|
if (flags & DVMRP_NF_DOWN)
|
||||||
|
printf("/down");
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_map(node->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *graph_name(addr, buf)
|
||||||
|
u_long addr;
|
||||||
|
char *buf;
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (show_names && (name = inet_name(addr)))
|
||||||
|
strcpy(buf, name);
|
||||||
|
else
|
||||||
|
inet_fmt(addr, buf);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void graph_edges(node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
Interface *ifc;
|
||||||
|
Neighbor *nb;
|
||||||
|
char name[100];
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
graph_edges(node->left);
|
||||||
|
if (node->tries >= 0) {
|
||||||
|
printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
|
||||||
|
(int) node->addr,
|
||||||
|
node->addr & 0xFF, (node->addr >> 8) & 0xFF,
|
||||||
|
graph_name(node->addr, name),
|
||||||
|
node->u.interfaces ? "" : "*");
|
||||||
|
for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
|
||||||
|
for (nb = ifc->neighbors; nb; nb = nb->next) {
|
||||||
|
Node *nb_node = find_node(nb->addr, &routers);
|
||||||
|
Neighbor *nb2;
|
||||||
|
|
||||||
|
if (nb_node->tries < 0)
|
||||||
|
nb_node = nb_node->u.alias;
|
||||||
|
|
||||||
|
if (node != nb_node &&
|
||||||
|
(!(nb2 = find_neighbor(node->addr, nb_node))
|
||||||
|
|| node->addr < nb_node->addr)) {
|
||||||
|
printf(" %d \"%d/%d",
|
||||||
|
nb_node->addr, nb->metric, nb->threshold);
|
||||||
|
if (nb2 && (nb2->metric != nb->metric
|
||||||
|
|| nb2->threshold != nb->threshold))
|
||||||
|
printf(",%d/%d", nb2->metric, nb2->threshold);
|
||||||
|
if (nb->flags & NF_PRESENT)
|
||||||
|
printf("%s%s",
|
||||||
|
nb->flags & DVMRP_NF_SRCRT ? "" :
|
||||||
|
nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
|
||||||
|
nb->flags & DVMRP_NF_DOWN ? "D" : "");
|
||||||
|
printf("\"\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(" ;\n");
|
||||||
|
}
|
||||||
|
graph_edges(node->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void elide_aliases(node)
|
||||||
|
Node *node;
|
||||||
|
{
|
||||||
|
if (node) {
|
||||||
|
elide_aliases(node->left);
|
||||||
|
if (node->tries >= 0) {
|
||||||
|
Interface *ifc;
|
||||||
|
|
||||||
|
for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
|
||||||
|
Neighbor *nb;
|
||||||
|
|
||||||
|
for (nb = ifc->neighbors; nb; nb = nb->next) {
|
||||||
|
Node *nb_node = find_node(nb->addr, &routers);
|
||||||
|
|
||||||
|
if (nb_node->tries < 0)
|
||||||
|
nb->addr = nb_node->u.alias->addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elide_aliases(node->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void graph_map()
|
||||||
|
{
|
||||||
|
u_long now = time(0);
|
||||||
|
char *nowstr = ctime(&now);
|
||||||
|
|
||||||
|
nowstr[24] = '\0'; /* Kill the newline at the end */
|
||||||
|
elide_aliases(routers);
|
||||||
|
printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
|
||||||
|
nowstr);
|
||||||
|
graph_edges(routers);
|
||||||
|
printf("END\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int get_number(var, deflt, pargv, pargc)
|
||||||
|
int *var, *pargc, deflt;
|
||||||
|
char ***pargv;
|
||||||
|
{
|
||||||
|
if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
|
||||||
|
if (*pargc > 1 && isdigit((*pargv)[1][0])) {
|
||||||
|
(*pargv)++, (*pargc)--;
|
||||||
|
*var = atoi((*pargv)[0]);
|
||||||
|
return 1;
|
||||||
|
} else if (deflt >= 0) {
|
||||||
|
*var = deflt;
|
||||||
|
return 1;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
} else { /* Get value from the rest of this argument */
|
||||||
|
if (isdigit((*pargv)[0][2])) {
|
||||||
|
*var = atoi((*pargv)[0] + 2);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u_long host_addr(name)
|
||||||
|
char *name;
|
||||||
|
{
|
||||||
|
struct hostent *e = gethostbyname(name);
|
||||||
|
int addr;
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
memcpy(&addr, e->h_addr_list[0], e->h_length);
|
||||||
|
else {
|
||||||
|
addr = inet_addr(name);
|
||||||
|
if (addr == -1)
|
||||||
|
addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
int flood = FALSE, graph = FALSE;
|
||||||
|
|
||||||
|
#ifdef SYSV
|
||||||
|
setvbuf(stderr, NULL, _IOLBF, 0);
|
||||||
|
#else
|
||||||
|
setlinebuf(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
fprintf(stderr, "must be root\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
argv++, argc--;
|
||||||
|
while (argc > 0 && argv[0][0] == '-') {
|
||||||
|
switch (argv[0][1]) {
|
||||||
|
case 'd':
|
||||||
|
if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
flood = TRUE;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
graph = TRUE;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
show_names = FALSE;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (!get_number(&retries, -1, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
if (!get_number(&timeout, -1, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto usage;
|
||||||
|
}
|
||||||
|
argv++, argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
usage:
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
|
||||||
|
"[-r retries] [-d [debug-level]] [router]");
|
||||||
|
fprintf(stderr, "\t-f Flood the routing graph with queries\n");
|
||||||
|
fprintf(stderr, "\t (True by default unless `router' is given)\n");
|
||||||
|
fprintf(stderr, "\t-g Generate output in GraphEd format\n");
|
||||||
|
fprintf(stderr, "\t-n Don't look up DNS names for routers\n");
|
||||||
|
exit(1);
|
||||||
|
} else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
|
||||||
|
fprintf(stderr, "Unknown host: %s\n", argv[0]);
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "Debug level %u\n", debug);
|
||||||
|
|
||||||
|
init_igmp();
|
||||||
|
|
||||||
|
{ /* Find a good local address for us. */
|
||||||
|
int udp;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int addrlen = sizeof(addr);
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = dvmrp_group;
|
||||||
|
addr.sin_port = htons(2000); /* any port over 1024 will do... */
|
||||||
|
if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
|
||||||
|
|| connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
|
||||||
|
|| getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
|
||||||
|
perror("Determining local address");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
close(udp);
|
||||||
|
our_addr = addr.sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send initial seed message to all local routers */
|
||||||
|
ask(target_addr ? target_addr : allhosts_group);
|
||||||
|
|
||||||
|
if (target_addr) {
|
||||||
|
Node *n = find_node(target_addr, &routers);
|
||||||
|
|
||||||
|
n->tries = 1;
|
||||||
|
|
||||||
|
if (flood)
|
||||||
|
target_addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main receive loop */
|
||||||
|
for(;;) {
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
int count, recvlen, dummy = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(igmp_socket, &fds);
|
||||||
|
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
|
||||||
|
|
||||||
|
if (count < 0) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
perror("select");
|
||||||
|
continue;
|
||||||
|
} else if (count == 0) {
|
||||||
|
log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
|
||||||
|
if (retry_requests(routers))
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
|
||||||
|
0, NULL, &dummy);
|
||||||
|
if (recvlen >= 0)
|
||||||
|
accept_igmp(recvlen);
|
||||||
|
else if (errno != EINTR)
|
||||||
|
perror("recvfrom");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (graph)
|
||||||
|
graph_map();
|
||||||
|
else {
|
||||||
|
if (!target_addr)
|
||||||
|
printf("Multicast Router Connectivity:\n\n");
|
||||||
|
print_map(routers);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_prune()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_graft()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_g_ack()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void add_table_entry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void leave_group_message()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void mtrace()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,480 @@
|
||||||
|
/*
|
||||||
|
* This tool requests configuration info from a multicast router
|
||||||
|
* and prints the reply (if any). Invoke it as:
|
||||||
|
*
|
||||||
|
* mrinfo router-name-or-address
|
||||||
|
*
|
||||||
|
* Written Wed Mar 24 1993 by Van Jacobson (adapted from the
|
||||||
|
* multicast mapper written by Pavel Curtis).
|
||||||
|
*
|
||||||
|
* The lawyers insist we include the following UC copyright notice.
|
||||||
|
* The mapper from which this is derived contained a Xerox copyright
|
||||||
|
* notice which follows the UC one. Try not to get depressed noting
|
||||||
|
* that the legal gibberish is larger than the program.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1993 Regents of the University of California.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the Computer Systems
|
||||||
|
* Engineering Group at Lawrence Berkeley Laboratory.
|
||||||
|
* 4. Neither the name of the University nor of the Laboratory may be used
|
||||||
|
* to endorse or promote products derived from this software without
|
||||||
|
* specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
* ---------------------------------
|
||||||
|
* Copyright (c) Xerox Corporation 1992. All rights reserved.
|
||||||
|
*
|
||||||
|
* License is granted to copy, to use, and to make and to use derivative works
|
||||||
|
* for research and evaluation purposes, provided that Xerox is acknowledged
|
||||||
|
* in all documentation pertaining to any such copy or derivative work. Xerox
|
||||||
|
* grants no other licenses expressed or implied. The Xerox trade name should
|
||||||
|
* not be used in any advertising without its written permission.
|
||||||
|
*
|
||||||
|
* XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
|
||||||
|
* MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
|
||||||
|
* ANY PARTICULAR PURPOSE. The software is provided "as is" without express
|
||||||
|
* or implied warranty of any kind.
|
||||||
|
*
|
||||||
|
* These notices must be retained in any copies of any part of this software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lint
|
||||||
|
static char rcsid[] =
|
||||||
|
"@(#) $Id: mrinfo.c,v 1.7 1994/08/24 23:54:04 thyagara Exp $";
|
||||||
|
/* original rcsid:
|
||||||
|
"@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
|
||||||
|
#define DEFAULT_RETRIES 3 /* How many times to ask each router */
|
||||||
|
|
||||||
|
u_long our_addr, target_addr = 0; /* in NET order */
|
||||||
|
int debug = 0;
|
||||||
|
int retries = DEFAULT_RETRIES;
|
||||||
|
int timeout = DEFAULT_TIMEOUT;
|
||||||
|
int target_level;
|
||||||
|
vifi_t numvifs; /* to keep loader happy */
|
||||||
|
/* (see COPY_TABLES macro called in kern.c) */
|
||||||
|
|
||||||
|
char *
|
||||||
|
inet_name(addr)
|
||||||
|
u_long addr;
|
||||||
|
{
|
||||||
|
struct hostent *e;
|
||||||
|
|
||||||
|
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
|
||||||
|
|
||||||
|
return e ? e->h_name : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log errors and other messages to stderr, according to the severity of the
|
||||||
|
* message and the current debug level. For errors of severity LOG_ERR or
|
||||||
|
* worse, terminate the program.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
log(severity, syserr, format, a, b, c, d, e)
|
||||||
|
int severity, syserr;
|
||||||
|
char *format;
|
||||||
|
int a, b, c, d, e;
|
||||||
|
{
|
||||||
|
char fmt[100];
|
||||||
|
|
||||||
|
switch (debug) {
|
||||||
|
case 0:
|
||||||
|
if (severity > LOG_WARNING)
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
if (severity > LOG_NOTICE)
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
if (severity > LOG_INFO)
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
fmt[0] = '\0';
|
||||||
|
if (severity == LOG_WARNING)
|
||||||
|
strcat(fmt, "warning - ");
|
||||||
|
strncat(fmt, format, 80);
|
||||||
|
fprintf(stderr, fmt, a, b, c, d, e);
|
||||||
|
if (syserr == 0)
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
else if (syserr < sys_nerr)
|
||||||
|
fprintf(stderr, ": %s\n", sys_errlist[syserr]);
|
||||||
|
else
|
||||||
|
fprintf(stderr, ": errno %d\n", syserr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (severity <= LOG_ERR)
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send a neighbors-list request.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ask(dst)
|
||||||
|
u_long dst;
|
||||||
|
{
|
||||||
|
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
|
||||||
|
htonl(MROUTED_LEVEL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ask2(dst)
|
||||||
|
u_long dst;
|
||||||
|
{
|
||||||
|
send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
|
||||||
|
htonl(MROUTED_LEVEL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an incoming neighbor-list message.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
accept_neighbors(src, dst, p, datalen)
|
||||||
|
u_long src, dst;
|
||||||
|
u_char *p;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
u_char *ep = p + datalen;
|
||||||
|
#define GET_ADDR(a) (a = ((u_long)*p++ << 24), a += ((u_long)*p++ << 16),\
|
||||||
|
a += ((u_long)*p++ << 8), a += *p++)
|
||||||
|
|
||||||
|
printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
|
||||||
|
while (p < ep) {
|
||||||
|
register u_long laddr;
|
||||||
|
register u_char metric;
|
||||||
|
register u_char thresh;
|
||||||
|
register int ncount;
|
||||||
|
|
||||||
|
GET_ADDR(laddr);
|
||||||
|
laddr = htonl(laddr);
|
||||||
|
metric = *p++;
|
||||||
|
thresh = *p++;
|
||||||
|
ncount = *p++;
|
||||||
|
while (--ncount >= 0) {
|
||||||
|
register u_long neighbor;
|
||||||
|
GET_ADDR(neighbor);
|
||||||
|
neighbor = htonl(neighbor);
|
||||||
|
printf(" %s -> ", inet_fmt(laddr, s1));
|
||||||
|
printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
|
||||||
|
inet_name(neighbor), metric, thresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
accept_neighbors2(src, dst, p, datalen)
|
||||||
|
u_long src, dst;
|
||||||
|
u_char *p;
|
||||||
|
int datalen;
|
||||||
|
{
|
||||||
|
u_char *ep = p + datalen;
|
||||||
|
|
||||||
|
printf("%s (%s) [version %d.%d]:\n", inet_fmt(src, s1), inet_name(src),
|
||||||
|
target_level & 0xff, (target_level >> 8) & 0xff);
|
||||||
|
while (p < ep) {
|
||||||
|
register u_char metric;
|
||||||
|
register u_char thresh;
|
||||||
|
register u_char flags;
|
||||||
|
register int ncount;
|
||||||
|
register u_long laddr = *(u_long*)p;
|
||||||
|
|
||||||
|
p += 4;
|
||||||
|
metric = *p++;
|
||||||
|
thresh = *p++;
|
||||||
|
flags = *p++;
|
||||||
|
ncount = *p++;
|
||||||
|
while (--ncount >= 0) {
|
||||||
|
register u_long neighbor = *(u_long*)p;
|
||||||
|
p += 4;
|
||||||
|
printf(" %s -> ", inet_fmt(laddr, s1));
|
||||||
|
printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
|
||||||
|
inet_name(neighbor), metric, thresh);
|
||||||
|
if (flags & DVMRP_NF_TUNNEL)
|
||||||
|
printf("/tunnel");
|
||||||
|
if (flags & DVMRP_NF_SRCRT)
|
||||||
|
printf("/srcrt");
|
||||||
|
if (flags & DVMRP_NF_QUERIER)
|
||||||
|
printf("/querier");
|
||||||
|
if (flags & DVMRP_NF_DISABLED)
|
||||||
|
printf("/disabled");
|
||||||
|
if (flags & DVMRP_NF_DOWN)
|
||||||
|
printf("/down");
|
||||||
|
printf("]\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
get_number(var, deflt, pargv, pargc)
|
||||||
|
int *var, *pargc, deflt;
|
||||||
|
char ***pargv;
|
||||||
|
{
|
||||||
|
if ((*pargv)[0][2] == '\0') { /* Get the value from the next
|
||||||
|
* argument */
|
||||||
|
if (*pargc > 1 && isdigit((*pargv)[1][0])) {
|
||||||
|
(*pargv)++, (*pargc)--;
|
||||||
|
*var = atoi((*pargv)[0]);
|
||||||
|
return 1;
|
||||||
|
} else if (deflt >= 0) {
|
||||||
|
*var = deflt;
|
||||||
|
return 1;
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
} else { /* Get value from the rest of this argument */
|
||||||
|
if (isdigit((*pargv)[0][2])) {
|
||||||
|
*var = atoi((*pargv)[0] + 2);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u_long
|
||||||
|
host_addr(name)
|
||||||
|
char *name;
|
||||||
|
{
|
||||||
|
struct hostent *e = gethostbyname(name);
|
||||||
|
int addr;
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
memcpy(&addr, e->h_addr_list[0], e->h_length);
|
||||||
|
else {
|
||||||
|
addr = inet_addr(name);
|
||||||
|
if (addr == -1)
|
||||||
|
addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
setlinebuf(stderr);
|
||||||
|
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
fprintf(stderr, "must be root\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
argv++, argc--;
|
||||||
|
while (argc > 0 && argv[0][0] == '-') {
|
||||||
|
switch (argv[0][1]) {
|
||||||
|
case 'd':
|
||||||
|
if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (!get_number(&retries, -1, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
if (!get_number(&timeout, -1, &argv, &argc))
|
||||||
|
goto usage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto usage;
|
||||||
|
}
|
||||||
|
argv++, argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 1 || (argc == 1 && !(target_addr = host_addr(argv[0])))) {
|
||||||
|
usage: fprintf(stderr,
|
||||||
|
"Usage: mrinfo [-t timeout] [-r retries] router\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (target_addr == 0)
|
||||||
|
goto usage;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "Debug level %u\n", debug);
|
||||||
|
|
||||||
|
init_igmp();
|
||||||
|
|
||||||
|
{ /* Find a good local address for us. */
|
||||||
|
int udp;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int addrlen = sizeof(addr);
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = target_addr;
|
||||||
|
addr.sin_port = htons(2000); /* any port over 1024 will
|
||||||
|
* do... */
|
||||||
|
if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
|
||||||
|
|| connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
|
||||||
|
|| getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0) {
|
||||||
|
perror("Determining local address");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
close(udp);
|
||||||
|
our_addr = addr.sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ask(target_addr);
|
||||||
|
|
||||||
|
/* Main receive loop */
|
||||||
|
for (;;) {
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
int count, recvlen, dummy = 0;
|
||||||
|
register u_long src, dst, group;
|
||||||
|
struct ip *ip;
|
||||||
|
struct igmp *igmp;
|
||||||
|
int ipdatalen, iphdrlen, igmpdatalen;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(igmp_socket, &fds);
|
||||||
|
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
|
||||||
|
|
||||||
|
if (count < 0) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
perror("select");
|
||||||
|
continue;
|
||||||
|
} else if (count == 0) {
|
||||||
|
log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
|
||||||
|
if (--retries < 0)
|
||||||
|
exit(1);
|
||||||
|
if (target_level == 0)
|
||||||
|
ask(target_addr);
|
||||||
|
else
|
||||||
|
ask2(target_addr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
|
||||||
|
0, NULL, &dummy);
|
||||||
|
if (recvlen <= 0) {
|
||||||
|
if (recvlen && errno != EINTR)
|
||||||
|
perror("recvfrom");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recvlen < sizeof(struct ip)) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"packet too short (%u bytes) for IP header",
|
||||||
|
recvlen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ip = (struct ip *) recv_buf;
|
||||||
|
src = ip->ip_src.s_addr;
|
||||||
|
if (src != target_addr) {
|
||||||
|
fprintf(stderr, "mrinfo: got reply from %s",
|
||||||
|
inet_fmt(src, s1));
|
||||||
|
fprintf(stderr, " instead of %s\n",
|
||||||
|
inet_fmt(target_addr, s1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dst = ip->ip_dst.s_addr;
|
||||||
|
iphdrlen = ip->ip_hl << 2;
|
||||||
|
ipdatalen = ip->ip_len;
|
||||||
|
if (iphdrlen + ipdatalen != recvlen) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"packet shorter (%u bytes) than hdr+data length (%u+%u)",
|
||||||
|
recvlen, iphdrlen, ipdatalen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
igmp = (struct igmp *) (recv_buf + iphdrlen);
|
||||||
|
group = igmp->igmp_group.s_addr;
|
||||||
|
igmpdatalen = ipdatalen - IGMP_MINLEN;
|
||||||
|
if (igmpdatalen < 0) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"IP data field too short (%u bytes) for IGMP, from %s",
|
||||||
|
ipdatalen, inet_fmt(src, s1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (igmp->igmp_type != IGMP_DVMRP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (igmp->igmp_code) {
|
||||||
|
|
||||||
|
case DVMRP_NEIGHBORS:
|
||||||
|
if (group) {
|
||||||
|
/* knows about DVMRP_NEIGHBORS2 msg */
|
||||||
|
if (target_level == 0) {
|
||||||
|
target_level = ntohl(group);
|
||||||
|
ask2(target_addr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accept_neighbors(src, dst, (char *)(igmp + 1),
|
||||||
|
igmpdatalen);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DVMRP_NEIGHBORS2:
|
||||||
|
accept_neighbors2(src, dst, (char *)(igmp + 1),
|
||||||
|
igmpdatalen);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dummies */
|
||||||
|
void accept_probe()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_group_report()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbor_request2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_report()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbor_request()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_prune()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_graft()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_g_ack()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void add_table_entry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void check_vif_state()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void leave_group_message()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void mtrace()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,319 @@
|
||||||
|
'\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
|
||||||
|
.TH MROUTED 8
|
||||||
|
.UC 5
|
||||||
|
.SH NAME
|
||||||
|
mrouted \- IP multicast routing daemon
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B /etc/mrouted
|
||||||
|
[
|
||||||
|
.B \-p
|
||||||
|
] [
|
||||||
|
.B \-c
|
||||||
|
.I config_file
|
||||||
|
] [
|
||||||
|
.B \-d
|
||||||
|
[
|
||||||
|
.I debug_level
|
||||||
|
]]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I Mrouted
|
||||||
|
is an implementation of the Distance-Vector Multicast Routing
|
||||||
|
Protocol (DVMRP), an earlier version of which is specified in RFC-1075.
|
||||||
|
It maintains topological knowledge via a distance-vector routing protocol
|
||||||
|
(like RIP, described in RFC-1058), upon which it implements a multicast
|
||||||
|
datagram forwarding algorithm called Reverse Path Multicasting.
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
forwards a multicast datagram along a shortest (reverse) path tree
|
||||||
|
rooted at the subnet on which the datagram originates. The multicast
|
||||||
|
delivery tree may be thought of as a broadcast delivery tree that has
|
||||||
|
been pruned back so that it does not extend beyond those subnetworks
|
||||||
|
that have members of the destination group. Hence, datagrams
|
||||||
|
are not forwarded along those branches which have no listeners of the
|
||||||
|
multicast group. The IP time-to-live of a multicast datagram can be
|
||||||
|
used to limit the range of multicast datagrams.
|
||||||
|
.PP
|
||||||
|
In order to support multicasting among subnets that are separated by (unicast)
|
||||||
|
routers that do not support IP multicasting,
|
||||||
|
.I mrouted
|
||||||
|
includes support for
|
||||||
|
"tunnels", which are virtual point-to-point links between pairs of
|
||||||
|
.IR mrouted s
|
||||||
|
located anywhere in an internet. IP multicast packets are encapsulated for
|
||||||
|
transmission through tunnels, so that they look like normal unicast datagrams
|
||||||
|
to intervening routers and subnets. The encapsulation
|
||||||
|
is added on entry to a tunnel, and stripped off
|
||||||
|
on exit from a tunnel.
|
||||||
|
By default, the packets are encapsulated using the IP-in-IP protocol
|
||||||
|
(IP protocol number 4).
|
||||||
|
Older versions of
|
||||||
|
.I mrouted
|
||||||
|
tunnel using IP source routing, which puts a heavy load on some
|
||||||
|
types of routers.
|
||||||
|
This version supports IP source route tunnelling only for backwards
|
||||||
|
compatibility.
|
||||||
|
.PP
|
||||||
|
The tunnelling mechanism allows
|
||||||
|
.I mrouted
|
||||||
|
to establish a virtual internet, for
|
||||||
|
the purpose of multicasting only, which is independent of the physical
|
||||||
|
internet, and which may span multiple Autonomous Systems. This capability
|
||||||
|
is intended for experimental support of internet multicasting only, pending
|
||||||
|
widespread support for multicast routing by the regular (unicast) routers.
|
||||||
|
.I Mrouted
|
||||||
|
suffers from the well-known scaling problems of any distance-vector
|
||||||
|
routing protocol, and does not (yet) support hierarchical multicast routing.
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
handles multicast routing only; there may or may not be unicast routing
|
||||||
|
software running on the same machine as
|
||||||
|
.IR mrouted .
|
||||||
|
With the use of tunnels, it
|
||||||
|
is not necessary for
|
||||||
|
.I mrouted
|
||||||
|
to have access to more than one physical subnet
|
||||||
|
in order to perform multicast forwarding.
|
||||||
|
.br
|
||||||
|
.ne 5
|
||||||
|
.SH INVOCATION
|
||||||
|
.PP
|
||||||
|
If no "\-d" option is given, or if the debug level is specified as 0,
|
||||||
|
.I mrouted
|
||||||
|
detaches from the invoking terminal. Otherwise, it remains attached to the
|
||||||
|
invoking terminal and responsive to signals from that terminal. If "\-d" is
|
||||||
|
given with no argument, the debug level defaults to 2. Regardless of the
|
||||||
|
debug level,
|
||||||
|
.I mrouted
|
||||||
|
always writes warning and error messages to the system
|
||||||
|
log demon. Non-zero debug levels have the following effects:
|
||||||
|
.IP "level 1"
|
||||||
|
all syslog'ed messages are also printed to stderr.
|
||||||
|
.IP "level 2"
|
||||||
|
all level 1 messages plus notifications of "significant"
|
||||||
|
events are printed to stderr.
|
||||||
|
.IP "level 3"
|
||||||
|
all level 2 messages plus notifications of all packet
|
||||||
|
arrivals and departures are printed to stderr.
|
||||||
|
.SH CONFIGURATION
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
automatically configures itself to forward on all multicast-capable
|
||||||
|
interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
|
||||||
|
the loopback "interface"), and it finds other
|
||||||
|
.IR mrouted s
|
||||||
|
directly reachable
|
||||||
|
via those interfaces. To override the default configuration, or to add
|
||||||
|
tunnel links to other
|
||||||
|
.IR mrouted s,
|
||||||
|
configuration commands may be placed in
|
||||||
|
/etc/mrouted.conf (or an alternative file, specified by the "\-c" option).
|
||||||
|
There are four types of configuration commands:
|
||||||
|
.nf
|
||||||
|
|
||||||
|
phyint <local-addr> [disable] [metric <m>]
|
||||||
|
[threshold <t>] [rate_limit <b>]
|
||||||
|
[boundary <scoped-addr>/<mask-len>]
|
||||||
|
|
||||||
|
tunnel <local-addr> <remote-addr> [metric <m>]
|
||||||
|
[threshold <t>] [srcrt] [rate_limit <b>]
|
||||||
|
[boundary <scoped-addr>/<mask-len>]
|
||||||
|
|
||||||
|
cache_lifetime <ct>
|
||||||
|
|
||||||
|
pruning <off/on>
|
||||||
|
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
One note about the configuration commands - all the phyint and tunnel
|
||||||
|
command options must be on a single line except for the boundary option
|
||||||
|
which may begin on a separate line. A single phyint or tunnel command may
|
||||||
|
have multiple boundary options.
|
||||||
|
.PP
|
||||||
|
The phyint command can be used to disable multicast routing on the physical
|
||||||
|
interface identified by local IP address <local-addr>, or to associate a
|
||||||
|
non-default metric or threshold with the specified physical interface.
|
||||||
|
The local IP address <local-addr> may be alternatively replaced by the
|
||||||
|
interface name (e.g le0) for the phyint command only.
|
||||||
|
Phyint commands must precede tunnel commands.
|
||||||
|
.PP
|
||||||
|
The tunnel command can be used to establish a tunnel link between local
|
||||||
|
IP address <local-addr> and remote IP address <remote-addr>, and to associate
|
||||||
|
a non-default metric or threshold with that tunnel. The tunnel must be set
|
||||||
|
up in the mrouted.conf files of both routers before it can be used.
|
||||||
|
For backwards compatibility with older
|
||||||
|
.IR mrouted s,
|
||||||
|
the srcrt keyword specifies
|
||||||
|
encapsulation using IP source routing.
|
||||||
|
.PP
|
||||||
|
The cache_lifetime is a value that determines the amount of time that a
|
||||||
|
cached multicast route stays in kernel before timing out. The value of this
|
||||||
|
entry should lie between 300 (5 min) and 86400 (1 day). It defaults to 300.
|
||||||
|
.PP
|
||||||
|
The pruning <off/on> option is provided for
|
||||||
|
.IR mrouted
|
||||||
|
to act as a non-pruning router. It is also possible to start
|
||||||
|
.Ir mrouted
|
||||||
|
in a non-pruning mode using the "-p" option on the command line. It is
|
||||||
|
expected that a router would be configured in this manner for test
|
||||||
|
purposes only. The default mode is pruning enabled.
|
||||||
|
.PP
|
||||||
|
The metric is the "cost" associated with sending a datagram on the given
|
||||||
|
interface or tunnel; it may be used to influence the choice of routes.
|
||||||
|
The metric defaults to 1. Metrics should be kept as small as possible,
|
||||||
|
because
|
||||||
|
.I mrouted
|
||||||
|
cannot route along paths with a sum of metrics greater
|
||||||
|
than 31.
|
||||||
|
.LP
|
||||||
|
The threshold is the minimum IP time-to-live required for a multicast datagram
|
||||||
|
to be forwarded to the given interface or tunnel. It is used to control the
|
||||||
|
scope of multicast datagrams. (The TTL of forwarded packets is only compared
|
||||||
|
to the threshold, it is not decremented by the threshold. Every multicast
|
||||||
|
router decrements the TTL by 1.) The default threshold is 1.
|
||||||
|
.LP
|
||||||
|
In general, all
|
||||||
|
.IR mrouted s
|
||||||
|
connected to a particular subnet or tunnel should
|
||||||
|
use the same metric and threshold for that subnet or tunnel.
|
||||||
|
.PP
|
||||||
|
The rate_limit option allows the network administrator to specify a
|
||||||
|
certain bandwidth in Kbits/second which would be allocated to multicast
|
||||||
|
traffic.
|
||||||
|
.PP
|
||||||
|
The boundary option allows an interface
|
||||||
|
to be configured as an administrative boundary for the specified
|
||||||
|
scoped address. Packets belonging to this address will not
|
||||||
|
be forwarded on a scoped interface.
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
will not initiate execution if it has fewer than two enabled vifs,
|
||||||
|
where a vif (virtual interface) is either a physical multicast-capable
|
||||||
|
interface or a tunnel. It will log a warning if all of its vifs are
|
||||||
|
tunnels; such an
|
||||||
|
.I mrouted
|
||||||
|
configuration would be better replaced by more
|
||||||
|
direct tunnels (i.e., eliminate the middle man).
|
||||||
|
.SH SIGNALS
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
responds to the following signals:
|
||||||
|
.IP HUP
|
||||||
|
restarts
|
||||||
|
.I mrouted .
|
||||||
|
The configuration file is reread every time this signal is evoked.
|
||||||
|
.IP INT
|
||||||
|
terminates execution gracefully (i.e., by sending
|
||||||
|
good-bye messages to all neighboring routers).
|
||||||
|
.IP TERM
|
||||||
|
same as INT
|
||||||
|
.IP USR1
|
||||||
|
dumps the internal routing tables to /usr/tmp/mrouted.dump.
|
||||||
|
.IP USR2
|
||||||
|
dumps the internal cache tables to /usr/tmp/mrouted.cache.
|
||||||
|
.IP QUIT
|
||||||
|
dumps the internal routing tables to stderr (only if
|
||||||
|
.I mrouted
|
||||||
|
was invoked with a non-zero debug level).
|
||||||
|
.bp
|
||||||
|
.SH EXAMPLE
|
||||||
|
.PP
|
||||||
|
The routing tables look like this:
|
||||||
|
.nf
|
||||||
|
|
||||||
|
Virtual Interface Table
|
||||||
|
Vif Local-Address Metric Thresh Flags
|
||||||
|
0 36.2.0.8 subnet: 36.2 1 1 querier
|
||||||
|
groups: 224.0.2.1
|
||||||
|
224.0.0.4
|
||||||
|
pkts in: 3456
|
||||||
|
pkts out: 2322323
|
||||||
|
|
||||||
|
1 36.11.0.1 subnet: 36.11 1 1 querier
|
||||||
|
groups: 224.0.2.1
|
||||||
|
224.0.1.0
|
||||||
|
224.0.0.4
|
||||||
|
pkts in: 345
|
||||||
|
pkts out: 3456
|
||||||
|
|
||||||
|
2 36.2.0.8 tunnel: 36.8.0.77 3 1
|
||||||
|
peers: 36.8.0.77 (2.2)
|
||||||
|
boundaries: 239.0.1
|
||||||
|
: 239.1.2
|
||||||
|
pkts in: 34545433
|
||||||
|
pkts out: 234342
|
||||||
|
|
||||||
|
3 36.2.0.8 tunnel: 36.6.8.23 3 16
|
||||||
|
|
||||||
|
Multicast Routing Table (1136 entries)
|
||||||
|
Origin-Subnet From-Gateway Metric In-Vif Out-Vifs
|
||||||
|
36.2 1 0 1* 2 3*
|
||||||
|
36.8 36.8.0.77 4 2 0* 1* 3*
|
||||||
|
36.11 1 1 0* 2 3*
|
||||||
|
.
|
||||||
|
.
|
||||||
|
.
|
||||||
|
|
||||||
|
.fi
|
||||||
|
In this example, there are four vifs connecting to two subnets and two
|
||||||
|
tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and
|
||||||
|
vif 1 subnets have some groups present; tunnels never have any groups. This
|
||||||
|
instance of
|
||||||
|
.I mrouted
|
||||||
|
is the one responsible for sending periodic group
|
||||||
|
membership queries on the vif 0 and vif 1 subnets, as indicated by the
|
||||||
|
"querier" flags. The list of boundaries indicate the scoped addresses on that
|
||||||
|
interface. A count of the no. of incoming and outgoing packets is also
|
||||||
|
shown at each interface.
|
||||||
|
.PP
|
||||||
|
Associated with each subnet from which a multicast datagram can originate
|
||||||
|
is the address of the previous hop router (unless the subnet is directly-
|
||||||
|
connected), the metric of the path back to the origin, the incoming vif for
|
||||||
|
multicasts from that origin, and a list of outgoing vifs. "*" means that
|
||||||
|
the outgoing vif is connected to a leaf of the broadcast tree rooted at the
|
||||||
|
origin, and a multicast datagram from that origin will be forwarded on that
|
||||||
|
outgoing vif only if there are members of the destination group on that leaf.
|
||||||
|
.bp
|
||||||
|
.PP
|
||||||
|
.I Mrouted
|
||||||
|
also maintains a copy of the kernel forwarding cache table. Entries
|
||||||
|
are created and deleted by
|
||||||
|
.I mrouted.
|
||||||
|
.PP
|
||||||
|
The cache tables look like this:
|
||||||
|
.nf
|
||||||
|
|
||||||
|
Multicast Routing Cache Table (325 entries)
|
||||||
|
Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs
|
||||||
|
134.207.7 224.2.140.239 300 1 0 0 2
|
||||||
|
138.15.103 224.2.203.214 295 1 2 P 0p 2p
|
||||||
|
128.237.0 224.2.253.119 290 1 1 0 2p
|
||||||
|
129.215.200 224.2.207.48 40 1 1 0p 2
|
||||||
|
36.77.14 239.0.1.234 345 2b
|
||||||
|
|
||||||
|
.fi
|
||||||
|
Each entry is characterized by the origin subnet number and the
|
||||||
|
destination multicast group. The 'CTmr' field indicates the lifetime
|
||||||
|
(in seconds) of the entry. The entry is deleted from the cache table
|
||||||
|
when the timer decrements to zero. The Ivif field indicates the
|
||||||
|
incoming vif for multicast packets from that origin. Each router also
|
||||||
|
maintains a record of the number of prunes received from neighbouring
|
||||||
|
routers for a particular source and group. If there are no members of
|
||||||
|
a multicast group on any downward link of the multicast tree for a
|
||||||
|
subnet, a prune message is sent to the upstream router. They are
|
||||||
|
indicated by a "P" in the Psnt field. The Forwvifs field shows the
|
||||||
|
interfaces along which datagrams belonging to the source-group are
|
||||||
|
forwarded. A "p" indicates that no datagrams are being forwarded along
|
||||||
|
that interface. An unlisted interface is a leaf subnet with are no
|
||||||
|
members of the particular group on that subnet. A "b" on an interface
|
||||||
|
indicates that it is a boundary interface, i.e. traffic will not be
|
||||||
|
forwarded on the scoped address on that interface.
|
||||||
|
|
||||||
|
|
||||||
|
.SH FILES
|
||||||
|
/etc/mrouted.conf
|
||||||
|
.SH SEE ALSO
|
||||||
|
DVMRP is described, along with other multicast routing algorithms, in the
|
||||||
|
paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering,
|
||||||
|
in the Proceedings of the ACM SIGCOMM '88 Conference.
|
||||||
|
.SH AUTHORS
|
||||||
|
Steve Deering & Ajit Thyagarajan
|
|
@ -0,0 +1,26 @@
|
||||||
|
# $Id: mrouted.conf,v 1.5 1994/08/24 23:54:21 thyagara Exp $
|
||||||
|
#
|
||||||
|
# This is the configuration file for "mrouted", an IP multicast router.
|
||||||
|
# mrouted looks for it in "/etc/mrouted.conf".
|
||||||
|
#
|
||||||
|
# Command formats:
|
||||||
|
#
|
||||||
|
# cache_lifetime 3600
|
||||||
|
# pruning on
|
||||||
|
#
|
||||||
|
# phyint <local-addr> [disable] [metric <m>] [threshold <t>] [rate_limit <b>]
|
||||||
|
# [boundary <scoped-addr>/<mask-len>]
|
||||||
|
# tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
|
||||||
|
# [threshold <t>] [rate_limit <b>]
|
||||||
|
# [boundary <scoped-addr>/<mask-len>]
|
||||||
|
#
|
||||||
|
# NOTE: any phyint commands MUST precede any tunnel commands
|
||||||
|
# NOTE: boundary commands may appear on a separate line
|
||||||
|
# (OTHER keywords must be on the same line as phyint or tunnel)
|
||||||
|
# NOTE: the mask-len is the no. of leading 1's in the mask
|
||||||
|
#
|
||||||
|
|
||||||
|
phyint 128.4.2.2 metric 1 threshold 16 boundary 239.2.0.0/16
|
||||||
|
boundary 239.5.8.0/24
|
||||||
|
tunnel 128.4.0.77 128.4.0.8 metric 3 rate_limit 500 # <-- EXAMPLE
|
||||||
|
boundary 239.2.3.3/16 # 239.2.x.x is scoped
|
|
@ -0,0 +1,459 @@
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT 10 /* How long to wait before retrying requests */
|
||||||
|
|
||||||
|
int timeout = DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
vifi_t numvifs; /* to keep loader happy */
|
||||||
|
/* (see COPY_TABLES macro called in kern.c) */
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
inet_name(addr)
|
||||||
|
u_long addr;
|
||||||
|
{
|
||||||
|
struct hostent *e;
|
||||||
|
|
||||||
|
e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
|
||||||
|
|
||||||
|
return e ? e->h_name : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
u_long
|
||||||
|
host_addr(name)
|
||||||
|
char *name;
|
||||||
|
{
|
||||||
|
struct hostent *e = gethostbyname(name);
|
||||||
|
int addr;
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
memcpy(&addr, e->h_addr_list[0], e->h_length);
|
||||||
|
else {
|
||||||
|
addr = inet_addr(name);
|
||||||
|
if (addr == -1)
|
||||||
|
addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
proto_type(type)
|
||||||
|
u_char type;
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case PROTO_DVMRP:
|
||||||
|
return ("PROTO_DVMRP");
|
||||||
|
case PROTO_MOSPF:
|
||||||
|
return ("PROTO_MOSPF");
|
||||||
|
case PROTO_PIM:
|
||||||
|
return ("PROTO_PIM");
|
||||||
|
case PROTO_CBT:
|
||||||
|
return ("PROTO_CBT");
|
||||||
|
default:
|
||||||
|
return ("PROTO_UNKNOWN");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
flag_type(type)
|
||||||
|
u_char type;
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TR_NO_ERR:
|
||||||
|
return ("NO_ERR");
|
||||||
|
case TR_WRONG_IF:
|
||||||
|
return ("WRONG_IF");
|
||||||
|
case TR_PRUNED:
|
||||||
|
return ("PRUNED");
|
||||||
|
case TR_SCOPED:
|
||||||
|
return ("SCOPED");
|
||||||
|
case TR_NO_RTE:
|
||||||
|
return ("NO_RTE");
|
||||||
|
default:
|
||||||
|
return ("INVALID ERR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
t_diff(a_sec, a_usec, b_sec, b_usec)
|
||||||
|
u_long a_sec, a_usec, b_sec, b_usec;
|
||||||
|
{
|
||||||
|
int d = a_sec - b_sec;
|
||||||
|
int ms = a_usec - b_usec;
|
||||||
|
|
||||||
|
if ((d < 0) ||
|
||||||
|
((d == 0) && (ms < 0))) {
|
||||||
|
d = b_sec - a_sec;
|
||||||
|
ms = b_usec - a_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (d) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ms += 1000000;
|
||||||
|
case 1:
|
||||||
|
ms += 1000000;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ms += (1000000) * d;
|
||||||
|
}
|
||||||
|
return (ms/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
main(argc, argv)
|
||||||
|
int argc;
|
||||||
|
char *argv[];
|
||||||
|
{
|
||||||
|
struct timeval tq;
|
||||||
|
struct timezone tzp;
|
||||||
|
u_long resptime;
|
||||||
|
|
||||||
|
int udp;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int addrlen = sizeof(addr);
|
||||||
|
u_long lcl_addr = 0; /* in NET order */
|
||||||
|
|
||||||
|
u_long qid = ((u_long)random() >> 8);
|
||||||
|
u_long qsrc = NULL;
|
||||||
|
u_long qgrp = NULL;
|
||||||
|
u_long qdst = NULL;
|
||||||
|
u_char qno = 0;
|
||||||
|
u_long raddr = NULL;
|
||||||
|
u_char qttl = 1;
|
||||||
|
u_char rttl = 1;
|
||||||
|
u_long dst = NULL;
|
||||||
|
|
||||||
|
struct tr_query *query;
|
||||||
|
|
||||||
|
struct tr_rlist *tr_rlist = NULL;
|
||||||
|
|
||||||
|
char *p;
|
||||||
|
int datalen = 0;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
argv++, argc--;
|
||||||
|
|
||||||
|
while (argc > 0 && *argv[0] == '-') {
|
||||||
|
switch (argv[0][1]) {
|
||||||
|
case 's':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
qsrc = host_addr(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'g':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
qgrp = host_addr(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'd':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
qdst = host_addr(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'x':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
dst = host_addr(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 't':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
qttl = atoi(argv[0]);
|
||||||
|
if (qttl < 1)
|
||||||
|
qttl = 1;
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'n':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
qno = atoi(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'l':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
rttl = atoi(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
case 'r':
|
||||||
|
if (argc > 1 && isdigit(*(argv + 1)[0])) {
|
||||||
|
argv++, argc--;
|
||||||
|
raddr = host_addr(argv[0]);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
goto usage;
|
||||||
|
default:
|
||||||
|
goto usage;
|
||||||
|
}
|
||||||
|
argv++, argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
usage: printf("usage: mtrace -s <src> -g <grp> -d <dst> -n <# reports> \n");
|
||||||
|
printf(" -t <ttl> [-x <qdst>] [-r <rdst>] [-l <rttl>]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Mtrace src %s grp %s dst %s #%d\n", inet_fmt(qsrc, s1),
|
||||||
|
inet_fmt(qgrp, s2), inet_fmt(qdst, s3), qno);
|
||||||
|
printf(" resp ttl %d resp addr %s\n", rttl, inet_fmt(raddr, s1));
|
||||||
|
|
||||||
|
init_igmp();
|
||||||
|
|
||||||
|
/* Obtain the local address from which to send out packets */
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_addr.s_addr = qgrp;
|
||||||
|
addr.sin_port = htons(2000);
|
||||||
|
|
||||||
|
if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
|
||||||
|
(connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
|
||||||
|
getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
|
||||||
|
perror("Determining local address");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
close(udp);
|
||||||
|
lcl_addr = addr.sin_addr.s_addr;
|
||||||
|
|
||||||
|
/* Got the local address now */
|
||||||
|
/* Now, make up the IGMP packet to send */
|
||||||
|
|
||||||
|
query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
|
||||||
|
|
||||||
|
query->tr_src = qsrc;
|
||||||
|
query->tr_dst = qdst;
|
||||||
|
query->tr_qid = qid;
|
||||||
|
if (raddr)
|
||||||
|
query->tr_raddr = raddr;
|
||||||
|
else
|
||||||
|
query->tr_raddr = lcl_addr;
|
||||||
|
query->tr_rttl = rttl;
|
||||||
|
|
||||||
|
datalen += sizeof(struct tr_query);
|
||||||
|
|
||||||
|
if (IN_MULTICAST(ntohl(qgrp)))
|
||||||
|
k_set_ttl(qttl);
|
||||||
|
else
|
||||||
|
k_set_ttl(1);
|
||||||
|
|
||||||
|
if (dst == NULL)
|
||||||
|
dst = qgrp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set timer to calculate delays & send query
|
||||||
|
*/
|
||||||
|
gettimeofday(&tq, &tzp);
|
||||||
|
|
||||||
|
send_igmp(lcl_addr, dst, IGMP_MTRACE, qno,
|
||||||
|
qgrp, datalen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the response is to be a multicast address, make sure we
|
||||||
|
* are listening on that multicast address.
|
||||||
|
*/
|
||||||
|
if (IN_MULTICAST(ntohl(raddr)))
|
||||||
|
k_join(raddr, lcl_addr);
|
||||||
|
|
||||||
|
/* Wait for our reply now */
|
||||||
|
while (!done) {
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
struct timezone tzp;
|
||||||
|
|
||||||
|
int count, recvlen, dummy = 0;
|
||||||
|
register u_long src, dst, group, smask;
|
||||||
|
struct ip *ip;
|
||||||
|
struct igmp *igmp;
|
||||||
|
struct tr_resp *resp;
|
||||||
|
int ipdatalen, iphdrlen, igmpdatalen;
|
||||||
|
int rno;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(igmp_socket, &fds);
|
||||||
|
|
||||||
|
/* need to input timeout as optional argument */
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
count = select(igmp_socket + 1, &fds, 0, 0, &tv);
|
||||||
|
|
||||||
|
if (count < 0) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
perror("select");
|
||||||
|
continue;
|
||||||
|
} else if (count == 0) {
|
||||||
|
printf("Timed out receiving responses\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
recvlen = recvfrom(igmp_socket, recv_buf, sizeof(recv_buf),
|
||||||
|
0, NULL, &dummy);
|
||||||
|
|
||||||
|
if (recvlen <= 0) {
|
||||||
|
if (recvlen && errno != EINTR)
|
||||||
|
perror("recvfrom");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recvlen < sizeof(struct ip)) {
|
||||||
|
log(LOG_WARNING, 0,
|
||||||
|
"packet too short (%u bytes) for IP header",
|
||||||
|
recvlen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ip = (struct ip *) recv_buf;
|
||||||
|
|
||||||
|
iphdrlen = ip->ip_hl << 2;
|
||||||
|
ipdatalen = ip->ip_len;
|
||||||
|
if (iphdrlen + ipdatalen != recvlen) {
|
||||||
|
printf("packet shorter (%u bytes) than hdr+data length (%u+%u)\n",
|
||||||
|
recvlen, iphdrlen, ipdatalen);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
igmp = (struct igmp *) (recv_buf + iphdrlen);
|
||||||
|
group = igmp->igmp_group.s_addr;
|
||||||
|
igmpdatalen = ipdatalen - IGMP_MINLEN;
|
||||||
|
if (igmpdatalen < 0) {
|
||||||
|
printf("IP data field too short (%u bytes) for IGMP, from %s\n",
|
||||||
|
ipdatalen, inet_fmt(src, s1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (igmp->igmp_type != IGMP_MTRACE &&
|
||||||
|
igmp->igmp_type != IGMP_MTRACE_RESP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (igmpdatalen == QLEN)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((igmpdatalen - QLEN)%RLEN) {
|
||||||
|
printf("packet with incorrect datalen\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = (struct tr_query *)(igmp + 1);
|
||||||
|
|
||||||
|
/* If this is query with a different id, ignore! */
|
||||||
|
if (query->tr_qid != qid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Most of the sanity checking done at this point.
|
||||||
|
* This is the packet we have been waiting for all this time
|
||||||
|
*/
|
||||||
|
resp = (struct tr_resp *)(query + 1);
|
||||||
|
|
||||||
|
rno = (igmpdatalen - QLEN)/RLEN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* print the responses out in reverse order (from src to dst)
|
||||||
|
*/
|
||||||
|
printf("src: <%s> grp: <%s> dst: <%s>\n\n", inet_fmt(qsrc, s1),
|
||||||
|
inet_fmt(qgrp, s2), inet_fmt(qdst, s3));
|
||||||
|
|
||||||
|
VAL_TO_MASK(smask, (resp+rno-1)->tr_smask);
|
||||||
|
|
||||||
|
if (((resp+rno-1)->tr_inaddr & smask) == (qsrc & smask))
|
||||||
|
printf(" %-15s \n", inet_fmt(qsrc, s1));
|
||||||
|
else
|
||||||
|
printf(" * * *\n");
|
||||||
|
|
||||||
|
resptime = 0;
|
||||||
|
while (rno--) {
|
||||||
|
struct tr_resp *r = resp + rno;
|
||||||
|
|
||||||
|
printf(" | \n");
|
||||||
|
printf(" %-15s ", inet_fmt(r->tr_inaddr, s1));
|
||||||
|
printf("ttl %d ", r->tr_fttl);
|
||||||
|
printf("cum: %d ms ",
|
||||||
|
t_diff(r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4,
|
||||||
|
tq.tv_sec & 0xffff, tq.tv_usec));
|
||||||
|
printf("hop: %d ms ",
|
||||||
|
t_diff(resptime >> 16, (resptime & 0xffff) << 4,
|
||||||
|
r->tr_qarr >> 16, (r->tr_qarr & 0xffff) << 4));
|
||||||
|
printf("%s ", proto_type(r->tr_rproto));
|
||||||
|
printf("%s\n", flag_type(r->tr_rflags));
|
||||||
|
|
||||||
|
printf(" %-15s ", inet_fmt(r->tr_outaddr, s1));
|
||||||
|
printf("v_in: %ld ", r->tr_vifin);
|
||||||
|
printf("v_out: %ld ", r->tr_vifout);
|
||||||
|
printf("pkts: %ld\n", r->tr_pktcnt);
|
||||||
|
|
||||||
|
resptime = r->tr_qarr;
|
||||||
|
}
|
||||||
|
printf(" | \n");
|
||||||
|
printf(" %-15s \n", inet_fmt(qdst, s1));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the response was multicast back, leave the group
|
||||||
|
*/
|
||||||
|
if (IN_MULTICAST(ntohl(raddr)))
|
||||||
|
k_leave(raddr, lcl_addr);
|
||||||
|
|
||||||
|
/* If I don't expect any more replies, exit here */
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* dummies */
|
||||||
|
void log()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_probe()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_group_report()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbors()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbors2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbor_request2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_report()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_neighbor_request()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_prune()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_graft()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void accept_g_ack()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void add_table_entry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void check_vif_state()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void mtrace()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void leave_group_message()
|
||||||
|
{
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: prune.h,v 1.3 1994/08/24 23:54:40 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macro for copying the user-level cache table to the kernel
|
||||||
|
* level table variable passed on by the setsock option
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define COPY_TABLES(from, to) { \
|
||||||
|
register u_int _i; \
|
||||||
|
(to).mfcc_origin.s_addr = (from)->kt_origin; \
|
||||||
|
(to).mfcc_mcastgrp.s_addr = (from)->kt_mcastgrp; \
|
||||||
|
(to).mfcc_originmask.s_addr = (from)->kt_originmask; \
|
||||||
|
(to).mfcc_parent = (from)->kt_parent; \
|
||||||
|
for (_i = 0; _i < numvifs; _i++) \
|
||||||
|
(to).mfcc_ttls[_i] = (from)->kt_ttls[_i]; \
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User level Kernel Cache Table structure
|
||||||
|
*
|
||||||
|
* A copy of the kernel table is kept at the user level. Modifications are
|
||||||
|
* made to this table and then passed on to the kernel. A timeout value is
|
||||||
|
* an extra field in the user level table.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct ktable
|
||||||
|
{
|
||||||
|
struct ktable *kt_next; /* pointer to the next entry */
|
||||||
|
u_long kt_origin; /* subnet origin of multicasts */
|
||||||
|
u_long kt_mcastgrp; /* multicast group associated */
|
||||||
|
u_long kt_originmask; /* subnet mask for origin */
|
||||||
|
vifi_t kt_parent; /* incoming vif */
|
||||||
|
u_long kt_gateway; /* upstream router */
|
||||||
|
vifbitmap_t kt_children; /* outgoing children vifs */
|
||||||
|
vifbitmap_t kt_leaves; /* subset of outgoing children vifs */
|
||||||
|
vifbitmap_t kt_scope; /* scoped interfaces */
|
||||||
|
u_char kt_ttls[MAXVIFS]; /* ttl vector for forwarding */
|
||||||
|
vifbitmap_t kt_grpmems; /* forw. vifs for src, grp */
|
||||||
|
int kt_timer; /* for timing out entry in cache */
|
||||||
|
struct prunlst *kt_rlist; /* router list nghboring this rter */
|
||||||
|
u_short kt_prun_count; /* count of total no. of prunes */
|
||||||
|
int kt_prsent_timer; /* prune lifetime timer */
|
||||||
|
u_int kt_grftsnt; /* graft sent upstream */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* structure to store incoming prunes
|
||||||
|
*/
|
||||||
|
struct prunlst
|
||||||
|
{
|
||||||
|
struct prunlst *rl_next;
|
||||||
|
u_long rl_router;
|
||||||
|
u_long rl_router_subnet;
|
||||||
|
vifi_t rl_vifi;
|
||||||
|
int rl_timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tr_query {
|
||||||
|
u_long tr_src; /* traceroute source */
|
||||||
|
u_long tr_dst; /* traceroute destination */
|
||||||
|
u_long tr_raddr; /* traceroute response address */
|
||||||
|
struct {
|
||||||
|
u_int ttl : 8; /* traceroute response ttl */
|
||||||
|
u_int qid : 24; /* traceroute query id */
|
||||||
|
} q;
|
||||||
|
} tr_query;
|
||||||
|
|
||||||
|
#define tr_rttl q.ttl
|
||||||
|
#define tr_qid q.qid
|
||||||
|
|
||||||
|
struct tr_resp {
|
||||||
|
u_long tr_qarr; /* query arrival time */
|
||||||
|
u_long tr_inaddr; /* incoming interface address */
|
||||||
|
u_long tr_outaddr; /* outgoing interface address */
|
||||||
|
u_long tr_rmtaddr; /* parent address in source tree */
|
||||||
|
u_long tr_vifin; /* input packet count on interface */
|
||||||
|
u_long tr_vifout; /* output packet count on interface */
|
||||||
|
u_long tr_pktcnt; /* total incoming packets for src-grp */
|
||||||
|
u_char tr_rproto; /* routing protocol deployed on router */
|
||||||
|
u_char tr_fttl; /* ttl required to forward on outvif */
|
||||||
|
u_char tr_smask; /* subnet mask for src addr */
|
||||||
|
u_char tr_rflags; /* forwarding error codes */
|
||||||
|
} tr_resp;
|
||||||
|
|
||||||
|
/* defs within mtrace */
|
||||||
|
#define QUERY 1
|
||||||
|
#define RESP 2
|
||||||
|
#define QLEN sizeof(struct tr_query)
|
||||||
|
#define RLEN sizeof(struct tr_resp)
|
||||||
|
|
||||||
|
/* fields for tr_rflags (forwarding error codes) */
|
||||||
|
#define TR_NO_ERR 0x0
|
||||||
|
#define TR_WRONG_IF 0x1
|
||||||
|
#define TR_PRUNED 0x2
|
||||||
|
#define TR_SCOPED 0x4
|
||||||
|
#define TR_NO_RTE 0x5
|
||||||
|
|
||||||
|
/* fields for tr_rproto (routing protocol) */
|
||||||
|
#define PROTO_DVMRP 0x1
|
||||||
|
#define PROTO_MOSPF 0x2
|
||||||
|
#define PROTO_PIM 0x3
|
||||||
|
#define PROTO_CBT 0x4
|
||||||
|
|
||||||
|
#define MASK_TO_VAL(x, i) { \
|
||||||
|
(i) = 0; \
|
||||||
|
while ((x) << (i)) \
|
||||||
|
(i)++; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VAL_TO_MASK(x, i) { \
|
||||||
|
x = ~((1 << (32 - (i))) - 1); \
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: route.h,v 1.3 1993/05/30 01:36:38 deering Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routing Table Entry, one per subnet from which a multicast could originate.
|
||||||
|
* (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
|
||||||
|
*
|
||||||
|
* The Routing Table is stored as a singly-linked list of these structures,
|
||||||
|
* ordered by increasing value of rt_originmask and, secondarily, by
|
||||||
|
* increasing value of rt_origin within each rt_originmask value.
|
||||||
|
* This data structure is efficient for generating route reports, whether
|
||||||
|
* full or partial, for processing received full reports, for clearing the
|
||||||
|
* CHANGED flags, and for periodically advancing the timers in all routes.
|
||||||
|
* It is not so efficient for updating a small number of routes in response
|
||||||
|
* to a partial report. In a stable topology, the latter are rare; if they
|
||||||
|
* turn out to be costing a lot, we can add an auxiliary hash table for
|
||||||
|
* faster access to arbitrary route entries.
|
||||||
|
*/
|
||||||
|
struct rtentry {
|
||||||
|
struct rtentry *rt_next; /* link to next entry MUST BE FIRST */
|
||||||
|
u_long rt_origin; /* subnet origin of multicasts */
|
||||||
|
u_long rt_originmask; /* subnet mask for origin */
|
||||||
|
short rt_originwidth; /* # bytes of origin subnet number */
|
||||||
|
u_char rt_metric; /* cost of route back to origin */
|
||||||
|
u_char rt_flags; /* RTF_ flags defined below */
|
||||||
|
u_long rt_gateway; /* first-hop gateway back to origin */
|
||||||
|
vifi_t rt_parent; /* incoming vif (ie towards origin) */
|
||||||
|
vifbitmap_t rt_children; /* outgoing children vifs */
|
||||||
|
vifbitmap_t rt_leaves; /* subset of outgoing children vifs */
|
||||||
|
u_long *rt_dominants; /* per vif dominant gateways */
|
||||||
|
u_long *rt_subordinates; /* per vif subordinate gateways */
|
||||||
|
u_long *rt_leaf_timers; /* per vif leaf confirmation timers */
|
||||||
|
u_long rt_timer; /* for timing out the route entry */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RTF_CHANGED 0x01 /* route changed but not reported */
|
||||||
|
#define RTF_LEAF_TIMING 0x02 /* some leaf timers are running */
|
||||||
|
|
||||||
|
|
||||||
|
#define ALL_ROUTES 0 /* possible arguments to report() */
|
||||||
|
#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* The mrouted program is covered by the license in the accompanying file
|
||||||
|
* named "LICENSE". Use of the mrouted program represents acceptance of
|
||||||
|
* the terms and conditions listed in that file.
|
||||||
|
*
|
||||||
|
* The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
|
||||||
|
* Leland Stanford Junior University.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: vif.h,v 1.6 1994/08/24 23:54:47 thyagara Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User level Virtual Interface structure
|
||||||
|
*
|
||||||
|
* A "virtual interface" is either a physical, multicast-capable interface
|
||||||
|
* (called a "phyint") or a virtual point-to-point link (called a "tunnel").
|
||||||
|
* (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
|
||||||
|
*/
|
||||||
|
struct uvif {
|
||||||
|
u_short uv_flags; /* VIFF_ flags defined below */
|
||||||
|
u_char uv_metric; /* cost of this vif */
|
||||||
|
u_int uv_rate_limit; /* rate limit on this vif */
|
||||||
|
u_char uv_threshold; /* min ttl required to forward on vif */
|
||||||
|
u_long uv_lcl_addr; /* local address of this vif */
|
||||||
|
u_long uv_rmt_addr; /* remote end-point addr (tunnels only) */
|
||||||
|
u_long uv_subnet; /* subnet number (phyints only) */
|
||||||
|
u_long uv_subnetmask; /* subnet mask (phyints only) */
|
||||||
|
u_long uv_subnetbcast;/* subnet broadcast addr (phyints only) */
|
||||||
|
char uv_name[IFNAMSIZ]; /* interface name */
|
||||||
|
struct listaddr *uv_groups; /* list of local groups (phyints only) */
|
||||||
|
struct listaddr *uv_neighbors; /* list of neighboring routers */
|
||||||
|
struct vif_acl *uv_acl; /* access control list of groups */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT)
|
||||||
|
#define VIFF_DOWN 0x0100 /* kernel state of interface */
|
||||||
|
#define VIFF_DISABLED 0x0200 /* administratively disabled */
|
||||||
|
#define VIFF_QUERIER 0x0400 /* I am the subnet's querier */
|
||||||
|
#define VIFF_ONEWAY 0x0800 /* Maybe one way interface */
|
||||||
|
|
||||||
|
struct vif_acl {
|
||||||
|
struct vif_acl *acl_next; /* next acl member */
|
||||||
|
u_long acl_addr; /* Group address */
|
||||||
|
u_long acl_mask; /* Group addr. mask */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct listaddr {
|
||||||
|
struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
|
||||||
|
u_long al_addr; /* local group or neighbor address */
|
||||||
|
u_long al_timer; /* for timing out group or neighbor */
|
||||||
|
u_long al_genid; /* generation id for neighbor */
|
||||||
|
u_char al_pv; /* router protocol version */
|
||||||
|
u_char al_mv; /* router mrouted version */
|
||||||
|
u_long al_timerid; /* returned by set timer */
|
||||||
|
u_long al_query; /* second query in case of leave*/
|
||||||
|
u_short al_old; /* if old memberships are present */
|
||||||
|
u_short al_last; /* # of query's since last old rep */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
|
Loading…
Reference in New Issue