aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Fenner <fenner@FreeBSD.org>1999-01-20 07:44:18 +0000
committerBill Fenner <fenner@FreeBSD.org>1999-01-20 07:44:18 +0000
commit1f25327484733d0bec3b33044da7bd5d82ad007f (patch)
tree19c94eeb7ba2b59b0ad53ae0864ebf06723a8afa
parentce595def0708b60ce4dde9002ac206fae20be240 (diff)
downloadsrc-vendor/mrouted.tar.gz
src-vendor/mrouted.zip
Import mrouted version 3.9-beta3+IOS12 . This is a version of 3.9-beta3vendor/mrouted
with minor changes to work around a bug in Cisco's IOS version 12.0 . 3.9-beta3 is much improved over 3.8, and is only labelled "beta" because of missing features, as opposed to instability or known bugs.
Notes
Notes: svn path=/cvs2svn/branches/XEROX/; revision=42888
-rw-r--r--usr.sbin/mrouted/RELEASE193
-rw-r--r--usr.sbin/mrouted/VERSION1
-rw-r--r--usr.sbin/mrouted/callout.c156
-rw-r--r--usr.sbin/mrouted/cfparse.y456
-rw-r--r--usr.sbin/mrouted/config.c65
-rw-r--r--usr.sbin/mrouted/defs.h298
-rw-r--r--usr.sbin/mrouted/dvmrp.h19
-rw-r--r--usr.sbin/mrouted/icmp.c225
-rw-r--r--usr.sbin/mrouted/igmp.c163
-rw-r--r--usr.sbin/mrouted/igmpv2.h42
-rw-r--r--usr.sbin/mrouted/inet.c16
-rw-r--r--usr.sbin/mrouted/ipip.c145
-rw-r--r--usr.sbin/mrouted/kern.c120
-rw-r--r--usr.sbin/mrouted/main.c694
-rw-r--r--usr.sbin/mrouted/mapper.c20
-rw-r--r--usr.sbin/mrouted/mrinfo.c36
-rw-r--r--usr.sbin/mrouted/mrouted.8458
-rw-r--r--usr.sbin/mrouted/mrouted.conf2
-rw-r--r--usr.sbin/mrouted/pathnames.h2
-rw-r--r--usr.sbin/mrouted/prune.c1307
-rw-r--r--usr.sbin/mrouted/prune.h14
-rw-r--r--usr.sbin/mrouted/route.c894
-rw-r--r--usr.sbin/mrouted/route.h11
-rw-r--r--usr.sbin/mrouted/rsrr.c76
-rw-r--r--usr.sbin/mrouted/vif.c999
-rw-r--r--usr.sbin/mrouted/vif.h200
26 files changed, 4759 insertions, 1853 deletions
diff --git a/usr.sbin/mrouted/RELEASE b/usr.sbin/mrouted/RELEASE
index 50127513aa00..979065011f28 100644
--- a/usr.sbin/mrouted/RELEASE
+++ b/usr.sbin/mrouted/RELEASE
@@ -1,21 +1,188 @@
-$Id: README-3.8.mrouted,v 3.8 1995/11/29 22:23:02 fenner Rel $
+README-3.9-beta3.mrouted,v 1.1.2.1 1998/03/01 03:00:20 fenner Exp
IP Multicast Extensions for BSD-Derived Unix Systems
+ Multicast Routing Daemon
- Release 3.8
- November 29, 1995
+ Release 3.9-beta3
+ February 28, 1998
available from parcftp.xerox.com,
- file pub/net-research/ipmulti/mrouted3.8.tar.Z
- binaries pub/net-research/ipmulti/mrouted3.8-sparc-sunos41x.tar.Z
- pub/net-research/ipmulti/mrouted3.8-sparc-solaris2.tar.Z
- pub/net-research/ipmulti/mrouted3.8-i386-bsd.tar.Z
- pub/net-research/ipmulti/mrouted3.8-alpha-osf1.tar.Z
- pub/net-research/ipmulti/mrouted3.8-sgi-irix.tar.Z
- pub/net-research/ipmulti/mrouted3.8-hp-hpux.tar.Z
-
-Note: The 3.8 release is mrouted-only, and will run on top of a 3.5 kernel.
-It is a drop-in replacement for mrouted 3.5, 3.6 or 3.7 .
+ file pub/net-research/ipmulti/beta-test/mrouted3.9-beta3.tar.Z
+ binaries:
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sparc-sunos41x.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sparc-solaris2.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-i386-freebsd22.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-alpha-osf1.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sgi-irix6.tar.Z
+
+Note: The 3.9 release is mrouted-only, and will run on top of a 3.5 kernel.
+It is a drop-in replacement for mrouted 3.5, 3.6, 3.7 or 3.8.
+
+NOTE WELL: This is a beta-test release of mrouted. The basic
+functionality has been extensively tested in CAIRN and other
+testbeds, but it is expected to have bugs. Please report bugs to Bill
+Fenner <fenner@parc.xerox.com>.
+
+
+The 3.9-beta3 release fixes the following bugs:
+ o There was a bug handling routing updates which caused random black
+ holes.
+
+ o There was a race condition in the timer handlers causing free'd memory
+ to sometimes get touched.
+
+ o "allow_nonpruners" wasn't allowed in the configuration file (and almost
+ nobody noticed! - probably a good sign)
+
+ o When a prune times out and the source has been active "recently",
+ mrouted now waits for further traffic instead of triggering a new
+ prune.
+
+ o mrouted now ignores unreachable routes when making a routing decision
+ (previously it would blackhole, now it can find a less-specific)
+
+The 3.9-beta3 release has the following new features:
+ o A "blaster" keyword for mrouted.conf, to turn on handling of routers
+ (mostly ciscos) which overwhelm the socket buffers by blasting the
+ whole routing table at once.
+
+ o A "notransit" keyword; routes learned on a "notransit" vif will not be
+ readvertised onto another "notransit" vif.
+
+ o The 500kbps default rate limit on tunnels has been removed.
+
+ o An ICMP listener which logs ICMP errors which appear to be in response to
+ tunnel packets that we sent.
+
+ o A tunnel traffic encapsulator, which encapsulates control traffic
+ inside the tunnel instead of unicasting it "beside" the tunnel.
+ This is turned off by default; use "beside off" to turn it on.
+
+ o A "force_leaf" flag to ignore any potential neighbors on a given interface.
+
+
+=========
+3.9-beta2
+June 11, 1997
+
+The 3.9-beta2 release fixes the following bugs:
+ o There was a bug in 3.9-beta1's raw socket buffer processing that
+ would cause an immediate lockup on startup on some systems.
+
+ o RSRR would not clear out the group membership information if
+ further notification of changes to this route entry was not possible.
+
+There is no need to upgrade to 3.9-beta2 if you are not experiencing
+one of the aforementioned bugs.
+
+=========
+3.9-beta1
+June 6, 1997
+
+The 3.9-beta1 release has the following known bugs:
+
+ o The startup message doesn't print properly if you have too many
+ interfaces.
+
+The 3.9-beta1 release fixes the following bugs:
+
+ o mrouted did not properly keep track of subordinates, and would not
+ time out subordinateness. This caused 2 major problems:
+ 1. pruning did not happen when there were equal-cost paths to
+ the same multi-access link
+ 2. subordinateness which did not get cancelled by a non-poisoned
+ route (e.g. in the face of route filtering) did not time out,
+ causing traffic to continue to flow.
+
+ o mrouted's IGMPv2 processing when it is not the querier now
+ conforms to draft-ietf-idmr-igmp-v2-06.txt Thanks to Lorenzo
+ VICISANO <L.Vicisano@cs.ucl.ac.uk> for finding a problem.
+
+ o mrouted is much more careful about forgetting prunes; 3.8
+ would forget prunes whenever any route change ocurred.
+
+The 3.9-beta1 release has the following new features:
+
+ o Longer prune lifetimes (2 hours) by default. Prune lifetimes may
+ be configured per-vif, with the "prune_lifetime N" mrouted.conf
+ configuration file entry (where N is in seconds). This helps to
+ work around the black holes caused on restart when you have a Cisco
+ upstream which does not handle genid's; if this is your situation
+ the recommended value is 300.
+
+ o mrouted's behavior of flooding new routes by default at startup
+ in order to speed healing of paths during startup can be turned off
+ per-vif or globally with the "noflood" configuration option.
+ Turning this option off means you are likely to experience
+ black holes for a minute or two when you restart a router. The
+ default is to flood for a minute or two until mrouted is able to
+ learn subordinate relationships.
+
+ o mrouted now retransmits prunes by default on point-to-point links.
+ prune retransmission can be turned on or off per vif via the
+ "rexmit_prunes [on|off]" mrouted.conf command. Prune retransmission
+ helps on lossy links, and also helps when a router has forgotten
+ about a prune (e.g. if it is out of memory and needs to shed state,
+ or due to a bug).
+
+ o The new "passive" mode causes mrouted to not actively send probes
+ looking for neighbors. This allows a dialup link to become quiescent
+ if there is no DVMRP neighbor on the other end. Configuring
+ "passive" on both ends of a link will cause it to never come up.
+
+ o mrouted defaults to not peering with DVMRP routers that do not
+ prune. Use the "allow_nonpruners" mrouted.conf option on a vif
+ on which you want to allow such peerings.
+
+ o mrouted now allows route filtering. mrouted.conf syntax:
+ accept 13/8
+
+ accepts all routes matching 13/8 (e.g. accepts
+ 13.2.116/22). If you want to accept only exactly
+ 13/8, use
+
+ accept 13/8 exact
+
+ deny 10/8 64/2 130/8 exact 172/8 exact
+
+ denies some common MBone martians
+
+ Only "accept" or "deny" is allowed, no combinations.
+
+ Add "bidir" to apply the filter to output too, otherwise
+ it's input only.
+
+ Expected usage:
+ - Providers filter routes that customers send them
+ - Martian removal
+ - Topology modification (e.g. don't let the existence of
+ private tunnel foo out into the world).
+
+
+ o mrouted now malloc's the buffer it uses for SIOCGIFCONF, to allow
+ for more interfaces. Thanks to Danny Mitzel
+
+ o mrouted now ignores multiple entries for a single interface
+ name (temporary hack until mrouted understands interface aliases)
+
+ o mrouted's "-d" flag has been modified to accept the names of the
+ systems which you would like to debug.
+ packet, prunes, routes, peers, cache, timeout, interface,
+ membership, traceroute, igmp
+
+ o mrouted now times neighbors out fater, and fully detects and
+ ignores routes from one-way peerings.
+
+ o mrouted's route processing has been sped up, especially at startup.
+
+ o mrouted uses the biggest SO_RCVBUF the operating system allows
+ (up to 256Kbytes)
+
+ o mrouted uses TOS 0xc0 ("Internet Control") for DVMRP messages.
+
+===========
+Release 3.8
+November 29, 1995
The 3.8 release fixes the following bugs:
diff --git a/usr.sbin/mrouted/VERSION b/usr.sbin/mrouted/VERSION
new file mode 100644
index 000000000000..a0ba149f7950
--- /dev/null
+++ b/usr.sbin/mrouted/VERSION
@@ -0,0 +1 @@
+3.9-beta3+IOS12
diff --git a/usr.sbin/mrouted/callout.c b/usr.sbin/mrouted/callout.c
index 358c7ae86a21..fe23170b0675 100644
--- a/usr.sbin/mrouted/callout.c
+++ b/usr.sbin/mrouted/callout.c
@@ -7,22 +7,25 @@
* Leland Stanford Junior University.
*
*
- * $Id: callout.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * callout.c,v 3.8.4.8 1998/01/06 01:58:45 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+callout.c,v 3.8.4.8 1998/01/06 01:58:45 fenner Exp $";
+#endif
+
/* 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;
-
struct timeout_q {
struct timeout_q *next; /* next event */
int id;
cfunc_t func; /* function to call */
- char *data; /* func's data */
+ void *data; /* func's data */
int time; /* time offset to next event*/
};
@@ -38,46 +41,63 @@ callout_init()
Q = (struct timeout_q *) 0;
}
+void
+free_all_callouts()
+{
+ struct timeout_q *p;
+
+ while (Q) {
+ p = Q;
+ Q = Q->next;
+ free(p);
+ }
+}
+
/*
- * signal handler for SIGALARM that is called once every second
+ * elapsed_time seconds have passed; perform all the events that should
+ * happen.
*/
void
-age_callout_queue()
+age_callout_queue(elapsed_time)
+ int elapsed_time;
{
struct timeout_q *ptr;
-
- if (in_callout)
- return;
+ int i = 0;
- in_callout = 1;
- ptr = Q;
-
- while (ptr) {
- if (!ptr->time) {
- /* timeout has happened */
+ for (ptr = Q; ptr; ptr = Q, i++) {
+ if (ptr->time > elapsed_time) {
+ ptr->time -= elapsed_time;
+ return;
+ } else {
+ elapsed_time -= ptr->time;
Q = Q->next;
-
- in_callout = 0;
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "about to call timeout %d (#%d)", ptr->id, i);
if (ptr->func)
ptr->func(ptr->data);
- in_callout = 1;
-
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;
}
+/*
+ * Return in how many seconds age_callout_queue() would like to be called.
+ * Return -1 if there are no events pending.
+ */
+int
+timer_nextTimer()
+{
+ if (Q) {
+ if (Q->time < 0) {
+ log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
+ Q->time);
+ return 0;
+ }
+ return Q->time;
+ }
+ return -1;
+}
/*
* sets the timer
@@ -86,20 +106,15 @@ 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 */
+ void *data; /* what to call the timeout function with */
{
struct timeout_q *ptr, *node, *prev;
-
- if (in_callout)
- return -1;
-
- in_callout = 1;
+ int i = 0;
/* create a node */
node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
if (node == 0) {
log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
- in_callout = 0;
return -1;
}
node->func = action;
@@ -129,7 +144,8 @@ timer_setTimer(delay, action, data)
prev->next = node;
ptr->time -= node->time;
print_Q();
- in_callout = 0;
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
return node->id;
} else {
/* keep moving */
@@ -138,29 +154,46 @@ timer_setTimer(delay, action, data)
prev = ptr;
ptr = ptr->next;
}
+ i++;
}
prev->next = node;
}
print_Q();
- in_callout = 0;
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
return node->id;
}
+/* returns the time until the timer is scheduled */
+int
+timer_leftTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left = 0;
+
+ if (!timer_id)
+ return -1;
-/* clears the associated timer */
-void
+ for (ptr = Q; ptr; ptr = ptr->next) {
+ left += ptr->time;
+ if (ptr->id == timer_id)
+ return left;
+ }
+ return -1;
+}
+
+/* clears the associated timer. Returns 1 if succeeded. */
+int
timer_clearTimer(timer_id)
int timer_id;
{
struct timeout_q *ptr, *prev;
+ int i = 0;
- if (in_callout)
- return;
if (!timer_id)
- return;
+ return 0;
- in_callout = 1;
-
prev = ptr = Q;
/*
@@ -183,17 +216,22 @@ timer_clearTimer(timer_id)
if (ptr->next != 0)
(ptr->next)->time += ptr->time;
- free(ptr->data);
+ if (ptr->data)
+ free(ptr->data);
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i);
free(ptr);
print_Q();
- in_callout = 0;
- return;
+ return 1;
}
prev = ptr;
ptr = ptr->next;
+ i++;
}
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i);
print_Q();
- in_callout = 0;
+ return 0;
}
#ifdef IGMP_DEBUG
@@ -205,22 +243,8 @@ print_Q()
{
struct timeout_q *ptr;
- for(ptr = Q; ptr; ptr = ptr->next)
- log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time);
+ IF_DEBUG(DEBUG_TIMEOUT)
+ for (ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time);
}
#endif /* IGMP_DEBUG */
-int
-secs_remaining( timer_id)
- int timer_id;
-{
- struct timeout_q *ptr;
- int left=0;
-
- for (ptr = Q; ptr && ptr->id != timer_id; ptr = ptr->next)
- left += ptr->time;
-
- if (!ptr) /* not found */
- return 0;
-
- return left + ptr->time;
-}
diff --git a/usr.sbin/mrouted/cfparse.y b/usr.sbin/mrouted/cfparse.y
index ab5b89f30ea8..b79b687d53ee 100644
--- a/usr.sbin/mrouted/cfparse.y
+++ b/usr.sbin/mrouted/cfparse.y
@@ -4,7 +4,7 @@
*
* Written by Bill Fenner, NRL, 1994
*
- * $Id: cfparse.y,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * cfparse.y,v 3.8.4.30 1998/03/01 01:48:58 fenner Exp
*/
#include <stdio.h>
#ifdef __STDC__
@@ -29,19 +29,23 @@ int yyparse __P((void));
static FILE *f;
-extern int udp_socket;
char *configfilename = _PATH_MROUTED_CONF;
extern int cache_lifetime;
-extern int max_prune_lifetime;
+extern int prune_lifetime;
+
+/* imported from config.c, with slight memory leak */
+extern struct ifconf ifc;
+
+int allow_black_holes = 0;
static int lineno;
-static struct ifreq ifbuf[32];
-static struct ifconf ifc;
static struct uvif *v;
-static int order;
+static int order, state;
+static int noflood = 0;
+static int rexmit = VIFF_REXMIT_PRUNES;
struct addrmask {
u_int32 addr;
@@ -66,12 +70,17 @@ int numbounds = 0; /* Number of named boundaries */
char *ptr;
struct addrmask addrmask;
u_int32 addr;
+ struct vf_element *filterelem;
};
-%token CACHE_LIFETIME PRUNING
+%token CACHE_LIFETIME PRUNE_LIFETIME PRUNING BLACK_HOLE NOFLOOD
%token PHYINT TUNNEL NAME
-%token DISABLE IGMPV1 SRCRT
-%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET
+%token DISABLE IGMPV1 SRCRT BESIDE
+%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET ADVERT_METRIC
+%token FILTER ACCEPT DENY EXACT BIDIR REXMIT_PRUNES REXMIT_PRUNES2
+%token PASSIVE ALLOW_NONPRUNERS
+%token NOTRANSIT BLASTER FORCE_LEAF
+%token PRUNE_LIFETIME2 NOFLOOD2
%token SYSNAM SYSCONTACT SYSVERSION SYSLOCATION
%token <num> BOOLEAN
%token <num> NUMBER
@@ -81,6 +90,7 @@ int numbounds = 0; /* Number of named boundaries */
%type <addr> interface addrname
%type <addrmask> bound boundary addrmask
+%type <filterelem> filter filtlist filtelement filtelem
%start conf
@@ -98,6 +108,8 @@ stmt : error
vifi_t vifi;
+ state++;
+
if (order)
fatal("phyints must appear before tunnels");
@@ -127,11 +139,9 @@ stmt : error
fatal("Tunnel local address %s is not mine",
inet_fmt($2, s1));
- strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
- if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
- fatal("ioctl SIOCGIFFLAGS on %s",ffr.ifr_name);
- if (ffr.ifr_flags & IFF_LOOPBACK)
- fatal("Tunnel local address %s is a loopback interface",
+ if (((ntohl($2) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) ==
+ IN_LOOPBACKNET)
+ fatal("Tunnel local address %s is a loopback address",
inet_fmt($2, s1));
if (ifconfaddr(&ifc, $3) != 0)
@@ -147,28 +157,25 @@ stmt : error
inet_fmt($3, s1));
} else if (!(v->uv_flags & VIFF_DISABLED)) {
if (($3 & v->uv_subnetmask) == v->uv_subnet)
- fatal("Unnecessary tunnel to %s",
- inet_fmt($3,s1));
+ fatal("Unnecessary tunnel to %s, same subnet as vif %d (%s)",
+ inet_fmt($3,s1), vifi, v->uv_name);
}
if (numvifs == MAXVIFS)
fatal("too many vifs");
+ strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
+ fatal("ioctl SIOCGIFFLAGS on %s", ffr.ifr_name);
+
v = &uvifs[numvifs];
- v->uv_flags = VIFF_TUNNEL;
- v->uv_metric = DEFAULT_METRIC;
- v->uv_rate_limit= DEFAULT_TUN_RATE_LIMIT;
- v->uv_threshold = DEFAULT_THRESHOLD;
+ zero_vif(v, 1);
+ v->uv_flags = VIFF_TUNNEL | rexmit | noflood;
+ v->uv_flags |= VIFF_OTUNNEL; /*XXX*/
v->uv_lcl_addr = $2;
v->uv_rmt_addr = $3;
- v->uv_subnet = 0;
- v->uv_subnetmask= 0;
- v->uv_subnetbcast= 0;
+ v->uv_dst_addr = $3;
strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
- v->uv_groups = NULL;
- v->uv_neighbors = NULL;
- v->uv_acl = NULL;
- v->uv_addrs = NULL;
if (!(ffr.ifr_flags & IFF_UP)) {
v->uv_flags |= VIFF_DOWN;
@@ -177,17 +184,101 @@ stmt : error
}
tunnelmods
{
- log(LOG_INFO, 0,
- "installing tunnel from %s to %s as vif #%u - rate=%d",
- inet_fmt($2, s1), inet_fmt($3, s2),
- numvifs, v->uv_rate_limit);
- ++numvifs;
+ if (!(v->uv_flags & VIFF_OTUNNEL)) {
+ init_ipip_on_vif(v);
+ }
+
+ log(LOG_INFO, 0,
+ "installing tunnel from %s to %s as vif #%u - rate=%d",
+ inet_fmt($2, s1), inet_fmt($3, s2),
+ numvifs, v->uv_rate_limit);
+
+ ++numvifs;
+
}
- | PRUNING BOOLEAN { pruning = $2; }
- | CACHE_LIFETIME NUMBER { cache_lifetime = $2;
- max_prune_lifetime = cache_lifetime * 2;
+ | CACHE_LIFETIME NUMBER {
+
+ if ($2 < MIN_CACHE_LIFETIME) {
+ warn("cache_lifetime %d must be at least %d",
+ $2, MIN_CACHE_LIFETIME);
+ } else {
+ cache_lifetime = $2;
+ }
+
+ }
+ | PRUNE_LIFETIME NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ prune_lifetime = $2;
+ }
+
+ }
+ | PRUNING BOOLEAN {
+
+ if ($2 != 1) {
+ warn("Disabling pruning is no longer supported");
+ }
+
+ }
+ | BLACK_HOLE {
+#ifdef ALLOW_BLACK_HOLES
+ allow_black_holes = 1;
+#endif
}
+ /*
+ * Turn off initial flooding (until subordinateness is learned
+ * via route exchange) on all phyints and set the default for
+ * all further tunnels.
+ */
+ | NOFLOOD {
+
+ vifi_t vifi;
+
+ noflood = VIFF_NOFLOOD;
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ /*
+ * Turn on prune retransmission on all interfaces.
+ * Tunnels default to retransmitting, so this just
+ * needs to turn on phyints.
+ */
+ | REXMIT_PRUNES {
+
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ /*
+ * If true, do as above. If false, no need to turn
+ * it off for phyints since they default to not
+ * rexmit; need to set flag to not rexmit on tunnels.
+ */
+ | REXMIT_PRUNES BOOLEAN {
+
+ if ($2) {
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ rexmit = 0;
+ }
+
+ }
| NAME STRING boundary { if (numbounds >= MAXBOUNDS) {
fatal("Too many named boundaries (max %d)", MAXBOUNDS);
}
@@ -223,6 +314,16 @@ tunnelmods : /* empty */
;
tunnelmod : mod
+ | BESIDE { v->uv_flags |= VIFF_OTUNNEL; }
+ | BESIDE BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_OTUNNEL;
+ } else {
+ v->uv_flags &= ~VIFF_OTUNNEL;
+ }
+
+ }
| SRCRT { fatal("Source-route tunnels not supported"); }
;
@@ -274,6 +375,20 @@ ifmod : mod
warn("Expected address after altnet keyword, ignored");
}
+ | FORCE_LEAF {
+
+ v->uv_flags |= VIFF_FORCE_LEAF;
+
+ }
+ | FORCE_LEAF BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_FORCE_LEAF;
+ } else {
+ v->uv_flags &= ~VIFF_FORCE_LEAF;
+ }
+
+ }
;
mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255)
@@ -294,6 +409,15 @@ mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255)
warn("Expected number after metric keyword, ignored");
}
+ | ADVERT_METRIC NUMBER { if ($2 < 0 || $2 > UNREACHABLE - 1)
+ fatal("Invalid advert_metric %d", $2);
+ v->uv_admetric = $2;
+ }
+ | ADVERT_METRIC {
+
+ warn("Expected number after advert_metric keyword, ignored");
+
+ }
| RATE_LIMIT NUMBER { if ($2 > MAX_RATE_LIMIT)
fatal("Invalid rate_limit %d",$2);
v->uv_rate_limit = $2;
@@ -324,6 +448,122 @@ mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255)
warn("Expected boundary spec after boundary keyword, ignored");
}
+ | REXMIT_PRUNES2 {
+
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ | REXMIT_PRUNES2 BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ v->uv_flags &= ~VIFF_REXMIT_PRUNES;
+ }
+
+ }
+ | PASSIVE {
+
+ v->uv_flags |= VIFF_PASSIVE;
+
+ }
+ | NOFLOOD2 {
+
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ | NOTRANSIT {
+
+ v->uv_flags |= VIFF_NOTRANSIT;
+
+ }
+ | BLASTER {
+
+ v->uv_flags |= VIFF_BLASTER;
+ blaster_alloc(v - uvifs);
+
+ }
+ | ALLOW_NONPRUNERS {
+
+ v->uv_flags |= VIFF_ALLOW_NONPRUNERS;
+
+ }
+ | PRUNE_LIFETIME2 NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ v->uv_prune_lifetime = $2;
+ }
+
+ }
+ | ACCEPT filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_ACCEPT;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_ACCEPT) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | ACCEPT {
+
+ warn("Expected filter spec after accept keyword, ignored");
+
+ }
+ | DENY filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_DENY;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_DENY) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | DENY {
+
+ warn("Expected filter spec after deny keyword, ignored");
+
+ }
+ | BIDIR {
+
+ if (v->uv_filter == NULL) {
+ fatal("bidir goes after filters");
+ }
+ v->uv_filter->vf_flags |= VFF_BIDIR;
+
+ }
;
interface : ADDR { $$ = $1; }
@@ -365,6 +605,9 @@ bound : boundary { $$ = $1; }
boundary : ADDRMASK {
+#ifdef ALLOW_BLACK_HOLES
+ if (!allow_black_holes)
+#endif
if ((ntohl($1.addr) & 0xff000000) != 0xef000000) {
fatal("Boundaries must be 239.x.x.x, not %s/%d",
inet_fmt($1.addr, s1), $1.mask);
@@ -377,6 +620,35 @@ boundary : ADDRMASK {
addrmask : ADDRMASK { $$ = $1; }
| ADDR { $$.addr = $1; $$.mask = 0; }
;
+
+filter : filtlist { $$ = $1; }
+ | STRING { fatal("named filters no implemented yet"); }
+ ;
+
+filtlist : filtelement { $$ = $1; }
+ | filtelement filtlist { $1->vfe_next = $2; $$ = $1; }
+ ;
+
+filtelement : filtelem { $$ = $1; }
+ | filtelem EXACT { $1->vfe_flags |= VFEF_EXACT; $$ = $1; }
+ ;
+
+filtelem : ADDRMASK {
+
+ struct vf_element *vfe;
+
+ vfe = (struct vf_element *)malloc(sizeof(struct vf_element));
+ if (vfe == NULL)
+ fatal("out of memory");
+
+ vfe->vfe_addr = $1.addr;
+ VAL_TO_MASK(vfe->vfe_mask, $1.mask);
+ vfe->vfe_flags = 0;
+ vfe->vfe_next = NULL;
+
+ $$ = vfe;
+
+ }
%%
#ifdef __STDC__
static void
@@ -442,7 +714,6 @@ next_word()
{
static char buf[1024];
static char *p=NULL;
- extern FILE *f;
char *q;
while (1) {
@@ -481,45 +752,75 @@ next_word()
}
}
+/*
+ * List of keywords. Must have an empty record at the end to terminate
+ * list. If a second value is specified, the first is used at the beginning
+ * of the file and the second is used while parsing interfaces (e.g. after
+ * the first "phyint" or "tunnel" keyword).
+ */
+static struct keyword {
+ char *word;
+ int val1;
+ int val2;
+} words[] = {
+ { "cache_lifetime", CACHE_LIFETIME },
+ { "prune_lifetime", PRUNE_LIFETIME, PRUNE_LIFETIME2 },
+ { "pruning", PRUNING },
+ { "phyint", PHYINT },
+ { "tunnel", TUNNEL },
+ { "disable", DISABLE },
+ { "metric", METRIC },
+ { "advert_metric", ADVERT_METRIC },
+ { "threshold", THRESHOLD },
+ { "rate_limit", RATE_LIMIT },
+ { "force_leaf", FORCE_LEAF },
+ { "srcrt", SRCRT },
+ { "sourceroute", SRCRT },
+ { "boundary", BOUNDARY },
+ { "netmask", NETMASK },
+ { "igmpv1", IGMPV1 },
+ { "altnet", ALTNET },
+ { "name", NAME },
+ { "accept", ACCEPT },
+ { "deny", DENY },
+ { "exact", EXACT },
+ { "bidir", BIDIR },
+ { "allow_nonpruners", ALLOW_NONPRUNERS },
+#ifdef ALLOW_BLACK_HOLES
+ { "allow_black_holes", BLACK_HOLE },
+#endif
+ { "noflood", NOFLOOD, NOFLOOD2},
+ { "notransit", NOTRANSIT },
+ { "blaster", BLASTER },
+ { "rexmit_prunes", REXMIT_PRUNES, REXMIT_PRUNES2 },
+ { "passive", PASSIVE },
+ { "beside", BESIDE },
+#ifdef SNMP
+ { "sysName", SYSNAM },
+ { "sysContact", SYSCONTACT },
+ { "sysVersion", SYSVERSION },
+ { "sysLocation", SYSLOCATION },
+#endif
+ { NULL, 0 }
+};
+
+
static int
yylex()
{
int n;
u_int32 addr;
char *q;
+ struct keyword *w;
if ((q = next_word()) == NULL) {
return 0;
}
- if (!strcmp(q,"cache_lifetime"))
- return CACHE_LIFETIME;
- if (!strcmp(q,"pruning"))
- return PRUNING;
- if (!strcmp(q,"phyint"))
- return PHYINT;
- if (!strcmp(q,"tunnel"))
- return TUNNEL;
- if (!strcmp(q,"disable"))
- return DISABLE;
- if (!strcmp(q,"metric"))
- return METRIC;
- if (!strcmp(q,"threshold"))
- return THRESHOLD;
- if (!strcmp(q,"rate_limit"))
- return RATE_LIMIT;
- if (!strcmp(q,"srcrt") || !strcmp(q,"sourceroute"))
- return SRCRT;
- if (!strcmp(q,"boundary"))
- return BOUNDARY;
- if (!strcmp(q,"netmask"))
- return NETMASK;
- if (!strcmp(q,"igmpv1"))
- return IGMPV1;
- if (!strcmp(q,"altnet"))
- return ALTNET;
- if (!strcmp(q,"name"))
- return NAME;
+ for (w = words; w->word; w++)
+ if (!strcmp(q, w->word))
+ return (state && w->val2) ? w->val2 : w->val1;
+
if (!strcmp(q,"on") || !strcmp(q,"yes")) {
yylval.num = 1;
return BOOLEAN;
@@ -528,8 +829,13 @@ yylex()
yylval.num = 0;
return BOOLEAN;
}
+ if (!strcmp(q,"default")) {
+ yylval.addrmask.mask = 0;
+ yylval.addrmask.addr = 0;
+ return ADDRMASK;
+ }
if (sscanf(q,"%[.0-9]/%d%c",s1,&n,s2) == 2) {
- if ((addr = inet_parse(s1)) != 0xffffffff) {
+ if ((addr = inet_parse(s1,1)) != 0xffffffff) {
yylval.addrmask.mask = n;
yylval.addrmask.addr = addr;
return ADDRMASK;
@@ -537,7 +843,7 @@ yylex()
/* fall through to returning STRING */
}
if (sscanf(q,"%[.0-9]%c",s1,s2) == 1) {
- if ((addr = inet_parse(s1)) != 0xffffffff &&
+ if ((addr = inet_parse(s1,4)) != 0xffffffff &&
inet_valid_host(addr)) {
yylval.addr = addr;
return ADDR;
@@ -552,14 +858,6 @@ yylex()
return NUMBER;
}
#ifdef SNMP
- if (!strcmp(q,"sysName"))
- return SYSNAM;
- if (!strcmp(q,"sysContact"))
- return SYSCONTACT;
- if (!strcmp(q,"sysVersion"))
- return SYSVERSION;
- if (!strcmp(q,"sysLocation"))
- return SYSLOCATION;
if (*q=='"') {
if (q[ strlen(q)-1 ]=='"')
q[ strlen(q)-1 ]='\0'; /* trash trailing quote */
@@ -574,9 +872,8 @@ yylex()
void
config_vifs_from_file()
{
- extern FILE *f;
-
order = 0;
+ state = 0;
numbounds = 0;
lineno = 0;
@@ -586,11 +883,6 @@ config_vifs_from_file()
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");
-
yyparse();
fclose(f);
@@ -623,7 +915,7 @@ ifconfaddr(ifcp, a)
if (ifrp->ifr_addr.sa_family == AF_INET &&
((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
return (ifrp);
-#if (defined(BSD) && (BSD >= 199006))
+#ifdef HAVE_SA_LEN
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
++ifrp;
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
index 1147412b904e..0b5ff7bcdc0d 100644
--- a/usr.sbin/mrouted/config.c
+++ b/usr.sbin/mrouted/config.c
@@ -7,12 +7,18 @@
* Leland Stanford Junior University.
*
*
- * $Id: config.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * config.c,v 3.8.4.10 1998/01/06 01:57:41 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+config.c,v 3.8.4.10 1998/01/06 01:57:41 fenner Exp $";
+#endif
+
+struct ifconf ifc;
/*
* Query the kernel to find network interfaces that are multicast-capable
@@ -21,28 +27,48 @@
void
config_vifs_from_kernel()
{
- struct ifreq ifbuf[32];
struct ifreq *ifrp, *ifend;
- struct ifconf ifc;
register struct uvif *v;
register vifi_t vifi;
int n;
u_int32 addr, mask, subnet;
short flags;
+ int num_ifreq = 32;
+
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ while (ifc.ifc_buf) {
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if ((num_ifreq * sizeof(struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
- ifc.ifc_buf = (char *)ifbuf;
- ifc.ifc_len = sizeof(ifbuf);
- if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
- log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ }
+ if (ifc.ifc_buf == NULL)
+ log(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory");
- ifrp = (struct ifreq *)ifbuf;
- ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len);
+ ifrp = (struct ifreq *)ifc.ifc_buf;
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
/*
* Loop through all of the interfaces.
*/
for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
struct ifreq ifr;
-#if BSD >= 199006
+#ifdef HAVE_SA_LEN
n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
if (n < sizeof(*ifrp))
n = sizeof(*ifrp);
@@ -97,6 +123,12 @@ config_vifs_from_kernel()
* one already installed in the uvifs array.
*/
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (strcmp(v->uv_name, ifr.ifr_name) == 0) {
+ log(LOG_DEBUG, 0, "skipping %s (%s on subnet %s) (alias for vif#%u?)",
+ v->uv_name, inet_fmt(addr, s1),
+ inet_fmts(subnet, mask, s2), vifi);
+ break;
+ }
if ((addr & v->uv_subnetmask) == v->uv_subnet ||
(v->uv_subnet & mask) == subnet) {
log(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
@@ -114,20 +146,15 @@ config_vifs_from_kernel()
continue;
}
v = &uvifs[numvifs];
- v->uv_flags = 0;
- v->uv_metric = DEFAULT_METRIC;
- v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
- v->uv_threshold = DEFAULT_THRESHOLD;
+ zero_vif(v, 0);
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;
- v->uv_addrs = NULL;
+
+ if (flags & IFF_POINTOPOINT)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
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),
diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h
index 63aef773e77c..c36191f6147e 100644
--- a/usr.sbin/mrouted/defs.h
+++ b/usr.sbin/mrouted/defs.h
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: defs.h,v 3.8 1995/11/29 22:36:34 fenner Rel $
+ * defs.h,v 3.8.4.15 1998/03/01 02:51:42 fenner Exp
*/
@@ -26,12 +26,25 @@
#ifdef SYSV
#include <sys/sockio.h>
#endif
+#ifdef _AIX
+#include <time.h>
+#endif
#include <sys/time.h>
+#include <sys/uio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
#include <netinet/igmp.h>
+#ifdef __FreeBSD__ /* sigh */
+#include <osreldate.h>
+#if __FreeBSD_version >= 220000
+#define rtentry kernel_rtentry
+#include <net/route.h>
+#undef rtentry
+#endif
+#endif
#include <netinet/ip_mroute.h>
#ifdef RSRR
#include <sys/un.h>
@@ -52,6 +65,7 @@ typedef void (*cfunc_t) __P((void *));
typedef void (*ihfunc_t) __P((int, fd_set *));
#include "dvmrp.h"
+#include "igmpv2.h"
#include "vif.h"
#include "route.h"
#include "prune.h"
@@ -71,21 +85,17 @@ typedef void (*ihfunc_t) __P((int, fd_set *));
#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
-#define VENDOR_CODE 1 /* Get a new vendor code if you make significant
- * changes to mrouted. */
-
#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
+#define MROUTED_VERSION 9 /* not in DVMRP packets at all */
-#define MROUTED_VERSION 8 /* increment on local changes or bug fixes, */
- /* reset to 0 whever PROTOCOL_VERSION increments */
+#define DVMRP_CONSTANT 0x000eff00 /* constant portion of 'group' field */
-#define MROUTED_LEVEL ((MROUTED_VERSION << 8) | PROTOCOL_VERSION | \
- ((NF_PRUNE | NF_GENID | NF_MTRACE) << 16) | \
- (VENDOR_CODE << 24))
+#define MROUTED_LEVEL (DVMRP_CONSTANT | PROTOCOL_VERSION)
/* for IGMP 'group' field of DVMRP messages */
#define LEAF_FLAGS (( vifs_with_neighbors == 1 ) ? 0x010000 : 0)
/* more for IGMP 'group' field of DVMRP messages */
+
#define DEL_RTE_GROUP 0
#define DEL_ALL_ROUTES 1
/* for Deleting kernel table entries */
@@ -109,7 +119,10 @@ typedef void (*ihfunc_t) __P((int, fd_set *));
#define bcopy(a, b, c) memcpy(b, a, c)
#define bzero(s, n) memset((s), 0, (n))
#define setlinebuf(s) setvbuf(s, NULL, _IOLBF, 0)
-#define signal(s,f) sigset(s,f)
+#endif
+
+#if defined(_AIX) || (defined(BSD) && (BSD >= 199103))
+#define HAVE_SA_LEN
#endif
/*
@@ -127,10 +140,27 @@ extern u_int32 allrtrs_group;
extern u_int32 dvmrp_group;
extern u_int32 dvmrp_genid;
-#define DEFAULT_DEBUG 2 /* default if "-d" given without value */
+#define IF_DEBUG(l) if (debug && debug & (l))
+
+#define DEBUG_PKT 0x0001
+#define DEBUG_PRUNE 0x0002
+#define DEBUG_ROUTE 0x0004
+#define DEBUG_PEER 0x0008
+#define DEBUG_CACHE 0x0010
+#define DEBUG_TIMEOUT 0x0020
+#define DEBUG_IF 0x0040
+#define DEBUG_MEMBER 0x0080
+#define DEBUG_TRACE 0x0100
+#define DEBUG_IGMP 0x0200
+#define DEBUG_RTDETAIL 0x0400
+#define DEBUG_KERN 0x0800
+#define DEBUG_RSRR 0x1000
+#define DEBUG_ICMP 0x2000
+
+#define DEFAULT_DEBUG 0x02de /* default if "-d" given without value */
extern int debug;
-extern u_char pruning;
+extern int did_final_init;
extern int routes_changed;
extern int delay_change_reports;
@@ -160,81 +190,134 @@ extern char * sys_errlist[];
#define MRT_DEL_VIF DVMRP_DEL_VIF
#define MRT_ADD_MFC DVMRP_ADD_MFC
#define MRT_DEL_MFC DVMRP_DEL_MFC
+#endif
+#ifndef IGMP_PIM
#define IGMP_PIM 0x14
#endif
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP 4
+#endif
+
+/*
+ * The original multicast releases defined
+ * IGMP_HOST_{MEMBERSHIP_QUERY,MEMBERSHIP_REPORT,NEW_MEMBERSHIP_REPORT
+ * ,LEAVE_MESSAGE}. Later releases removed the HOST and inserted
+ * the IGMP version number. NetBSD inserted the version number in
+ * a different way. mrouted uses the new names, so we #define them
+ * to the old ones if needed.
+ */
+#if !defined(IGMP_MEMBERSHIP_QUERY) && defined(IGMP_HOST_MEMBERSHIP_QUERY)
+#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY
+#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE
+#endif
+#ifndef IGMP_V1_MEMBERSHIP_REPORT
+#ifdef IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT
+#endif
+#ifdef IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT
+#endif
+#endif
+
+/*
+ * NetBSD also renamed the mtrace types.
+ */
+#if !defined(IGMP_MTRACE_RESP) && defined(IGMP_MTRACE_REPLY)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
/* main.c */
+extern char * scaletime __P((u_long));
extern void log __P((int, int, char *, ...));
-extern int register_input_handler __P((int fd, ihfunc_t func));
+extern int register_input_handler __P((int, ihfunc_t));
/* igmp.c */
extern void init_igmp __P((void));
-extern void accept_igmp __P((int recvlen));
-extern void send_igmp __P((u_int32 src, u_int32 dst, int type,
- int code, u_int32 group,
- int datalen));
+extern void accept_igmp __P((int));
+extern void build_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern void send_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern char * igmp_packet_kind __P((u_int, u_int));
+extern int igmp_debug_kind __P((u_int, u_int));
+
+/* icmp.c */
+extern void init_icmp __P((void));
+
+/* ipip.c */
+extern void init_ipip __P((void));
+extern void init_ipip_on_vif __P((struct uvif *));
+extern void send_ipip __P((u_int32, u_int32, int, int, u_int32,
+ int, struct uvif *));
/* callout.c */
extern void callout_init __P((void));
-extern void age_callout_queue __P((void));
-extern int timer_setTimer __P((int delay, cfunc_t action,
- char *data));
-extern void timer_clearTimer __P((int timer_id));
+extern void free_all_callouts __P((void));
+extern void age_callout_queue __P((int));
+extern int timer_nextTimer __P((void));
+extern int timer_setTimer __P((int, cfunc_t, void *));
+extern int timer_clearTimer __P((int));
+extern int timer_leftTimer __P((int));
/* route.c */
extern void init_routes __P((void));
extern void start_route_updates __P((void));
-extern void update_route __P((u_int32 origin, u_int32 mask,
- u_int metric, u_int32 src,
- vifi_t vifi));
+extern void update_route __P((u_int32, u_int32, u_int, u_int32,
+ vifi_t, struct listaddr *));
extern void age_routes __P((void));
extern void expire_all_routes __P((void));
extern void free_all_routes __P((void));
-extern void accept_probe __P((u_int32 src, u_int32 dst,
- char *p, int datalen,
- u_int32 level));
-extern void accept_report __P((u_int32 src, u_int32 dst,
- char *p, int datalen,
- u_int32 level));
+extern void accept_probe __P((u_int32, u_int32, char *, int,
+ u_int32));
+extern void accept_report __P((u_int32, u_int32, char *, int,
+ u_int32));
extern struct rtentry * determine_route __P((u_int32 src));
-extern void report __P((int which_routes, vifi_t vifi,
- u_int32 dst));
-extern void report_to_all_neighbors __P((int which_routes));
+extern void report __P((int, vifi_t, u_int32));
+extern void report_to_all_neighbors __P((int));
extern int report_next_chunk __P((void));
-extern void add_vif_to_routes __P((vifi_t vifi));
-extern void delete_vif_from_routes __P((vifi_t vifi));
-extern void delete_neighbor_from_routes __P((u_int32 addr,
- vifi_t vifi));
+extern void blaster_alloc __P((vifi_t));
+extern void add_vif_to_routes __P((vifi_t));
+extern void delete_vif_from_routes __P((vifi_t));
+extern void add_neighbor_to_routes __P((vifi_t, int));
+extern void delete_neighbor_from_routes __P((u_int32,
+ vifi_t, int));
extern void dump_routes __P((FILE *fp));
-extern void start_route_updates __P((void));
/* vif.c */
extern void init_vifs __P((void));
+extern void zero_vif __P((struct uvif *, int));
+extern void init_installvifs __P((void));
extern void check_vif_state __P((void));
-extern vifi_t find_vif __P((u_int32 src, u_int32 dst));
+extern void send_on_vif __P((struct uvif *, u_int32, int, int));
+extern vifi_t find_vif __P((u_int32, u_int32));
extern void age_vifs __P((void));
-extern void dump_vifs __P((FILE *fp));
+extern void dump_vifs __P((FILE *));
extern void stop_all_vifs __P((void));
-extern struct listaddr *neighbor_info __P((vifi_t vifi, u_int32 addr));
-extern void accept_group_report __P((u_int32 src, u_int32 dst,
- u_int32 group, int r_type));
+extern struct listaddr *neighbor_info __P((vifi_t, u_int32));
+extern void accept_group_report __P((u_int32, u_int32,
+ u_int32, int));
extern void query_groups __P((void));
extern void probe_for_neighbors __P((void));
-extern int update_neighbor __P((vifi_t vifi, u_int32 addr,
- int msgtype, char *p, int datalen,
- u_int32 level));
-extern void accept_neighbor_request __P((u_int32 src, u_int32 dst));
-extern void accept_neighbor_request2 __P((u_int32 src,
- u_int32 dst));
-extern void accept_neighbors __P((u_int32 src, u_int32 dst,
- u_char *p, int datalen, u_int32 level));
-extern void accept_neighbors2 __P((u_int32 src, u_int32 dst,
- u_char *p, int datalen, u_int32 level));
-extern void accept_leave_message __P((u_int32 src, u_int32 dst,
- u_int32 group));
-extern void accept_membership_query __P((u_int32 src, u_int32 dst,
- u_int32 group, int tmo));
+extern struct listaddr *update_neighbor __P((vifi_t, u_int32, int, char *, int,
+ u_int32));
+extern void accept_neighbor_request __P((u_int32, u_int32));
+extern void accept_neighbor_request2 __P((u_int32, u_int32));
+extern void accept_info_request __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_info_reply __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_neighbors __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_neighbors2 __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_leave_message __P((u_int32, u_int32,
+ u_int32));
+extern void accept_membership_query __P((u_int32, u_int32,
+ u_int32, int));
/* config.c */
extern void config_vifs_from_kernel __P((void));
@@ -243,75 +326,84 @@ extern void config_vifs_from_kernel __P((void));
extern void config_vifs_from_file __P((void));
/* inet.c */
-extern int inet_valid_host __P((u_int32 naddr));
-extern int inet_valid_subnet __P((u_int32 nsubnet, u_int32 nmask));
-extern char * inet_fmt __P((u_int32 addr, char *s));
-extern char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s));
-extern u_int32 inet_parse __P((char *s));
-extern int inet_cksum __P((u_short *addr, u_int len));
+extern int inet_valid_host __P((u_int32));
+extern int inet_valid_mask __P((u_int32));
+extern int inet_valid_subnet __P((u_int32, u_int32));
+extern char * inet_fmt __P((u_int32, char *));
+extern char * inet_fmts __P((u_int32, u_int32, char *));
+extern u_int32 inet_parse __P((char *, int));
+extern int inet_cksum __P((u_short *, u_int));
/* prune.c */
extern unsigned kroutes;
-extern void add_table_entry __P((u_int32 origin, u_int32 mcastgrp));
-extern void del_table_entry __P((struct rtentry *r,
- u_int32 mcastgrp, u_int del_flag));
-extern void update_table_entry __P((struct rtentry *r));
+extern void determine_forwvifs __P((struct gtable *));
+extern void send_prune_or_graft __P((struct gtable *));
+extern void add_table_entry __P((u_int32, u_int32));
+extern void del_table_entry __P((struct rtentry *,
+ u_int32, u_int));
+extern void update_table_entry __P((struct rtentry *, u_int32));
+extern int find_src_grp __P((u_int32, u_int32, u_int32));
extern void init_ktable __P((void));
-extern void accept_prune __P((u_int32 src, u_int32 dst, char *p,
- int datalen));
-extern void steal_sources __P((struct rtentry *rt));
-extern void reset_neighbor_state __P((vifi_t vifi, u_int32 addr));
-extern int grplst_mem __P((vifi_t vifi, u_int32 mcastgrp));
-extern int scoped_addr __P((vifi_t vifi, u_int32 addr));
+extern void steal_sources __P((struct rtentry *));
+extern void reset_neighbor_state __P((vifi_t, u_int32));
+extern int grplst_mem __P((vifi_t, u_int32));
extern void free_all_prunes __P((void));
extern void age_table_entry __P((void));
-extern void dump_cache __P((FILE *fp2));
-extern void update_lclgrp __P((vifi_t vifi, u_int32 mcastgrp));
-extern void delete_lclgrp __P((vifi_t vifi, u_int32 mcastgrp));
-extern void chkgrp_graft __P((vifi_t vifi, u_int32 mcastgrp));
-extern void accept_graft __P((u_int32 src, u_int32 dst, char *p,
- int datalen));
-extern void accept_g_ack __P((u_int32 src, u_int32 dst, char *p,
- int datalen));
+extern void dump_cache __P((FILE *));
+extern void update_lclgrp __P((vifi_t, u_int32));
+extern void delete_lclgrp __P((vifi_t, u_int32));
+extern void chkgrp_graft __P((vifi_t, u_int32));
+extern void accept_prune __P((u_int32, u_int32, char *, int));
+extern void accept_graft __P((u_int32, u_int32, char *, int));
+extern void accept_g_ack __P((u_int32, u_int32, char *, int));
/* u_int is promoted u_char */
-extern void accept_mtrace __P((u_int32 src, u_int32 dst,
- u_int32 group, char *data, u_int no,
- int datalen));
+extern void accept_mtrace __P((u_int32, u_int32,
+ u_int32, char *, u_int, int));
/* kern.c */
-extern void k_set_rcvbuf __P((int bufsize));
-extern void k_hdr_include __P((int bool));
-extern void k_set_ttl __P((int t));
-extern void k_set_loop __P((int l));
-extern void k_set_if __P((u_int32 ifa));
-extern void k_join __P((u_int32 grp, u_int32 ifa));
-extern void k_leave __P((u_int32 grp, u_int32 ifa));
+extern void k_set_rcvbuf __P((int, int));
+extern void k_hdr_include __P((int));
+extern void k_set_ttl __P((int));
+extern void k_set_loop __P((int));
+extern void k_set_if __P((u_int32));
+extern void k_join __P((u_int32, u_int32));
+extern void k_leave __P((u_int32, u_int32));
extern void k_init_dvmrp __P((void));
extern void k_stop_dvmrp __P((void));
-extern void k_add_vif __P((vifi_t vifi, struct uvif *v));
-extern void k_del_vif __P((vifi_t vifi));
-extern void k_add_rg __P((u_int32 origin, struct gtable *g));
-extern int k_del_rg __P((u_int32 origin, struct gtable *g));
+extern void k_add_vif __P((vifi_t, struct uvif *));
+extern void k_del_vif __P((vifi_t));
+extern void k_add_rg __P((u_int32, struct gtable *));
+extern int k_del_rg __P((u_int32, struct gtable *));
extern int k_get_version __P((void));
#ifdef SNMP
/* prune.c */
-extern struct rtentry * snmp_find_route __P(());
-extern struct gtable * find_grp __P(());
-extern struct stable * find_grp_src __P(());
+extern struct gtable * find_grp __P((u_int32));
+extern struct stable * find_grp_src __P((struct gtable *, u_int32));
+extern int next_grp_src_mask __P((struct gtable **,
+ struct stable **, u_int32,
+ u_int32, u_int32));
+extern void refresh_sg __P((struct sioc_sg_req *, struct gtable *,
+ struct stable *));
+extern int next_child __P((struct gtable **, struct stable **,
+ u_int32, u_int32, u_int32,
+ vifi_t *));
+
+/* route.c */
+extern struct rtentry * snmp_find_route __P((u_int32, u_int32));
+extern int next_route __P((struct rtentry **, u_int32, u_int32));
+extern int next_route_child __P((struct rtentry **,
+ u_int32, u_int32, vifi_t *));
#endif
#ifdef RSRR
/* prune.c */
extern struct gtable *kernel_table;
extern struct gtable *gtp;
-extern int find_src_grp __P((u_int32 src, u_int32 mask,
- u_int32 grp));
/* rsrr.c */
extern void rsrr_init __P((void));
-extern void rsrr_read __P((int f, fd_set *rfd));
extern void rsrr_clean __P((void));
-extern void rsrr_cache_send __P((struct gtable *gt, int notify));
-extern void rsrr_cache_clean __P((struct gtable *gt));
+extern void rsrr_cache_send __P((struct gtable *, int));
+extern void rsrr_cache_clean __P((struct gtable *));
#endif /* RSRR */
diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h
index e471800588dc..e8c8a2809386 100644
--- a/usr.sbin/mrouted/dvmrp.h
+++ b/usr.sbin/mrouted/dvmrp.h
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: dvmrp.h,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * dvmrp.h,v 3.8.4.5 1997/11/18 23:25:57 fenner Exp
*/
/*
@@ -152,13 +152,8 @@
#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 LEAVE_EXPIRE_TIME 3 /* " " after receiving a leave */
-/* Note: LEAVE_EXPIRE_TIME should ideally be shorter, but the resolution of
- * the timer in mrouted doesn't allow us to make it any shorter. */
+#define NEIGHBOR_EXPIRE_TIME 30 /* time to consider neighbor gone */
+#define OLD_NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */
#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */
#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
@@ -166,9 +161,11 @@
#define MAX_RATE_LIMIT 100000 /* max rate limit */
#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */
-#define DEFAULT_TUN_RATE_LIMIT 500 /* default tunnel rate limit */
+#define DEFAULT_TUN_RATE_LIMIT 0 /* default tunnel rate limit */
#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
+#define MIN_CACHE_LIFETIME 60 /* minimum allowed cache lifetime */
+#define AVERAGE_PRUNE_LIFETIME 7200 /* average lifetime of prunes sent */
+#define MIN_PRUNE_LIFETIME 120 /* minimum allowed prune lifetime */
#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
-
-#define OLD_AGE_THRESHOLD 2
+#define PRUNE_REXMIT_VAL 3 /* initial time for prune rexmission*/
diff --git a/usr.sbin/mrouted/icmp.c b/usr.sbin/mrouted/icmp.c
new file mode 100644
index 000000000000..72efa0ac745b
--- /dev/null
+++ b/usr.sbin/mrouted/icmp.c
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ *
+ *
+ * icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp
+ */
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp $";
+#endif
+
+static int icmp_socket;
+
+static void icmp_handler __P((int, fd_set *));
+static char * icmp_name __P((struct icmp *));
+
+void
+init_icmp()
+{
+ if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
+ log(LOG_ERR, errno, "ICMP socket");
+
+ register_input_handler(icmp_socket, icmp_handler);
+
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "registering icmp socket fd %d\n", icmp_socket);
+}
+
+static void
+icmp_handler(fd, rfds)
+ int fd;
+ fd_set *rfds;
+{
+ u_char icmp_buf[RECV_BUF_SIZE];
+ struct sockaddr_in from;
+ int fromlen, recvlen, iphdrlen, ipdatalen;
+ struct icmp *icmp;
+ struct ip *ip;
+ vifi_t i;
+ struct uvif *v;
+ u_int32 src;
+
+ fromlen = sizeof(from);
+ recvlen = recvfrom(icmp_socket, icmp_buf, RECV_BUF_SIZE, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "icmp_socket recvfrom");
+ return;
+ }
+ ip = (struct ip *)icmp_buf;
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "hdr %d data %d != rcv %d", iphdrlen, ipdatalen, recvlen);
+ /* Malformed ICMP, just return. */
+ return;
+ }
+ if (ipdatalen < ICMP_MINLEN + sizeof(struct ip)) {
+ /* Not enough data for us to be interested in it. */
+ return;
+ }
+ src = ip->ip_src.s_addr;
+ icmp = (struct icmp *)(icmp_buf + iphdrlen);
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "got ICMP type %d from %s",
+ icmp->icmp_type, inet_fmt(src, s1));
+ /*
+ * Eventually:
+ * have registry of ICMP listeners, by type, code and ICMP_ID
+ * (and maybe fields of the original packet too -- maybe need a
+ * generalized packet filter!) to allow ping and traceroute
+ * from the monitoring tool.
+ */
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ case ICMP_TIMXCEED:
+ /* Look at returned packet to see if it's us sending on a tunnel */
+ ip = &icmp->icmp_ip;
+ if (ip->ip_p != IPPROTO_IGMP && ip->ip_p != IPPROTO_IPIP)
+ return;
+ for (v = uvifs, i = 0; i < numvifs; v++, i++) {
+ if (ip->ip_src.s_addr == v->uv_lcl_addr &&
+ ip->ip_dst.s_addr == v->uv_dst_addr) {
+ char *p;
+ int n;
+ /*
+ * I sent this packet on this vif.
+ */
+ n = ++v->uv_icmp_warn;
+ while (n && !(n & 1))
+ n >>= 1;
+ if (n == 1 && ((p = icmp_name(icmp)) != NULL))
+ log(LOG_WARNING, 0, "Received ICMP %s from %s %s %s on vif %d",
+ p, inet_fmt(src, s1), "for traffic sent to",
+ inet_fmt(ip->ip_dst.s_addr, s2),
+ i);
+
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Return NULL for ICMP informational messages.
+ * Return string describing the error for ICMP errors.
+ */
+static char *
+icmp_name(icmp)
+ struct icmp *icmp;
+{
+ static char retval[30];
+
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ switch (icmp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ return "network unreachable";
+ case ICMP_UNREACH_HOST:
+ return "host unreachable";
+ case ICMP_UNREACH_PROTOCOL:
+ return "protocol unreachable";
+ case ICMP_UNREACH_PORT:
+ return "port unreachable";
+ case ICMP_UNREACH_NEEDFRAG:
+ return "needs fragmentation";
+ case ICMP_UNREACH_SRCFAIL:
+ return "source route failed";
+#ifndef ICMP_UNREACH_NET_UNKNOWN
+#define ICMP_UNREACH_NET_UNKNOWN 6
+#endif
+ case ICMP_UNREACH_NET_UNKNOWN:
+ return "network unknown";
+#ifndef ICMP_UNREACH_HOST_UNKNOWN
+#define ICMP_UNREACH_HOST_UNKNOWN 7
+#endif
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ return "host unknown";
+#ifndef ICMP_UNREACH_ISOLATED
+#define ICMP_UNREACH_ISOLATED 8
+#endif
+ case ICMP_UNREACH_ISOLATED:
+ return "source host isolated";
+#ifndef ICMP_UNREACH_NET_PROHIB
+#define ICMP_UNREACH_NET_PROHIB 9
+#endif
+ case ICMP_UNREACH_NET_PROHIB:
+ return "network access prohibited";
+#ifndef ICMP_UNREACH_HOST_PROHIB
+#define ICMP_UNREACH_HOST_PROHIB 10
+#endif
+ case ICMP_UNREACH_HOST_PROHIB:
+ return "host access prohibited";
+#ifndef ICMP_UNREACH_TOSNET
+#define ICMP_UNREACH_TOSNET 11
+#endif
+ case ICMP_UNREACH_TOSNET:
+ return "bad TOS for net";
+#ifndef ICMP_UNREACH_TOSHOST
+#define ICMP_UNREACH_TOSHOST 12
+#endif
+ case ICMP_UNREACH_TOSHOST:
+ return "bad TOS for host";
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB 13
+#endif
+ case ICMP_UNREACH_FILTER_PROHIB:
+ return "prohibited by filter";
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE 14
+#endif
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ return "host precedence violation";
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
+#endif
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ return "precedence cutoff";
+ default:
+ sprintf(retval, "unreachable code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_SOURCEQUENCH:
+ return "source quench";
+ case ICMP_REDIRECT:
+ return NULL; /* XXX */
+ case ICMP_TIMXCEED:
+ switch (icmp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ return "time exceeded in transit";
+ case ICMP_TIMXCEED_REASS:
+ return "time exceeded in reassembly";
+ default:
+ sprintf(retval, "time exceeded code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_PARAMPROB:
+ switch (icmp->icmp_code) {
+#ifndef ICMP_PARAMPROB_OPTABSENT
+#define ICMP_PARAMPROB_OPTABSENT 1
+#endif
+ case ICMP_PARAMPROB_OPTABSENT:
+ return "required option absent";
+ default:
+ sprintf(retval, "parameter problem code %d", icmp->icmp_code);
+ return retval;
+ }
+ }
+ return NULL;
+}
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c
index 3c27e6d38d26..5c7b73f02368 100644
--- a/usr.sbin/mrouted/igmp.c
+++ b/usr.sbin/mrouted/igmp.c
@@ -7,12 +7,16 @@
* Leland Stanford Junior University.
*
*
- * $Id: igmp.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp $";
+#endif
/*
* Exported variables.
@@ -29,7 +33,6 @@ u_int32 dvmrp_genid; /* IGMP generation id */
* Local function definitions.
*/
/* u_char promoted to u_int */
-static char * packet_kind __P((u_int type, u_int code));
static int igmp_log_level __P((u_int type, u_int code));
/*
@@ -48,17 +51,23 @@ init_igmp()
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_rcvbuf(256*1024,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_hl = sizeof(struct ip) >> 2;
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
ip->ip_v = IPVERSION;
- ip->ip_tos = 0;
- ip->ip_off = 0;
- ip->ip_p = IPPROTO_IGMP;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IGMP;
allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
dvmrp_group = htonl(INADDR_DVMRP_GROUP);
@@ -74,29 +83,33 @@ init_igmp()
#define PIM_GRAFT 6
#define PIM_GRAFT_ACK 7
-static char *
-packet_kind(type, code)
+char *
+igmp_packet_kind(type, code)
u_int type, code;
{
+ static char unknown[20];
+
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 member report ";
- case IGMP_HOST_LEAVE_MESSAGE: return "leave message ";
+ case IGMP_MEMBERSHIP_QUERY: return "membership query ";
+ case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report ";
+ case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report ";
+ case IGMP_V2_LEAVE_GROUP: 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_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 ";
case DVMRP_INFO_REQUEST: return "info request ";
case DVMRP_INFO_REPLY: return "info reply ";
- default: return "unknown DVMRP msg ";
+ default:
+ sprintf(unknown, "unknown DVMRP %3d ", code);
+ return unknown;
}
case IGMP_PIM:
switch (code) {
@@ -108,11 +121,57 @@ packet_kind(type, code)
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 ";
+ default:
+ sprintf(unknown, "unknown PIM msg%3d", code);
+ return unknown;
}
case IGMP_MTRACE: return "IGMP trace query ";
case IGMP_MTRACE_RESP: return "IGMP trace reply ";
- default: return "unknown IGMP msg ";
+ default:
+ sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code);
+ return unknown;
+ }
+}
+
+int
+igmp_debug_kind(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP;
+ case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP;
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_PROBE: return DEBUG_PEER;
+ case DVMRP_REPORT: return DEBUG_ROUTE;
+ case DVMRP_ASK_NEIGHBORS: return 0;
+ case DVMRP_NEIGHBORS: return 0;
+ case DVMRP_ASK_NEIGHBORS2: return 0;
+ case DVMRP_NEIGHBORS2: return 0;
+ case DVMRP_PRUNE: return DEBUG_PRUNE;
+ case DVMRP_GRAFT: return DEBUG_PRUNE;
+ case DVMRP_GRAFT_ACK: return DEBUG_PRUNE;
+ case DVMRP_INFO_REQUEST: return 0;
+ case DVMRP_INFO_REPLY: return 0;
+ default: return 0;
+ }
+ case IGMP_PIM:
+ switch (code) {
+ case PIM_QUERY: return 0;
+ case PIM_REGISTER: return 0;
+ case PIM_REGISTER_STOP: return 0;
+ case PIM_JOIN_PRUNE: return 0;
+ case PIM_RP_REACHABLE: return 0;
+ case PIM_ASSERT: return 0;
+ case PIM_GRAFT: return 0;
+ case PIM_GRAFT_ACK: return 0;
+ default: return 0;
+ }
+ case IGMP_MTRACE: return DEBUG_TRACE;
+ case IGMP_MTRACE_RESP: return DEBUG_TRACE;
+ default: return DEBUG_IGMP;
}
}
@@ -153,7 +212,11 @@ accept_igmp(recvlen)
}
iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
ipdatalen = ip->ip_len;
+#endif
if (iphdrlen + ipdatalen != recvlen) {
log(LOG_WARNING, 0,
"received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
@@ -171,22 +234,23 @@ accept_igmp(recvlen)
return;
}
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
- packet_kind(igmp->igmp_type, igmp->igmp_code),
+ igmp_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:
+ case IGMP_MEMBERSHIP_QUERY:
accept_membership_query(src, dst, group, igmp->igmp_code);
return;
- case IGMP_HOST_MEMBERSHIP_REPORT:
- case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
accept_group_report(src, dst, group, igmp->igmp_type);
return;
- case IGMP_HOST_LEAVE_MESSAGE:
+ case IGMP_V2_LEAVE_GROUP:
accept_leave_message(src, dst, group);
return;
@@ -297,25 +361,31 @@ igmp_log_level(type, code)
/*
* 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'.
+ * have already placed data in that buffer, of length 'datalen'.
*/
void
-send_igmp(src, dst, type, code, group, datalen)
+build_igmp(src, dst, type, code, group, datalen)
u_int32 src, dst;
int type, code;
u_int32 group;
int datalen;
{
- struct sockaddr_in sdst;
struct ip *ip;
struct igmp *igmp;
- int setloop;
+ extern int curttl;
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;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ if (IN_MULTICAST(ntohl(dst))) {
+ ip->ip_ttl = curttl;
+ } else {
+ ip->ip_ttl = MAXTTL;
+ }
igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
igmp->igmp_type = type;
@@ -324,10 +394,28 @@ send_igmp(src, dst, type, code, group, datalen)
igmp->igmp_cksum = 0;
igmp->igmp_cksum = inet_cksum((u_short *)igmp,
IGMP_MINLEN + datalen);
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then send the message from the interface with IP address 'src' to
+ * destination 'dst'.
+ */
+void
+send_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct sockaddr_in sdst;
+ int setloop = 0;
+
+ build_igmp(src, dst, type, code, group, datalen);
if (IN_MULTICAST(ntohl(dst))) {
k_set_if(src);
- if (type != IGMP_DVMRP) {
+ if (type != IGMP_DVMRP || dst == allhosts_group) {
setloop = 1;
k_set_loop(TRUE);
}
@@ -335,11 +423,12 @@ send_igmp(src, dst, type, code, group, datalen)
bzero(&sdst, sizeof(sdst));
sdst.sin_family = AF_INET;
-#if (defined(BSD) && (BSD >= 199103))
+#ifdef HAVE_SA_LEN
sdst.sin_len = sizeof(sdst);
#endif
sdst.sin_addr.s_addr = dst;
- if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
+ if (sendto(igmp_socket, send_buf,
+ MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen, 0,
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
if (errno == ENETDOWN)
check_vif_state();
@@ -352,6 +441,8 @@ send_igmp(src, dst, type, code, group, datalen)
if (setloop)
k_set_loop(FALSE);
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
- packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2));
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2));
}
diff --git a/usr.sbin/mrouted/igmpv2.h b/usr.sbin/mrouted/igmpv2.h
new file mode 100644
index 000000000000..5f5ae27e68e8
--- /dev/null
+++ b/usr.sbin/mrouted/igmpv2.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *
+ *
+ * igmpv2.h,v 3.8.4.1 1997/11/18 23:25:58 fenner Exp
+ */
+
+/*
+ * Constants for IGMP Version 2. Several of these, especially the
+ * robustness variable, should be variables and not constants.
+ */
+#define IGMP_ROBUSTNESS_VARIABLE 2
+#define IGMP_QUERY_INTERVAL 125
+#define IGMP_QUERY_RESPONSE_INTERVAL 10
+#define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL)
+#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL / 2)
+ /* Round to the nearest TIMER_INTERVAL */
+#define IGMP_STARTUP_QUERY_INTERVAL (((IGMP_QUERY_INTERVAL / 4) \
+ / TIMER_INTERVAL) * TIMER_INTERVAL)
+#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL 1
+#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+
+/*
+ * OLD_AGE_THRESHOLD is the number of IGMP_QUERY_INTERVAL's to remember the
+ * presence of an IGMPv1 group member. According to the IGMPv2 specification,
+ * routers remember this presence for [Robustness Variable] * [Query Interval] +
+ * [Query Response Interval]. However, OLD_AGE_THRESHOLD is in units of
+ * [Query Interval], so doesn't have sufficient resolution to represent
+ * [Query Response Interval]. When the timer mechanism gets an efficient
+ * method of refreshing timers, this should get fixed.
+ */
+#define OLD_AGE_THRESHOLD IGMP_ROBUSTNESS_VARIABLE
diff --git a/usr.sbin/mrouted/inet.c b/usr.sbin/mrouted/inet.c
index b60e3854da35..ecbef3d1aaf9 100644
--- a/usr.sbin/mrouted/inet.c
+++ b/usr.sbin/mrouted/inet.c
@@ -7,12 +7,16 @@
* Leland Stanford Junior University.
*
*
- * $Id: inet.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * inet.c,v 3.8.4.2 1998/01/06 01:57:44 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+inet.c,v 3.8.4.2 1998/01/06 01:57:44 fenner Exp $";
+#endif
/*
* Exported variables.
@@ -155,15 +159,17 @@ inet_fmts(addr, mask, s)
* with "255.255.255.255".)
*/
u_int32
-inet_parse(s)
+inet_parse(s,n)
char *s;
+ int n;
{
u_int32 a = 0;
- u_int a0, a1, a2, a3;
+ u_int a0 = 0, a1 = 0, a2 = 0, a3 = 0;
+ int i;
char c;
- if (sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c) != 4 ||
- a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
+ i = sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c);
+ if (i < n || i > 4 || a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
return (0xffffffff);
((u_char *)&a)[0] = a0;
diff --git a/usr.sbin/mrouted/ipip.c b/usr.sbin/mrouted/ipip.c
new file mode 100644
index 000000000000..6e88d932d3b4
--- /dev/null
+++ b/usr.sbin/mrouted/ipip.c
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ *
+ *
+ * ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp
+ */
+
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp $";
+#endif
+
+/*
+ * Exported variables.
+ */
+#ifdef notyet
+int raw_socket; /* socket for raw network I/O */
+#endif
+/*
+ *XXX For now, we just use the IGMP socket to send packets.
+ * This is legal in BSD, because the protocol # is not checked
+ * on raw sockets. The k_* interfaces need to gain a socket
+ * argument so that we can call them on the raw_socket also.
+ */
+#define raw_socket igmp_socket
+
+/*
+ * Private variables.
+ */
+static int rawid = 0;
+
+/*
+ * Open and initialize the raw socket.
+ */
+void
+init_ipip()
+{
+#ifdef notyet
+ if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ log(LOG_ERR, errno, "Raw IP socket");
+#endif
+}
+
+/*
+ * Allocate and fill in static IP header for encapsulating on a tunnel.
+ */
+void
+init_ipip_on_vif(v)
+ struct uvif *v;
+{
+ struct ip *ip;
+
+ ip = v->uv_encap_hdr = (struct ip *)malloc(sizeof(struct ip));
+ if (ip == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IPIP;
+ ip->ip_src.s_addr = v->uv_lcl_addr;
+ ip->ip_dst.s_addr = v->uv_rmt_addr;
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then fill in the fields of the IP packet that build_igmp() left for the
+ * kernel to fill in, and encapsulate the original packet with the
+ * pre-created ip header for this vif.
+ */
+void
+send_ipip(src, dst, type, code, group, datalen, v)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+ struct uvif *v;
+{
+ struct msghdr msg;
+ struct iovec iov[2];
+ struct sockaddr_in sdst;
+ struct ip *ip;
+
+ build_igmp(src, dst, type, code, group, datalen);
+ ip = (struct ip *)send_buf;
+#ifndef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ ip->ip_id = htons(rawid++);
+ ip->ip_sum = 0;
+ ip->ip_sum = inet_cksum((u_short *)ip, ip->ip_hl << 2);
+
+ ip = v->uv_encap_hdr;
+ ip->ip_len = 2 * MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr = ip->ip_dst;
+
+ iov[0].iov_base = (caddr_t)v->uv_encap_hdr;
+ iov[0].iov_len = sizeof(struct ip);
+ iov[1].iov_base = (caddr_t)send_buf;
+ iov[1].iov_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+
+ bzero(&msg, sizeof(msg));
+ msg.msg_name = (caddr_t)&sdst;
+ msg.msg_namelen = sizeof(sdst);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ if (sendmsg(raw_socket, &msg, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno,
+ "sendmsg to %s on %s",
+ inet_fmt(sdst.sin_addr.s_addr, s1), inet_fmt(src, s2));
+ }
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s encaped to %s",
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2),
+ inet_fmt(sdst.sin_addr.s_addr, s3));
+}
diff --git a/usr.sbin/mrouted/kern.c b/usr.sbin/mrouted/kern.c
index 2a64e5c13916..64844766c854 100644
--- a/usr.sbin/mrouted/kern.c
+++ b/usr.sbin/mrouted/kern.c
@@ -7,19 +7,58 @@
* Leland Stanford Junior University.
*
*
- * $Id: kern.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * kern.c,v 3.8.4.10 1998/01/06 02:00:51 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+kern.c,v 3.8.4.10 1998/01/06 02:00:51 fenner Exp $";
+#endif
+
+int curttl = 0;
-void k_set_rcvbuf(bufsize)
+void k_set_rcvbuf(bufsize, minsize)
int bufsize;
+ int minsize;
{
+ int delta = bufsize / 2;
+ int iter = 0;
+
+ /*
+ * Set the socket buffer. If we can't set it as large as we
+ * want, search around to try to find the highest acceptable
+ * value. The highest acceptable value being smaller than
+ * minsize is a fatal error.
+ */
if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
- (char *)&bufsize, sizeof(bufsize)) < 0)
- log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize);
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ while (1) {
+ iter++;
+ if (delta > 1)
+ delta /= 2;
+
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ } else {
+ if (delta < 1024)
+ break;
+ bufsize += delta;
+ }
+ }
+ if (bufsize < minsize) {
+ log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
+ bufsize, minsize);
+ /*NOTREACHED*/
+ }
+ }
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations",
+ bufsize, iter);
}
@@ -37,12 +76,15 @@ void k_hdr_include(bool)
void k_set_ttl(t)
int t;
{
+#ifndef RAW_OUTPUT_IS_RAW
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);
+#endif
+ curttl = t;
}
@@ -141,7 +183,7 @@ void k_add_vif(vifi, v)
if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF,
(char *)&vc, sizeof(vc)) < 0)
- log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF");
+ log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF on vif %d", vifi);
}
@@ -150,7 +192,7 @@ void k_del_vif(vifi)
{
if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF,
(char *)&vifi, sizeof(vifi)) < 0)
- log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF");
+ log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF on vif %d", vifi);
}
@@ -183,7 +225,8 @@ void k_add_rg(origin, g)
#ifdef DEBUG_MFC
md_log(MD_ADD_FAIL, origin, g->gt_mcastgrp);
#endif
- log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC");
+ log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
}
}
@@ -214,7 +257,8 @@ int k_del_rg(origin, g)
#ifdef DEBUG_MFC
md_log(MD_DEL_FAIL, origin, g->gt_mcastgrp);
#endif
- log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC");
+ log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC of (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
}
return retval;
@@ -239,3 +283,63 @@ int k_get_version()
return vers;
#endif
}
+
+#if 0
+/*
+ * Get packet counters
+ */
+int
+k_get_vif_count(vifi, icount, ocount, ibytes, obytes)
+ vifi_t vifi;
+ int *icount, *ocount, *ibytes, *obytes;
+{
+ struct sioc_vif_req vreq;
+ int retval = 0;
+
+ vreq.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETVIFCNT on vif %d", vifi);
+ vreq.icount = vreq.ocount = vreq.ibytes =
+ vreq.obytes = 0xffffffff;
+ retval = 1;
+ }
+ if (icount)
+ *icount = vreq.icount;
+ if (ocount)
+ *ocount = vreq.ocount;
+ if (ibytes)
+ *ibytes = vreq.ibytes;
+ if (obytes)
+ *obytes = vreq.obytes;
+ return retval;
+}
+
+/*
+ * Get counters for a desired source and group.
+ */
+int
+k_get_sg_count(src, grp, pktcnt, bytecnt, wrong_if)
+ u_int32 src;
+ u_int32 grp;
+ struct sg_count *retval;
+{
+ struct sioc_sg_req sgreq;
+ int retval = 0;
+
+ sgreq.src.s_addr = src;
+ sgreq.grp.s_addr = grp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sgreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(src, s1), inet_fmt(grp, s2));
+ sgreq.pktcnt = sgreq.bytecnt = sgreq.wrong_if = 0xffffffff;
+ return 1;
+ }
+ if (pktcnt)
+ *pktcnt = sgreq.pktcnt;
+ if (bytecnt)
+ *bytecnt = sgreq.bytecnt;
+ if (wrong_if)
+ *wrong_if = sgreq.wrong_if;
+ return retval;
+}
+#endif
diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c
index b5ef723bcb73..1bd147705f9e 100644
--- a/usr.sbin/mrouted/main.c
+++ b/usr.sbin/mrouted/main.c
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: main.c,v 3.8 1995/11/29 22:36:34 fenner Rel $
+ * main.c,v 3.8.4.29 1998/03/01 01:49:00 fenner Exp
*/
/*
@@ -33,8 +33,8 @@
#endif
#ifndef lint
-static char rcsid[] =
- "@(#) $Id: main.c,v 3.8 1995/11/29 22:36:34 fenner Rel $";
+static char rcsid[] = "@(#) $Id: \
+main.c,v 3.8.4.29 1998/03/01 01:49:00 fenner Exp $";
#endif
extern char *configfilename;
@@ -45,11 +45,21 @@ static char dumpfilename[] = _PATH_MROUTED_DUMP;
static char cachefilename[] = _PATH_MROUTED_CACHE;
static char genidfilename[] = _PATH_MROUTED_GENID;
+static int haveterminal = 1;
+int did_final_init = 0;
+
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+
int cache_lifetime = DEFAULT_CACHE_LIFETIME;
-int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2;
+int prune_lifetime = AVERAGE_PRUNE_LIFETIME;
int debug = 0;
-u_char pruning = 1; /* Enable pruning by default */
+char *progname;
+time_t mrouted_init_time;
#ifdef SNMP
#define NHANDLERS 34
@@ -63,16 +73,48 @@ static struct ihandler {
} ihandlers[NHANDLERS];
static int nhandlers = 0;
+static struct debugname {
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+ { "packet", DEBUG_PKT, 2 },
+ { "pkt", DEBUG_PKT, 3 },
+ { "pruning", DEBUG_PRUNE, 1 },
+ { "prunes", DEBUG_PRUNE, 1 },
+ { "routing", DEBUG_ROUTE, 1 },
+ { "routes", DEBUG_ROUTE, 1 },
+ { "route_detail", DEBUG_RTDETAIL, 6 },
+ { "rtdetail", DEBUG_RTDETAIL, 2 },
+ { "peers", DEBUG_PEER, 2 },
+ { "neighbors", DEBUG_PEER, 1 },
+ { "cache", DEBUG_CACHE, 1 },
+ { "timeout", DEBUG_TIMEOUT, 1 },
+ { "callout", DEBUG_TIMEOUT, 2 },
+ { "interface", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "membership", DEBUG_MEMBER, 1 },
+ { "groups", DEBUG_MEMBER, 1 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "igmp", DEBUG_IGMP, 1 },
+ { "icmp", DEBUG_ICMP, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
/*
* Forward declarations.
*/
-static void fasttimer __P((int));
-static void done __P((int));
-static void dump __P((int));
-static void fdump __P((int));
-static void cdump __P((int));
-static void restart __P((int));
-static void timer __P((void));
+static void final_init __P((void *));
+static void fasttimer __P((void *));
+static void timer __P((void *));
+static void dump __P((void));
+static void dump_version __P((FILE *));
+static void fdump __P((void));
+static void cdump __P((void));
+static void restart __P((void));
+static void handler __P((int));
static void cleanup __P((void));
static void resetlogging __P((void *));
@@ -99,14 +141,15 @@ main(argc, argv)
char *argv[];
{
register int recvlen;
- register int omask;
int dummy;
FILE *fp;
- struct timeval tv;
+ struct timeval tv, difftime, curtime, lasttime, *timeout;
u_int32 prev_genid;
int vers;
fd_set rfds, readers;
- int nfds, n, i;
+ int nfds, n, i, secs;
+ extern char todaysversion[];
+ struct sigaction sa;
#ifdef SNMP
struct timeval timeout, *tvp = &timeout;
struct timeval sched, *svp = &sched, now, *nvp = &now;
@@ -116,16 +159,57 @@ main(argc, argv)
setlinebuf(stderr);
if (geteuid() != 0) {
- fprintf(stderr, "must be root\n");
+ fprintf(stderr, "mrouted: must be root\n");
exit(1);
}
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
argv++, argc--;
while (argc > 0 && *argv[0] == '-') {
if (strcmp(*argv, "-d") == 0) {
- if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ if (argc > 1 && *(argv + 1)[0] != '-') {
+ char *p,*q;
+ int i, len;
+ struct debugname *d;
+
argv++, argc--;
- debug = atoi(*argv);
+ debug = 0;
+ p = *argv; q = NULL;
+ while (p) {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0])) {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++) {
+ if ((j & d->level) == d->level) {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ goto usage;
+ }
+ debug |= d->level;
+ p = q;
+ }
} else
debug = DEFAULT_DEBUG;
} else if (strcmp(*argv, "-c") == 0) {
@@ -135,7 +219,7 @@ main(argc, argv)
} else
goto usage;
} else if (strcmp(*argv, "-p") == 0) {
- pruning = 0;
+ log(LOG_WARNING, 0, "disabling pruning is no longer supported");
#ifdef SNMP
} else if (strcmp(*argv, "-P") == 0) {
if (argc > 1 && isdigit(*(argv + 1)[0])) {
@@ -151,40 +235,27 @@ main(argc, argv)
if (argc > 0) {
usage: fprintf(stderr,
- "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
+ "usage: mrouted [-p] [-c configfile] [-d [debug_level][,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);
-#ifdef SYSV
- (void)setpgrp();
-#else
-#ifdef TIOCNOTTY
- t = open("/dev/tty", 2);
- if (t >= 0) {
- (void)ioctl(t, TIOCNOTTY, (char *)0);
- (void)close(t);
+ if (debug != 0) {
+ struct debugname *d;
+ char c;
+ int tmpd = debug;
+
+ fprintf(stderr, "debug level 0x%x ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++) {
+ if ((tmpd & d->level) == d->level) {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
}
-#else
- if (setsid() < 0)
- perror("setsid");
-#endif
-#endif
+ fprintf(stderr, ")\n");
}
- else
- fprintf(stderr, "debug level %u\n", debug);
#ifdef LOG_DAEMON
(void)openlog("mrouted", LOG_PID, LOG_DAEMON);
@@ -192,10 +263,9 @@ usage: fprintf(stderr,
#else
(void)openlog("mrouted", LOG_PID);
#endif
- sprintf(versionstring, "mrouted version %d.%d",
- PROTOCOL_VERSION, MROUTED_VERSION);
+ sprintf(versionstring, "mrouted version %s", todaysversion);
- log(LOG_NOTICE, 0, "%s", versionstring);
+ log(LOG_DEBUG, 0, "%s starting", versionstring);
#ifdef SYSV
srand48(time(NULL));
@@ -223,14 +293,27 @@ usage: fprintf(stderr,
(void) fclose(fp);
}
+ /* Start up the log rate-limiter */
+ resetlogging(NULL);
+
callout_init();
init_igmp();
+ init_icmp();
+ init_ipip();
init_routes();
init_ktable();
- k_init_dvmrp(); /* enable DVMRP routing in kernel */
-
#ifndef OLD_KERNEL
+ /*
+ * Unfortunately, you can't k_get_version() unless you've
+ * k_init_dvmrp()'d. Now that we want to move the
+ * k_init_dvmrp() to later in the initialization sequence,
+ * we have to do the disgusting hack of initializing,
+ * getting the version, then stopping the kernel multicast
+ * forwarding.
+ */
+ k_init_dvmrp();
vers = k_get_version();
+ k_stop_dvmrp();
/*XXX
* This function must change whenever the kernel version changes
*/
@@ -261,33 +344,14 @@ usage: fprintf(stderr,
rsrr_init();
#endif /* RSRR */
-#if defined(__STDC__) || defined(__GNUC__)
- /*
- * Allow cleanup if unexpected exit. Apparently some architectures
- * have a kernel bug where closing the socket doesn't do an
- * ip_mrouter_done(), so we attempt to do it on exit.
- */
- atexit(cleanup);
-#endif
-
- if (debug)
- fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
-
- fp = fopen(pidfilename, "w");
- if (fp != NULL) {
- fprintf(fp, "%d\n", (int)getpid());
- (void) fclose(fp);
- }
-
- (void)signal(SIGALRM, fasttimer);
-
- (void)signal(SIGHUP, restart);
- (void)signal(SIGTERM, done);
- (void)signal(SIGINT, done);
- (void)signal(SIGUSR1, fdump);
- (void)signal(SIGUSR2, cdump);
- if (debug != 0)
- (void)signal(SIGQUIT, dump);
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
FD_ZERO(&readers);
FD_SET(igmp_socket, &readers);
@@ -298,30 +362,82 @@ usage: fprintf(stderr,
nfds = ihandlers[i].fd + 1;
}
- /*
- * Install the vifs in the kernel as late as possible in the
- * initialization sequence.
- */
- init_installvifs();
+ IF_DEBUG(DEBUG_IF)
+ dump_vifs(stderr);
+ IF_DEBUG(DEBUG_ROUTE)
+ dump_routes(stderr);
- if (debug >= 2) dump(0);
+ /* schedule first timer interrupt */
+ timer_setTimer(1, fasttimer, NULL);
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
- /* Start up the log rate-limiter */
- resetlogging(NULL);
+ if (debug == 0) {
+ /*
+ * Detach from the terminal
+ */
+ int t;
- (void)alarm(1); /* schedule first timer interrupt */
+ haveterminal = 0;
+ if (fork()) exit(0);
+ (void)close(0);
+ (void)close(1);
+ (void)close(2);
+ (void)open("/", 0);
+ (void)dup2(0, 1);
+ (void)dup2(0, 2);
+#if defined(SYSV) || defined(linux)
+ (void)setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void)ioctl(t, TIOCNOTTY, (char *)0);
+ (void)close(t);
+ }
+#else
+ if (setsid() < 0)
+ perror("setsid");
+#endif
+#endif
+ }
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ (void) fclose(fp);
+ }
+
+ /* XXX HACK
+ * This will cause black holes for the first few seconds after startup,
+ * since we are exchanging routes but not actually forwarding.
+ * However, it eliminates much of the startup transient.
+ *
+ * It's possible that we can set a flag which says not to report any
+ * routes (just accept reports) until this timer fires, and then
+ * do a report_to_all_neighbors(ALL_ROUTES) immediately before
+ * turning on DVMRP.
+ */
+ timer_setTimer(10, final_init, NULL);
/*
* Main receive loop.
*/
dummy = 0;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
for(;;) {
-#ifdef SYSV
- sigset_t block, oblock;
-#endif
bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
#ifdef SNMP
- gettimeofday(nvp, 0);
+ THIS IS BROKEN
if (nvp->tv_sec > svp->tv_sec
|| (nvp->tv_sec == svp->tv_sec && nvp->tv_usec > svp->tv_usec)){
alarmTimer(nvp);
@@ -344,51 +460,121 @@ usage: fprintf(stderr,
if (block == 1)
tvp = NULL; /* block without timeout */
if ((n = select(nfds, &rfds, NULL, NULL, tvp)) < 0)
-#else
- if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0)
#endif
- {
- if (errno != EINTR) /* SIGALRM is expected */
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP) {
+ sighandled &= ~GOT_SIGHUP;
+ restart();
+ }
+ if (sighandled & GOT_SIGUSR1) {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump();
+ }
+ if (sighandled & GOT_SIGUSR2) {
+ sighandled &= ~GOT_SIGUSR2;
+ cdump();
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) {
+ if (errno != EINTR)
log(LOG_WARNING, errno, "select failed");
continue;
}
- if (FD_ISSET(igmp_socket, &rfds)) {
- recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
- 0, NULL, &dummy);
- if (recvlen < 0) {
- if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
- continue;
+ if (n > 0) {
+ if (FD_ISSET(igmp_socket, &rfds)) {
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen < 0) {
+ if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
+ continue;
+ }
+ accept_igmp(recvlen);
}
-#ifdef SYSV
- (void)sigemptyset(&block);
- (void)sigaddset(&block, SIGALRM);
- if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
- log(LOG_ERR, errno, "sigprocmask");
-#else
- omask = sigblock(sigmask(SIGALRM));
-#endif
- accept_igmp(recvlen);
-#ifdef SYSV
- (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
-#else
- (void)sigsetmask(omask);
-#endif
- }
- for (i = 0; i < nhandlers; i++) {
- if (FD_ISSET(ihandlers[i].fd, &rfds)) {
- (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ for (i = 0; i < nhandlers; i++) {
+ if (FD_ISSET(ihandlers[i].fd, &rfds)) {
+ (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ }
}
}
#ifdef SNMP
+ THIS IS BROKEN
snmp_read(&rfds);
snmp_timeout(); /* poll */
#endif
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second,
+ * or if there is a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the
+ * time doesn't drift too badly.
+ *
+ * If the timeout handlers took more than 1 second,
+ * age the timeout queue again. XXX This introduces the
+ * potential for infinite loops!
+ */
+ do {
+ /*
+ * If the select timed out, then there's no other
+ * activity to account for and we don't need to
+ * call gettimeofday.
+ */
+ if (n == 0) {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ } else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+ while (difftime.tv_usec > 1000000) {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0) {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0)
+ age_callout_queue(difftime.tv_sec);
+ secs = -1;
+ } while (difftime.tv_sec > 0);
}
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
}
+static void
+final_init(i)
+ void *i;
+{
+ char *s = (char *)i;
+
+ log(LOG_NOTICE, 0, "%s%s", versionstring, s ? s : "");
+ if (s)
+ free(s);
+
+ k_init_dvmrp(); /* enable DVMRP routing in kernel */
+
+ /*
+ * Install the vifs in the kernel as late as possible in the
+ * initialization sequence.
+ */
+ init_installvifs();
+
+ time(&mrouted_init_time);
+ did_final_init = 1;
+}
/*
* routine invoked every second. Its main goal is to cycle through
@@ -399,7 +585,7 @@ usage: fprintf(stderr,
*/
static void
fasttimer(i)
- int i;
+ void *i;
{
static unsigned int tlast;
static unsigned int nsent;
@@ -428,12 +614,8 @@ fasttimer(i)
}
tlast = t;
}
- if ((t % TIMER_INTERVAL) == 0)
- timer();
- age_callout_queue();/* Advance the timer for the callout queue
- for groups */
- alarm(1);
+ timer_setTimer(1, fasttimer, NULL);
}
/*
@@ -453,7 +635,7 @@ fasttimer(i)
* avoid unwanted synchronization with other routers.
*/
-static u_long virtual_time = 0;
+u_long virtual_time = 0;
/*
@@ -462,13 +644,14 @@ static u_long virtual_time = 0;
* virtual interface data structures.
*/
static void
-timer()
+timer(i)
+ void *i;
{
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) {
+ if (virtual_time % IGMP_QUERY_INTERVAL == 0) {
/*
* Time to query the local group memberships on all subnets
* for which this router is the elected querier.
@@ -507,21 +690,10 @@ timer()
* Advance virtual time
*/
virtual_time += TIMER_INTERVAL;
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
}
-/*
- * On termination, let everyone know we're going away.
- */
-static void
-done(i)
- int i;
-{
- log(LOG_NOTICE, 0, "%s exiting", versionstring);
- cleanup();
- _exit(1);
-}
-
static void
cleanup()
{
@@ -534,7 +706,36 @@ cleanup()
#endif /* RSRR */
expire_all_routes();
report_to_all_neighbors(ALL_ROUTES);
- k_stop_dvmrp();
+ if (did_final_init)
+ k_stop_dvmrp();
+ }
+}
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
}
}
@@ -543,25 +744,39 @@ cleanup()
* Dump internal data structures to stderr.
*/
static void
-dump(i)
- int i;
+dump()
{
dump_vifs(stderr);
dump_routes(stderr);
}
+static void
+dump_version(fp)
+ FILE *fp;
+{
+ time_t t;
+
+ time(&t);
+ fprintf(fp, "%s ", versionstring);
+ if (did_final_init)
+ fprintf(fp, "up %s",
+ scaletime(t - mrouted_init_time));
+ else
+ fprintf(fp, "(not yet initialized)");
+ fprintf(fp, " %s\n", ctime(&t));
+}
/*
* Dump internal data structures to a file.
*/
static void
-fdump(i)
- int i;
+fdump()
{
FILE *fp;
fp = fopen(dumpfilename, "w");
if (fp != NULL) {
+ dump_version(fp);
dump_vifs(fp);
dump_routes(fp);
(void) fclose(fp);
@@ -573,13 +788,13 @@ fdump(i)
* Dump local cache contents to a file.
*/
static void
-cdump(i)
- int i;
+cdump()
{
FILE *fp;
fp = fopen(cachefilename, "w");
if (fp != NULL) {
+ dump_version(fp);
dump_cache(fp);
(void) fclose(fp);
}
@@ -590,52 +805,42 @@ cdump(i)
* Restart mrouted
*/
static void
-restart(i)
- int i;
+restart()
{
- register int omask;
-#ifdef SYSV
- sigset_t block, oblock;
-#endif
+ char *s;
- log(LOG_NOTICE, 0, "%s restart", versionstring);
+ s = (char *)malloc(sizeof(" restart"));
+ if (s == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ strcpy(s, " restart");
/*
* reset all the entries
*/
-#ifdef SYSV
- (void)sigemptyset(&block);
- (void)sigaddset(&block, SIGALRM);
- if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
- log(LOG_ERR, errno, "sigprocmask");
-#else
- omask = sigblock(sigmask(SIGALRM));
-#endif
free_all_prunes();
free_all_routes();
+ free_all_callouts();
stop_all_vifs();
k_stop_dvmrp();
close(igmp_socket);
close(udp_socket);
+ did_final_init = 0;
/*
* start processing again
*/
dvmrp_genid++;
- pruning = 1;
init_igmp();
init_routes();
init_ktable();
init_vifs();
- k_init_dvmrp(); /* enable DVMRP routing in kernel */
- init_installvifs();
+ /*XXX Schedule final_init() as main does? */
+ final_init(s);
-#ifdef SYSV
- (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
-#else
- (void)sigsetmask(omask);
-#endif
+ /* schedule timer interrupts */
+ timer_setTimer(1, fasttimer, NULL);
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
}
#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
@@ -661,6 +866,61 @@ resetlogging(arg)
timer_setTimer(nxttime, resetlogging, narg);
}
+char *
+scaletime(t)
+ u_long t;
+{
+#define SCALETIMEBUFLEN 20
+ static char buf1[20];
+ static char buf2[20];
+ static char *buf = buf1;
+ char *p;
+
+ p = buf;
+ if (buf == buf1)
+ buf = buf2;
+ else
+ buf = buf1;
+
+ /* XXX snprintf */
+ sprintf(p, "%2ld:%02ld:%02ld", t / 3600, (t % 3600) / 60, t % 60);
+ p[SCALETIMEBUFLEN - 1] = '\0';
+ return p;
+}
+
+#ifdef RINGBUFFER
+#define NLOGMSGS 10000
+#define LOGMSGSIZE 200
+char *logmsg[NLOGMSGS];
+static int logmsgno = 0;
+
+void
+printringbuf()
+{
+ FILE *f;
+ int i;
+
+ f = fopen("/var/tmp/mrouted.log", "a");
+ if (f == NULL) {
+ log(LOG_ERR, errno, "can't open /var/tmp/mrouted.log");
+ /*NOTREACHED*/
+ }
+ fprintf(f, "--------------------------------------------\n");
+
+ i = (logmsgno + 1) % NLOGMSGS;
+
+ while (i != logmsgno) {
+ if (*logmsg[i]) {
+ fprintf(f, "%s\n", logmsg[i]);
+ *logmsg[i] = '\0';
+ }
+ i = (i + 1) % NLOGMSGS;
+ }
+
+ fclose(f);
+}
+#endif
+
/*
* 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.
@@ -673,9 +933,11 @@ log(int severity, int syserr, char *format, ...)
va_list ap;
static char fmt[211] = "warning - ";
char *msg;
- char tbuf[20];
struct timeval now;
struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
va_start(ap, format);
#else
@@ -687,11 +949,14 @@ log(severity, syserr, format, va_alist)
va_dcl
{
va_list ap;
- static char fmt[211] = "warning - ";
+ static char fmt[311] = "warning - ";
char *msg;
char tbuf[20];
struct timeval now;
struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
va_start(ap);
#endif
@@ -699,35 +964,66 @@ log(severity, syserr, format, va_alist)
va_end(ap);
msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
- switch (debug) {
- case 0: break;
- case 1: if (severity > LOG_NOTICE) break;
- case 2: if (severity > LOG_INFO ) break;
- default:
- gettimeofday(&now,NULL);
- thyme = localtime(&now.tv_sec);
- strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme);
- fprintf(stderr, tbuf, now.tv_usec / 1000);
- fprintf(stderr, "%s", msg);
- 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);
- }
+#ifdef RINGBUFFER
+ if (!ringbufinit) {
+ int i;
- if (severity <= LOG_NOTICE) {
- if (log_nmsgs++ < LOG_MAX_MSGS) {
- if (syserr != 0) {
- errno = syserr;
- syslog(severity, "%s: %m", msg);
- } else
- syslog(severity, "%s", msg);
+ for (i = 0; i < NLOGMSGS; i++) {
+ logmsg[i] = malloc(LOGMSGSIZE);
+ if (logmsg[i] == 0) {
+ syslog(LOG_ERR, "out of memory");
+ exit(-1);
+ }
+ *logmsg[i] = 0;
}
+ ringbufinit = 1;
+ }
+ gettimeofday(&now,NULL);
+ thyme = localtime(&now.tv_sec);
+ sprintf(logmsg[logmsgno++], "%02d:%02d:%02d.%03ld %s err %d",
+ thyme->tm_hour, thyme->tm_min, thyme->tm_sec,
+ now.tv_usec / 1000, msg, syserr);
+ logmsgno %= NLOGMSGS;
+ if (severity <= LOG_NOTICE)
+#endif
+ /*
+ * Log to stderr if we haven't forked yet and it's a warning or worse,
+ * or if we're debugging.
+ */
+ if (haveterminal && (debug || severity <= LOG_WARNING)) {
+ gettimeofday(&now,NULL);
+ thyme = localtime(&now.tv_sec);
+ if (!debug)
+ fprintf(stderr, "%s: ", progname);
+ fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ 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);
+ /*
+ * Always log things that are worse than warnings, no matter what
+ * the log_nmsgs rate limiter says.
+ * Only count things worse than debugging in the rate limiter
+ * (since if you put daemon.debug in syslog.conf you probably
+ * actually want to log the debugging messages so they shouldn't
+ * be rate-limited)
+ */
+ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) {
+ if (severity < LOG_DEBUG)
+ log_nmsgs++;
+ if (syserr != 0) {
+ errno = syserr;
+ syslog(severity, "%s: %m", msg);
+ } else
+ syslog(severity, "%s", msg);
}
+
+ if (severity <= LOG_ERR) exit(-1);
}
#ifdef DEBUG_MFC
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
index 1eacd04bb61d..c53ec074455e 100644
--- a/usr.sbin/mrouted/mapper.c
+++ b/usr.sbin/mrouted/mapper.c
@@ -1,7 +1,7 @@
/* Mapper for connections between MRouteD multicast routers.
* Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
*
- * $Id: mapper.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * mapper.c,v 3.8.4.3 1998/01/06 01:57:47 fenner Exp
*/
/*
@@ -32,6 +32,11 @@
#include <varargs.h>
#endif
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+mapper.c,v 3.8.4.3 1998/01/06 01:57:47 fenner Exp $";
+#endif
+
#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
#define DEFAULT_RETRIES 1 /* How many times to ask each router */
@@ -844,13 +849,16 @@ int main(argc, argv)
{
int flood = FALSE, graph = FALSE;
- setlinebuf(stderr);
-
if (geteuid() != 0) {
- fprintf(stderr, "must be root\n");
+ fprintf(stderr, "map-mbone: must be root\n");
exit(1);
}
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
argv++, argc--;
while (argc > 0 && argv[0][0] == '-') {
switch (argv[0][1]) {
@@ -899,15 +907,13 @@ int main(argc, argv)
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;
-#if (defined(BSD) && (BSD >= 199103))
+#ifdef HAVE_SA_LEN
addr.sin_len = sizeof addr;
#endif
addr.sin_addr.s_addr = dvmrp_group;
diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c
index 7335aba95cf9..14ffa2575e7f 100644
--- a/usr.sbin/mrouted/mrinfo.c
+++ b/usr.sbin/mrouted/mrinfo.c
@@ -60,11 +60,8 @@
*/
#ifndef lint
-static char rcsid[] =
- "@(#) $Id: mrinfo.c,v 3.8 1995/11/29 22:36:34 fenner Rel $";
-/* original rcsid:
- "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
-*/
+static char rcsid[] = "@(#) $Id: \
+mrinfo.c,v 3.8.4.7 1998/03/01 03:05:20 fenner Exp $";
#endif
#include <netdb.h>
@@ -236,13 +233,14 @@ accept_neighbors2(src, dst, p, datalen, level)
u_char *ep = p + datalen;
u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
/* well, only possibly_broken_cisco, but that's too long to type. */
+ u_int majvers = level & 0xff;
+ u_int minvers = (level >> 8) & 0xff;
- printf("%s (%s) [version %d.%d", inet_fmt(src, s1), inet_name(src),
- level & 0xff, (level >> 8) & 0xff);
- if ((level >> 16) & NF_LEAF) { printf (",leaf"); }
- if ((level >> 16) & NF_PRUNE) { printf (",prune"); }
- if ((level >> 16) & NF_GENID) { printf (",genid"); }
- if ((level >> 16) & NF_MTRACE) { printf (",mtrace"); }
+ printf("%s (%s) [", inet_fmt(src, s1), inet_name(src));
+ if (majvers == 3 && minvers == 0xff)
+ printf("DVMRPv3 compliant");
+ else
+ printf("version %d.%d", majvers, minvers);
printf ("]:\n");
while (p < ep) {
@@ -333,12 +331,16 @@ main(argc, argv)
char *host;
int curaddr;
- setlinebuf(stderr);
-
if (geteuid() != 0) {
fprintf(stderr, "mrinfo: must be root\n");
exit(1);
}
+
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
argv++, argc--;
while (argc > 0 && argv[0][0] == '-') {
switch (argv[0][1]) {
@@ -386,8 +388,6 @@ main(argc, argv)
if (debug)
fprintf(stderr, "Debug level %u\n", debug);
- init_igmp();
-
/* Check all addresses; mrouters often have unreachable interfaces */
for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
memcpy(&target_addr, hp->h_addr_list[curaddr], hp->h_length);
@@ -397,7 +397,7 @@ main(argc, argv)
int addrlen = sizeof(addr);
addr.sin_family = AF_INET;
-#if (defined(BSD) && (BSD >= 199103))
+#ifdef HAVE_SA_LEN
addr.sin_len = sizeof addr;
#endif
addr.sin_addr.s_addr = target_addr;
@@ -492,7 +492,11 @@ main(argc, argv)
src = ip->ip_src.s_addr;
dst = ip->ip_dst.s_addr;
iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
ipdatalen = ip->ip_len;
+#endif
if (iphdrlen + ipdatalen != recvlen) {
log(LOG_WARNING, 0,
"packet shorter (%u bytes) than hdr+data length (%u+%u)",
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
index 6c4c7bd98616..d0d7c6b7917a 100644
--- a/usr.sbin/mrouted/mrouted.8
+++ b/usr.sbin/mrouted/mrouted.8
@@ -1,14 +1,12 @@
'\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
-'\"$Id: mrouted.8,v 3.8 1995/11/29 22:37:21 fenner Rel $
+'\"mrouted.8,v 3.8.4.5 1998/03/01 01:50:28 fenner Exp
.TH MROUTED 8
.UC 5
.SH NAME
mrouted \- IP multicast routing daemon
.SH SYNOPSIS
-.B /etc/mrouted
+.B /usr/sbin/mrouted
[
-.B \-p
-] [
.B \-c
.I config_file
] [
@@ -39,17 +37,17 @@ 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
+multicast routers
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
+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
+tunneled using IP source routing, which puts a heavy load on some
types of routers.
This version does not support IP source route tunnelling.
.PP
@@ -75,136 +73,323 @@ to have access to more than one physical subnet
in order to perform multicast forwarding.
.br
.ne 5
+.SH OPTIONS
+.TP 10
+.BI \-c " config_file"
+Specifies an alternate configuration file to read (normally /etc/mrouted.conf)
+.TP
+.BI \-d " debug_level"
+Turn on debugging;
+.I debug_level
+is a comma-seperated list of subsections to debug.
.SH INVOCATION
.PP
-If no "\-d" option is given, or if the debug level is specified as 0,
+If no "\-d" option is given,
.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,
+invoking terminal and responsive to signals from that terminal.
+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.
+log demon. The
+.I debug-level
+argument is a comma-seperated list of any of the following:
+.TP 13
+packet
+Display the type, source and destination of all packets sent or received.
+.TP
+pruning
+Display more information about prunes sent or received.
+.TP
+routing
+Display more information about routing update packets sent or received.
+.TP
+route_detail
+Display routing updates in excruciating detail. This is generally way too
+much information.
+.TP
+neighbors
+Display information about neighbor discovery.
+.TP
+cache
+Display insertions, deletions and refreshes of entries in
+the kernel forwarding cache.
+.TP
+timeout
+Debug timeouts and periodic processes.
+.TP
+interface
+Display information about interfaces and their configuration.
+.TP
+membership
+Display information about group memberships on physical interfaces.
+.TP
+traceroute
+Display information about multicast traceroute requests
+passing through this router.
+.TP
+igmp
+Display IGMP operation including group membership and querier election.
+.TP
+icmp
+Monitor ICMP handling.
+.TP
+rsrr
+Monitor RSRR operation.
.PP
-Upon startup, mrouted writes its pid to the file /etc/mrouted.pid .
+Upon startup, mrouted writes its pid to the file /var/run/mrouted.pid .
.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
+the loopback "interface"), and it finds other DVMRP routers directly reachable
via those interfaces. To override the default configuration, or to add
-tunnel links to other
-.IR mrouted s,
+tunnel links to other multicast routers,
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 (<boundary-name>|<scoped-addr>/<mask-len>)]
- [altnet <network>/<mask-len>]
-
- tunnel <local-addr> <remote-addr> [metric <m>]
- [threshold <t>] [rate_limit <b>]
- [boundary (<boundary-name>|<scoped-addr>/<mask-len>)]
-
- cache_lifetime <ct>
-
- pruning <off/on>
-
- name <boundary-name> <scoped-addr>/<mask-len>
-
-.fi
.PP
The file format is free-form; whitespace (including newlines) is not
significant.
-The
-.I boundary
-and
-.I altnet
-options may be specified as many times as necessary.
+The file begins with commands that apply to
+.IR mrouted 's
+overall operation or set defaults.
+.TP 15
+.BI cache_lifetime " secs"
+Specifies, in seconds, the lifetime of a multicast forwarding cache
+entry in the kernel. Multicast forwarding cache entries in the kernel
+are checked every
+.I secs
+seconds, and are refreshed if the source is still
+active or deleted if not. Care should be taken when setting this value,
+as a low value can keep the kernel cache small at the cost of "thrashing"
+the cache for periodic senders, but high values can cause the kernel
+cache to grow unacceptably large. The default is 300 seconds (5 minutes).
+.TP
+.BI prune_lifetime " secs"
+Sepcifies, in seconds, the average lifetime of prunes that are sent towards
+parents. The actual lifetimes will be randomized in the range
+[.5\fIsecs\fP,1.5\fIsecs\fP]. The default is 7200 (2 hours). Smaller values
+cause less state to be kept both at this router and the parent, at the
+cost of more frequent broadcasts. However, some routers (e.g. mrouted <3.3
+and all currently known versions of cisco's IOS) do not use the
+DVMRP generation ID to determine that a neighbor has rebooted. Prunes
+sent towards these neighbors should be kept short, in order to shorten
+the time to recover from a reboot. For use in this situation, the
+prune_lifetime keyword may be specified on an interface as described
+below.
+.TP
+.B noflood
+.I Mrouted
+uses a DVMRP optimization to prevent having to keep individual routing tables
+for each neighbor; part of this optimization is that
+.I mrouted
+assumes that it is the forwarder for each of its attached subnets on
+startup. This can cause duplicates for a short period (approximately
+one full route report interval), since both the router that just
+started up and the proper forwarder will be forwarding traffic. This
+behavior can be turned off with the noflood keyword;
+.I mrouted
+will not assume that it is the forwarder on startup.
+Turning on noflood can cause black holes on restart, which will generally
+last approximately one full route report interval.
+The noflood keyword can also be specified on individual interfaces.
+.TP
+.BI rexmit_prunes " [on|off]"
+.IR Mrouted 's
+default is to retransmit prunes on all point-to-point interfaces
+(including tunnels) but no multi-access interfaces. This option
+may be used to make the default on (or off) for all interfaces.
+The rexmit_prunes keyword can also be specified on individual interfaces.
+.TP
+.BI name " boundary-name scoped-addr/mask-len"
+Associates
+.I boundary-name
+with the boundary described by
+.IR scoped-addr/mask-len ,
+to help make interface configurations
+more readable and reduce repetition in the configuration file.
.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 replaced by the
-interface name (e.g le0).
+The second section of the configuration file, which may optionally
+be empty, describes options that apply to physical interfaces.
+.TP 15
+.BI phyint " local-addr|ifname"
+The phyint command does nothing by itself; it is simply a place holder
+which interface-specific commands may follow. An interface address or
+name may be specified.
+.TP
+.B disable
+Disables multicast forwarding on this interface. By default,
+.I mrouted
+discovers all locally attached multicast capable interfaces and forwards
+on all of them.
+.TP
+.BI netmask " netmask"
+If the kernel's netmask does not accurately reflect
+the subnet (e.g. you're using proxy-ARP in lieu of IP subnetting), use the
+netmask command to describe the real netmask.
+.TP
+.BI altnet " network/mask-len"
If a phyint is attached to multiple IP subnets, describe each additional subnet
-with the altnet keyword.
-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 local IP address <local-addr> may be replaced by the
-interface name (e.g. le0). The remote IP address <remote-addr> may
-be replaced by a host name, if and only if the host name has a single
-IP address associated with it.
-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.
+with the altnet keyword. This command may be specified multiple times
+to describe multiple subnets.
+.TP
+.B igmpv1
+If there are any IGMPv1 routers on the phyint, use the \fBigmpv1\fP
+keyword to force \fImrouted\fP into IGMPv1 mode. All routers on the phyint
+must use the same version of IGMP.
+.TP
+.B force_leaf
+Force \fImrouted\fP to ignore other routers on this interface.
+mrouted will never send or accept neighbor probes or
+route reports on this interface.
.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.
+In addition, the common vif commands described later may all be used on
+a phyint.
.PP
-You may assign names to boundaries to make configuration easier with
-the name keyword. The boundary option on phyint or tunnel commands
-can accept either a name or a boundary.
+The third section of the configuration file, also optional, describes
+the configuration of any DVMRP tunnels this router might have.
+.TP 15
+.BI tunnel " local-addr|ifname remote-addr|remote-hostname"
+This command establishes a DVMRP tunnel between this host (on the interface
+described by
+.I local-addr
+or
+.IR ifname )
+and a remote host (identified by
+.I remote-addr
+or
+.IR remote-hostname ).
+A remote hostname may only be used if
+it maps to a single IP address.
+A tunnel must be configured on both routers before it can be used.
+
+Be careful that the unicast route to the remote address goes out the
+interface specified by the
+.I local-addr|ifname
+argument. Some UNIX
+kernels rewrite the source address of
+.IR mrouted 's
+packets on their way out to contain the address of the transmission
+interface. This is best assured via a static host route.
.PP
-The metric is the "cost" associated with sending a datagram on the given
+The common vif commands described below
+may all be used on tunnels or phyints.
+.TP 15
+.BI metric " m"
+The metric is the "cost" associated with receiving 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
+because DVMRP cannot route along paths with a sum of metrics greater
than 31.
-.LP
+.TP
+.BI advert_metric " m"
+The advert_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 advert_metric defaults to 0. Note that the effective
+metric of a link is one end's metric plus the other end's advert_metric.
+.TP
+.BI threshold " t"
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.
+router decrements the TTL by exactly 1.) The default threshold is 1.
.LP
-In general, all
-.IR mrouted s
+In general, all multicast routers
connected to a particular subnet or tunnel should
use the same metric and threshold for that subnet or tunnel.
-.PP
+.TP 15
+.BI rate_limit " r"
The rate_limit option allows the network administrator to specify a
certain bandwidth in Kbits/second which would be allocated to multicast
-traffic. It defaults to 500Kbps on tunnels, and 0 (unlimited) on physical
-interfaces.
-.PP
+traffic. It defaults 0 (unlimited).
+.TP
+.BI boundary " boundary-name|scoped-addr/mask-len"
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. The boundary option accepts either
-a name or a boundary spec.
+a name or a boundary spec. This command may be specified several times
+on an interface in order to describe multiple boundaries.
+.TP
+.B passive
+No packets will be sent on this link or tunnel until we hear from the other
+end. This is useful for the "server" end of a tunnel that goes over
+a dial-on-demand link; configure the "server" end as passive and
+it will not send its periodic probes until it hears one from the other
+side, so will not keep the link up. If this option is specified on both
+ends of a tunnel, the tunnel will never come up.
+.TP
+.B noflood
+As described above, but only applicable to this interface/tunnel.
+.TP
+.BI prune_lifetime " secs"
+As described above, but only applicable to this interface/tunnel.
+.TP
+.BI rexmit_prunes " [on|off]"
+As described above, but only applicable to this interface/tunnel.
+Recall that prune retransmission
+defaults to on on point-to-point links and tunnels, and off on
+multi-access links.
+.TP
+.B allow_nonpruners
+By default, \fImrouted\fP refuses to peer with DVMRP neighbors that
+do not claim to support pruning. This option allows such peerings
+on this interface.
+.TP
+.B notransit
+A specialized case of route filtering; no route learned from an interface
+marked "notransit" will be advertised on another interface marked
+"notransit". Marking only a single interface "notransit" has no meaning.
+.TP
+.BI accept|deny " (route/mask-len [exact])+ [bidir]"
+The
+.B accept
+and
+.B deny
+commands allow rudimentary route filtering. The
+.B accept
+command causes
+.I mrouted
+to accept only the listed routes on the configured interface; the
+.B deny
+command causes
+.I mrouted
+to accept all but the listed routes.
+Only one of
+.B accept
+or
+.B deny
+commands may be used on a given interface.
+
+The list of routes follows the
+.B accept
+or
+.B deny
+keyword. If the keyword
+.I exact
+follows a route, then only that route is matched; otherwise, that route
+and any more specific route is matched. For example,
+.B deny 0/0
+denys all routes, while
+.B deny 0/0 exact
+denys only the default route. The default route may also be specified
+with the
+.B default
+keyword.
+
+The
+.B bidir
+keyword enables bidirectional route filtering; the filter will be applied
+to routes on both output and input. Without the
+.B bidir
+keyword,
+.B accept
+and
+.B deny
+filters are only applied on input. Poison reverse routes are never
+filtered out.
.PP
.I Mrouted
will not initiate execution if it has fewer than two enabled vifs,
@@ -213,7 +398,7 @@ 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).
+direct tunnels (i.e. eliminate the middle man).
.SH "EXAMPLE CONFIGURATION"
.PP
This is an example configuration for a mythical multicast router at a big
@@ -267,9 +452,9 @@ good-bye messages to all neighboring routers).
.IP TERM
same as INT
.IP USR1
-dumps the internal routing tables to /usr/tmp/mrouted.dump.
+dumps the internal routing tables to /var/tmp/mrouted.dump.
.IP USR2
-dumps the internal cache tables to /usr/tmp/mrouted.cache.
+dumps the internal cache tables to /var/tmp/mrouted.cache.
.IP QUIT
dumps the internal routing tables to stderr (only if
.I mrouted
@@ -277,22 +462,23 @@ was invoked with a non-zero debug level).
.PP
For convenience in sending signals,
.I mrouted
-writes its pid to /etc/mrouted.pid upon startup.
+writes its pid to /var/run/mrouted.pid upon startup.
.bp
.SH EXAMPLE
.PP
-The routing tables look like this:
+The routing tables dumped in /var/tmp/mrouted.dump look like this:
.nf
+.ft C
Virtual Interface Table
Vif Local-Address Metric Thresh Flags
- 0 36.2.0.8 subnet: 36.2 1 1 querier
+ 0 36.2.0.8 subnet: 36.2/16 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
+ 1 36.11.0.1 subnet: 36.11/16 1 1 querier
groups: 224.0.2.1
224.0.1.0
224.0.0.4
@@ -300,9 +486,9 @@ Virtual Interface Table
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
+ peers: 36.8.0.77 (3.255)
+ boundaries: 239.0.1/24
+ : 239.1.2/24
pkts in: 34545433
pkts out: 234342
@@ -318,6 +504,7 @@ Multicast Routing Table (1136 entries)
.
.fi
+.LP
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
@@ -344,8 +531,9 @@ 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:
+The cache tables dumped in /var/tmp/mrouted.cache look like this:
.nf
+.ft C
Multicast Routing Cache Table (147 entries)
Origin Mcast-group CTmr Age Ptmr IVif Forwvifs
@@ -360,9 +548,11 @@ Multicast Routing Cache Table (147 entries)
>198.106.194.22
.fi
+.LP
Each entry is characterized by the origin subnet number and mask and the
destination multicast group. The 'CTmr' field indicates the lifetime
of the entry. The entry is deleted from the cache table
+(or refreshed, if traffic is flowing)
when the timer decrements to zero. The 'Age' field is the time since
this cache entry was originally created. Since cache entries get refreshed
if traffic is flowing, routing entries can grow very old.
@@ -377,21 +567,55 @@ subnet, a prune message is sent to the upstream router. They are
indicated by a "P" after the vif number. 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
+that interface. An unlisted interface is a leaf subnet with 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.
An additional line with a ">" as the first character is printed for
each source on the subnet. Note that there can be many sources in
one subnet.
+An additional line with a "<" as the first character is printed
+describing any prunes received from downstream dependent neighbors
+for this subnet and group.
.SH FILES
-/etc/mrouted.conf
-.br
-/etc/mrouted.pid
-.br
-/usr/tmp/mrouted.dump
-.br
-/usr/tmp/mrouted.cache
+.TP 25
+.B /etc/mrouted.conf
+.IR mrouted 's
+configuration file.
+.TP
+.B /var/run/mrouted.pid
+.IR mrouted 's
+PID file.
+.TP
+.B /var/tmp/mrouted.dump
+Where
+.I mrouted
+dumps its routing table when sent a SIGUSR1.
+.TP
+.B /var/tmp/mrouted.cache
+Where
+.I mrouted
+dumps its forwarding cache when sent a SIGUSR2.
+.PP
+Note that these files are located in the following places on pre-4.4BSD systems:
+.TP 25
+.B /etc/mrouted.conf
+.IR mrouted 's
+configuration file.
+.TP
+.B /etc/mrouted.pid
+.IR mrouted 's
+PID file.
+.TP
+.B /usr/tmp/mrouted.dump
+Where
+.I mrouted
+dumps its routing table when sent a SIGUSR1.
+.TP
+.B /usr/tmp/mrouted.cache
+Where
+.I mrouted
+dumps its forwarding cache when sent a SIGUSR2.
.SH SEE ALSO
.BR mrinfo (8) ,
.BR mtrace (8) ,
diff --git a/usr.sbin/mrouted/mrouted.conf b/usr.sbin/mrouted/mrouted.conf
index eb3bad3f4995..6891435770e8 100644
--- a/usr.sbin/mrouted/mrouted.conf
+++ b/usr.sbin/mrouted/mrouted.conf
@@ -1,4 +1,4 @@
-# $Id: mrouted.conf,v 3.8 1995/11/29 22:40:47 fenner Rel $
+# mrouted.conf,v 3.8 1995/11/29 22:40:47 fenner Rel
#
# This is the configuration file for "mrouted", an IP multicast router.
# mrouted looks for it in "/etc/mrouted.conf".
diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h
index b3e3c88f8988..18cced1104e4 100644
--- a/usr.sbin/mrouted/pathnames.h
+++ b/usr.sbin/mrouted/pathnames.h
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: pathnames.h,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * pathnames.h,v 3.8 1995/11/29 22:36:57 fenner Rel
*/
#define _PATH_MROUTED_CONF "/etc/mrouted.conf"
diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c
index fe3cdd5b4e14..0498d79ee8fd 100644
--- a/usr.sbin/mrouted/prune.c
+++ b/usr.sbin/mrouted/prune.c
@@ -7,42 +7,36 @@
* Leland Stanford Junior University.
*
*
- * $Id: prune.c,v 3.8 1995/11/29 22:36:34 fenner Rel $
+ * prune.c,v 3.8.4.59 1998/03/01 02:06:32 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+prune.c,v 3.8.4.59 1998/03/01 02:06:32 fenner Exp $";
+#endif
+
extern int cache_lifetime;
-extern int max_prune_lifetime;
+extern int prune_lifetime;
extern struct rtentry *routing_table;
extern int phys_vif;
+extern int allow_black_holes;
+
/*
- * dither cache lifetime to obtain a value between x and 2*x
+ * randomize value to obtain a value between .5x and 1.5x
+ * in order to prevent synchronization
*/
#ifdef SYSV
-#define CACHE_LIFETIME(x) ((x) + (lrand48() % (x)))
+#define JITTERED_VALUE(x) ((x)/2 + (lrand48() % (x)))
#else
-#define CACHE_LIFETIME(x) ((x) + (random() % (x)))
+#define JITTERED_VALUE(x) ((x)/2 + (random() % (x)))
#endif
+#define CACHE_LIFETIME(x) JITTERED_VALUE(x) /* XXX */
-#define CHK_GS(x, y) { \
- switch(x) { \
- case 2: \
- case 4: \
- case 8: \
- case 16: \
- case 32: \
- case 64: \
- case 128: \
- case 256: y = 1; \
- break; \
- default: y = 0; \
- } \
- }
-
struct gtable *kernel_table; /* ptr to list of kernel grp entries*/
static struct gtable *kernel_no_route; /* list of grp entries w/o routes */
struct gtable *gtp; /* pointer for kernel rt entries */
@@ -51,17 +45,20 @@ unsigned int kroutes; /* current number of cache entries */
/****************************************************************************
Functions that are local to prune.c
****************************************************************************/
+static int scoped_addr __P((vifi_t vifi, u_int32 addr));
static void prun_add_ttls __P((struct gtable *gt));
static int pruning_neighbor __P((vifi_t vifi, u_int32 addr));
static int can_mtrace __P((vifi_t vifi, u_int32 addr));
static struct ptable * find_prune_entry __P((u_int32 vr, struct ptable *pt));
+static void remove_sources __P((struct gtable *gt));
+static void rexmit_prune __P((void *arg));
static void expire_prune __P((vifi_t vifi, struct gtable *gt));
static void send_prune __P((struct gtable *gt));
static void send_graft __P((struct gtable *gt));
static void send_graft_ack __P((u_int32 src, u_int32 dst,
- u_int32 origin, u_int32 grp));
+ u_int32 origin, u_int32 grp,
+ vifi_t vifi));
static void update_kernel __P((struct gtable *g));
-static char * scaletime __P((u_long t));
/*
* Updates the ttl values for each vif.
@@ -83,16 +80,34 @@ prun_add_ttls(gt)
/*
* checks for scoped multicast addresses
+ * XXX I want to make the check of allow_black_holes based on ALLOW_BLACK_HOLES
+ * but macros are not functions.
*/
#define GET_SCOPE(gt) { \
register vifi_t _i; \
- if ((ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \
+ VIFM_CLRALL((gt)->gt_scope); \
+ if (allow_black_holes || \
+ (ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \
for (_i = 0; _i < numvifs; _i++) \
if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
VIFM_SET(_i, (gt)->gt_scope); \
+ } \
+ if ((gt)->gt_route == NULL || ((gt)->gt_route->rt_parent != NO_VIF && \
+ VIFM_ISSET((gt)->gt_route->rt_parent, (gt)->gt_scope))) \
+ VIFM_SETALL((gt)->gt_scope);
+
+#define APPLY_SCOPE(gt) VIFM_CLR_MASK((gt)->gt_grpmems, (gt)->gt_scope)
+
+#define GET_MEMBERSHIP(gt, vifi) { \
+ if ((gt)->gt_route && \
+ VIFM_ISSET((vifi), (gt)->gt_route->rt_children) && \
+ (!SUBS_ARE_PRUNED((gt)->gt_route->rt_subordinates, \
+ uvifs[vifi].uv_nbrmap, (gt)->gt_prunes) || \
+ grplst_mem((vifi), (gt)->gt_mcastgrp))) \
+ VIFM_SET((vifi), (gt)->gt_grpmems); \
}
-int
+static int
scoped_addr(vifi, addr)
vifi_t vifi;
u_int32 addr;
@@ -106,6 +121,38 @@ scoped_addr(vifi, addr)
return 0;
}
+/*
+ * Determine the list of outgoing vifs, based upon
+ * route subordinates, prunes received, and group
+ * memberships.
+ */
+void
+determine_forwvifs(gt)
+ struct gtable *gt;
+{
+ vifi_t i;
+
+ VIFM_CLRALL(gt->gt_grpmems);
+ for (i = 0; i < numvifs; i++) {
+ GET_MEMBERSHIP(gt, i);
+ }
+ GET_SCOPE(gt);
+ APPLY_SCOPE(gt);
+}
+
+/*
+ * Send a prune or a graft if necessary.
+ */
+void
+send_prune_or_graft(gt)
+ struct gtable *gt;
+{
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
+ send_prune(gt);
+ else if (gt->gt_prsent_timer)
+ send_graft(gt);
+}
+
/*
* Determine if mcastgrp has a listener on vifi
*/
@@ -180,15 +227,8 @@ pruning_neighbor(vifi, addr)
if (n == NULL)
return 0;
- if (n->al_flags & NF_PRUNE)
- return 1;
-
- /*
- * Versions from 3.0 to 3.4 relied on the version number to identify
- * that they could handle pruning.
- */
vers = NBR_VERS(n);
- return (vers >= 0x0300 && vers <= 0x0304);
+ return (vers >= 0x0300 && ((vers & 0xff00) != 0x0a00));
}
/*
@@ -203,17 +243,10 @@ can_mtrace(vifi, addr)
int vers;
if (n == NULL)
- return 0;
-
- if (n->al_flags & NF_MTRACE)
- return 1;
+ return 1; /* fail "safe" */
- /*
- * Versions 3.3 and 3.4 relied on the version number to identify
- * that they could handle traceroute.
- */
vers = NBR_VERS(n);
- return (vers >= 0x0303 && vers <= 0x0304);
+ return (vers >= 0x0303 && ((vers & 0xff00) != 0x0a00));
}
/*
@@ -234,6 +267,76 @@ find_prune_entry(vr, pt)
}
/*
+ * Remove all the sources hanging off the group table entry from the kernel
+ * cache. Remember the packet counts wherever possible, to keep the mtrace
+ * counters consistent. This prepares for possible prune retransmission,
+ * either on a multi-access network or when a prune that we sent upstream
+ * has expired.
+ */
+static void
+remove_sources(gt)
+ struct gtable *gt;
+{
+ struct stable *st;
+ struct sioc_sg_req sg_req;
+
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+
+ /*
+ * call k_del_rg() on every one of the gt->gt_srctbl entries
+ * but first save the packet count so that the mtrace packet
+ * counters can remain approximately correct. There's a race
+ * here but it's minor.
+ */
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ if (st->st_ctime == 0)
+ continue;
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune deleting (%s %s) (next is %d sec)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prune_rexmit);
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ sg_req.pktcnt = 0;
+ }
+ k_del_rg(st->st_origin, gt);
+ st->st_ctime = 0; /* flag that it's not in the kernel any more */
+ st->st_savpkt += sg_req.pktcnt;
+ kroutes--;
+ }
+
+ /*
+ * Now, add_table_entry will prune when asked to add a cache entry.
+ */
+}
+
+/*
+ * Prepare for possible prune retransmission
+ */
+static void
+rexmit_prune(arg)
+ void *arg;
+{
+ struct gtable *gt = *(struct gtable **)arg;
+
+ free(arg);
+
+ gt->gt_rexmit_timer = 0;
+
+ /* Make sure we're still not forwarding traffic */
+ if (!VIFM_ISEMPTY(gt->gt_grpmems)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune (%s %s): gm:%x",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_grpmems);
+ return;
+ }
+
+ remove_sources(gt);
+}
+
+/*
* Send a prune message to the dominant router for
* this source.
*
@@ -247,38 +350,77 @@ send_prune(gt)
char *p;
int i;
int datalen;
- u_int32 src;
u_int32 dst;
u_int32 tmp;
+ int rexmitting = 0;
+ struct uvif *v;
- /* Don't process any prunes if router is not pruning */
- if (pruning == 0)
- return;
-
- /* Can't process a prune if we don't have an associated route */
- if (gt->gt_route == NULL)
+ /*
+ * Can't process a prune if we don't have an associated route
+ * or if the route points to a local interface.
+ */
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF ||
+ gt->gt_route->rt_gateway == 0)
return;
/* Don't send a prune to a non-pruning router */
if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway))
return;
+ v = &uvifs[gt->gt_route->rt_parent];
/*
* sends a prune message to the router upstream.
*/
- src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+#if 0
+ dst = v->uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
dst = gt->gt_route->rt_gateway;
+#endif
p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
datalen = 0;
/*
- * determine prune lifetime
+ * determine prune lifetime, if this isn't a retransmission.
+ *
+ * Use interface-specified lifetime if there is one.
*/
- gt->gt_prsent_timer = gt->gt_timer;
- for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next)
- if (pt->pt_timer < gt->gt_prsent_timer)
- gt->gt_prsent_timer = pt->pt_timer;
+ if (gt->gt_prsent_timer == 0) {
+ int l = prune_lifetime;
+
+ if (v->uv_prune_lifetime != 0)
+ l = v->uv_prune_lifetime;
+
+ gt->gt_prsent_timer = JITTERED_VALUE(l);
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next)
+ if (pt->pt_timer < gt->gt_prsent_timer)
+ gt->gt_prsent_timer = pt->pt_timer;
+ } else if (gt->gt_prsent_timer < 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "asked to rexmit? (%s,%s)/%d on vif %d to %s with negative time",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ } else
+ rexmitting = 1;
+
+ if (rexmitting && !(v->uv_flags & VIFF_REXMIT_PRUNES)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not rexmitting prune for (%s %s)/%d on vif %d to %s",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
+ if (gt->gt_prsent_timer <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not bothering to send prune for (%s,%s)/%d on vif %d to %s because it's too short",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
/*
* If we have a graft pending, cancel graft retransmission
@@ -294,14 +436,27 @@ send_prune(gt)
*p++ = ((char *)&(tmp))[i];
datalen += 12;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE,
- htonl(MROUTED_LEVEL), datalen);
+ send_on_vif(v, dst, DVMRP_PRUNE, datalen);
- log(LOG_DEBUG, 0, "sent prune for (%s %s)/%d on vif %d to %s",
- inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1),
- inet_fmt(gt->gt_mcastgrp, s2),
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s prune for (%s %s)/%d on vif %d to %s",
+ rexmitting ? "rexmitted" : "sent",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
gt->gt_prsent_timer, gt->gt_route->rt_parent,
inet_fmt(gt->gt_route->rt_gateway, s3));
+
+ if ((v->uv_flags & VIFF_REXMIT_PRUNES) &&
+ gt->gt_rexmit_timer == 0 &&
+ gt->gt_prsent_timer > gt->gt_prune_rexmit) {
+ struct gtable **arg =
+ (struct gtable **)malloc(sizeof (struct gtable **));
+
+ *arg = gt;
+ gt->gt_rexmit_timer = timer_setTimer(
+ JITTERED_VALUE(gt->gt_prune_rexmit),
+ rexmit_prune, arg);
+ gt->gt_prune_rexmit *= 2;
+ }
}
/*
@@ -318,16 +473,28 @@ send_graft(gt)
register char *p;
register int i;
int datalen;
- u_int32 src;
u_int32 dst;
/* Can't send a graft without an associated route */
- if (gt->gt_route == NULL)
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF) {
+ gt->gt_grftsnt = 0;
return;
-
- src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+ }
+
+ gt->gt_prsent_timer = 0;
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ if (gt->gt_grftsnt == 0)
+ gt->gt_grftsnt = 1;
+
+#if 0
+ dst = uvifs[gt->gt_route->rt_parent].uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
dst = gt->gt_route->rt_gateway;
-
+#endif
+
p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
datalen = 0;
@@ -337,13 +504,10 @@ send_graft(gt)
*p++ = ((char *)&(gt->gt_mcastgrp))[i];
datalen += 8;
- if (datalen != 0) {
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
- htonl(MROUTED_LEVEL), datalen);
- }
+ send_on_vif(&uvifs[gt->gt_route->rt_parent], dst, DVMRP_GRAFT, datalen);
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d",
- inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1),
- inet_fmt(gt->gt_mcastgrp, s2),
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent);
}
@@ -351,11 +515,12 @@ send_graft(gt)
* Send an ack that a graft was received
*/
static void
-send_graft_ack(src, dst, origin, grp)
+send_graft_ack(src, dst, origin, grp, vifi)
u_int32 src;
u_int32 dst;
u_int32 origin;
u_int32 grp;
+ vifi_t vifi;
{
register char *p;
register int i;
@@ -370,11 +535,24 @@ send_graft_ack(src, dst, origin, grp)
*p++ = ((char *)&(grp))[i];
datalen += 8;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
- htonl(MROUTED_LEVEL), datalen);
+ if (vifi == NO_VIF)
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
+ htonl(MROUTED_LEVEL), datalen);
+ else {
+#if 0
+ if (uvifs[vifi].uv_flags & VIFF_TUNNEL)
+ dst = dvmrp_group; /* XXX */
+#endif
+ send_on_vif(&uvifs[vifi], dst, DVMRP_GRAFT_ACK, datalen);
+ }
- log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
- inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3));
+ IF_DEBUG(DEBUG_PRUNE)
+ if (vifi == NO_VIF)
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3));
+ else
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s on vif %d",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3), vifi);
}
/*
@@ -387,7 +565,8 @@ update_kernel(g)
struct stable *st;
for (st = g->gt_srctbl; st; st = st->st_next)
- k_add_rg(st->st_origin, g);
+ if (st->st_ctime != 0)
+ k_add_rg(st->st_origin, g);
}
/****************************************************************************
@@ -403,7 +582,7 @@ update_kernel(g)
*/
struct gtable *
find_grp(grp)
- u_long grp;
+ u_int32 grp;
{
struct gtable *gt;
@@ -423,7 +602,7 @@ find_grp(grp)
struct stable *
find_grp_src(gt, src)
struct gtable *gt;
- u_long src;
+ u_int32 src;
{
struct stable *st;
u_long grp = gt->gt_mcastgrp;
@@ -444,9 +623,9 @@ int
next_grp_src_mask(gtpp, stpp, grp, src, mask)
struct gtable **gtpp; /* ordered by group */
struct stable **stpp; /* ordered by source */
- u_long grp;
- u_long src;
- u_long mask;
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
{
struct gtable *gt, *gbest = NULL;
struct stable *st, *sbest = NULL;
@@ -508,76 +687,6 @@ refresh_sg(sg, gt, st)
}
/*
- * Return pointer to a specific route entry. This must be a separate
- * function from find_route() which modifies rtp.
- */
-struct rtentry *
-snmp_find_route(src, mask)
- register u_long src, mask;
-{
- register struct rtentry *rt;
-
- for (rt = routing_table; rt; rt = rt->rt_next) {
- if (src == rt->rt_origin && mask == rt->rt_originmask)
- return rt;
- }
- return NULL;
-}
-
-/*
- * Find next route entry > specification
- */
-int
-next_route(rtpp, src, mask)
- struct rtentry **rtpp;
- u_long src;
- u_long mask;
-{
- struct rtentry *rt, *rbest = NULL;
-
- /* Among all entries > spec, find "lowest" one in order */
- for (rt = routing_table; rt; rt=rt->rt_next) {
- if ((ntohl(rt->rt_origin) > ntohl(src)
- || (ntohl(rt->rt_origin) == ntohl(src)
- && ntohl(rt->rt_originmask) > ntohl(mask)))
- && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
- || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
- && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
- rbest = rt;
- }
- (*rtpp) = rbest;
- return (*rtpp)!=0;
-}
-
-/*
- * Given a routing table entry, and a vifi, find the next vifi/entry
- */
-int
-next_route_child(rtpp, src, mask, vifi)
- struct rtentry **rtpp;
- u_long src;
- u_long mask;
- vifi_t *vifi; /* vif at which to start looking */
-{
- struct rtentry *rt;
-
- /* Get (S,M) entry */
- if (!((*rtpp) = snmp_find_route(src,mask)))
- if (!next_route(rtpp, src, mask))
- return 0;
-
- /* Continue until we get one with a valid next vif */
- do {
- for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
- if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
- return 1;
- *vifi = 0;
- } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
-
- return 0;
-}
-
-/*
* Given a routing table entry, and a vifi, find the next entry
* equal to or greater than those
*/
@@ -585,13 +694,11 @@ int
next_child(gtpp, stpp, grp, src, mask, vifi)
struct gtable **gtpp;
struct stable **stpp;
- u_long grp;
- u_long src;
- u_long mask;
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
vifi_t *vifi; /* vif at which to start looking */
{
- struct stable *st;
-
/* Get (G,S,M) entry */
if (mask!=0xFFFFFFFF
|| !((*gtpp) = find_grp(grp))
@@ -634,7 +741,15 @@ add_table_entry(origin, mcastgrp)
struct rtentry *r;
struct gtable *gt,**gtnp,*prev_gt;
struct stable *st,**stnp;
- vifi_t i;
+
+ /*
+ * Since we have to enable mrouting to get the version number,
+ * some cache creation requests can sneak through. Ignore them
+ * since we're not going to do useful stuff until we've performed
+ * final initialization.
+ */
+ if (!did_final_init)
+ return;
#ifdef DEBUG_MFC
md_log(MD_MISS, origin, mcastgrp);
@@ -670,35 +785,20 @@ add_table_entry(origin, mcastgrp)
gt->gt_mcastgrp = mcastgrp;
gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
time(&gt->gt_ctime);
- gt->gt_grpmems = 0;
- gt->gt_scope = 0;
gt->gt_prsent_timer = 0;
gt->gt_grftsnt = 0;
gt->gt_srctbl = NULL;
gt->gt_pruntbl = NULL;
gt->gt_route = r;
+ gt->gt_rexmit_timer = 0;
+ NBRM_CLRALL(gt->gt_prunes);
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
#ifdef RSRR
gt->gt_rsrr_cache = NULL;
#endif
- if (r != NULL) {
- /* obtain the multicast group membership list */
- for (i = 0; i < numvifs; i++) {
- if (VIFM_ISSET(i, r->rt_children) &&
- !(VIFM_ISSET(i, r->rt_leaves)))
- VIFM_SET(i, gt->gt_grpmems);
-
- if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
- VIFM_SET(i, gt->gt_grpmems);
- }
- GET_SCOPE(gt);
- if (VIFM_ISSET(r->rt_parent, gt->gt_scope))
- gt->gt_scope = -1;
- gt->gt_grpmems &= ~gt->gt_scope;
- } else {
- gt->gt_scope = -1;
- gt->gt_grpmems = 0;
- }
+ /* Calculate forwarding vifs */
+ determine_forwvifs(gt);
/* update ttls */
prun_add_ttls(gt);
@@ -715,8 +815,7 @@ add_table_entry(origin, mcastgrp)
g = gtp ? gtp->gt_gnext : kernel_table;
log(LOG_WARNING, 0, "Entry for (%s %s) (rt:%x) exists (rt:%x)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
- inet_fmt(g->gt_mcastgrp, s2),
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
r, g->gt_route);
} else {
if (gtp) {
@@ -750,39 +849,46 @@ add_table_entry(origin, mcastgrp)
st->st_origin = origin;
st->st_pktcnt = 0;
+ st->st_savpkt = 0;
+ time(&st->st_ctime);
st->st_next = *stnp;
*stnp = st;
} else {
+ if (st->st_ctime == 0) {
+ /* An old source which we're keeping around for statistics */
+ time(&st->st_ctime);
+ } else {
#ifdef DEBUG_MFC
- md_log(MD_DUPE, origin, mcastgrp);
+ md_log(MD_DUPE, origin, mcastgrp);
#endif
- log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
- inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
- /* XXX Doing this should cause no harm, and may ensure
- * kernel<>mrouted synchronization */
- k_add_rg(origin, gt);
- return;
+ /* Ignore kernel->mrouted retransmissions */
+ if (time(0) - st->st_ctime > 5)
+ log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ k_add_rg(origin, gt);
+ return;
+ }
}
kroutes++;
k_add_rg(origin, gt);
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d",
inet_fmt(origin, s1),
inet_fmt(mcastgrp, s2),
gt->gt_grpmems, r ? r->rt_parent : -1);
-
- /* If there are no leaf vifs
- * which have this group, then
- * mark this src-grp as a prune candidate.
+
+ /*
+ * If there are no downstream routers that want traffic for
+ * this group, send (or retransmit) a prune upstream.
*/
- if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway)
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
send_prune(gt);
}
/*
- * An mrouter has gone down and come up on an interface
- * Forward on that interface immediately
+ * A router has gone down. Remove prune state pertinent to that router.
*/
void
reset_neighbor_state(vifi, addr)
@@ -804,35 +910,29 @@ reset_neighbor_state(vifi, addr)
*/
if (vifi == r->rt_parent) {
if (addr == r->rt_gateway) {
+ IF_DEBUG(DEBUG_PEER)
log(LOG_DEBUG, 0, "reset_neighbor_state parent reset (%s %s)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
- inet_fmt(g->gt_mcastgrp, s2));
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
g->gt_prsent_timer = 0;
g->gt_grftsnt = 0;
- while (st = g->gt_srctbl) {
+ while ((st = g->gt_srctbl) != NULL) {
g->gt_srctbl = st->st_next;
- k_del_rg(st->st_origin, g);
- kroutes--;
+ if (st->st_ctime != 0) {
+ k_del_rg(st->st_origin, g);
+ kroutes--;
+ }
free(st);
}
}
} else {
/*
- * Neighbor was not the parent, send grafts to join the groups
- */
- if (g->gt_prsent_timer) {
- g->gt_grftsnt = 1;
- send_graft(g);
- g->gt_prsent_timer = 0;
- }
-
- /*
* Remove any prunes that this router has sent us.
*/
ptnp = &g->gt_pruntbl;
while ((pt = *ptnp) != NULL) {
if (pt->pt_vifi == vifi && pt->pt_router == addr) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
*ptnp = pt->pt_next;
free(pt);
} else
@@ -843,15 +943,8 @@ reset_neighbor_state(vifi, addr)
* And see if we want to forward again.
*/
if (!VIFM_ISSET(vifi, g->gt_grpmems)) {
- if (VIFM_ISSET(vifi, r->rt_children) &&
- !(VIFM_ISSET(vifi, r->rt_leaves)))
- VIFM_SET(vifi, g->gt_grpmems);
-
- if (VIFM_ISSET(vifi, r->rt_leaves) &&
- grplst_mem(vifi, g->gt_mcastgrp))
- VIFM_SET(vifi, g->gt_grpmems);
-
- g->gt_grpmems &= ~g->gt_scope;
+ GET_MEMBERSHIP(g, vifi);
+ APPLY_SCOPE(g);
prun_add_ttls(g);
/* Update kernel state */
@@ -861,8 +954,17 @@ reset_neighbor_state(vifi, addr)
rsrr_cache_send(g,1);
#endif /* RSRR */
- log(LOG_DEBUG, 0, "reset member state (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ /*
+ * If removing this prune causes us to start forwarding
+ * (e.g. the neighbor rebooted), and we sent a prune upstream,
+ * send a graft to cancel the prune.
+ */
+ if (!VIFM_ISEMPTY(g->gt_grpmems) && g->gt_prsent_timer)
+ send_graft(g);
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "reset neighbor state (%s %s) gm:%x",
+ RT_FMT(r, s1),
inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
}
}
@@ -886,18 +988,20 @@ del_table_entry(r, mcastgrp, del_flag)
if (del_flag == DEL_ALL_ROUTES) {
g = r->rt_groups;
while (g) {
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
- inet_fmt(g->gt_mcastgrp, s2));
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
st = g->gt_srctbl;
while (st) {
- if (k_del_rg(st->st_origin, g) < 0) {
- log(LOG_WARNING, errno,
- "del_table_entry trying to delete (%s, %s)",
- inet_fmt(st->st_origin, s1),
- inet_fmt(g->gt_mcastgrp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
- kroutes--;
prev_st = st;
st = st->st_next;
free(prev_st);
@@ -924,6 +1028,9 @@ del_table_entry(r, mcastgrp, del_flag)
rsrr_cache_send(g,0);
rsrr_cache_clean(g);
#endif /* RSRR */
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
+
prev_g = g;
g = g->gt_next;
free(prev_g);
@@ -938,18 +1045,20 @@ del_table_entry(r, mcastgrp, del_flag)
prev_g = (struct gtable *)&r->rt_groups;
for (g = r->rt_groups; g; g = g->gt_next) {
if (g->gt_mcastgrp == mcastgrp) {
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
- inet_fmt(g->gt_mcastgrp, s2));
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
st = g->gt_srctbl;
while (st) {
- if (k_del_rg(st->st_origin, g) < 0) {
- log(LOG_WARNING, errno,
- "del_table_entry trying to delete (%s, %s)",
- inet_fmt(st->st_origin, s1),
- inet_fmt(g->gt_mcastgrp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
- kroutes--;
prev_st = st;
st = st->st_next;
free(prev_st);
@@ -977,6 +1086,8 @@ del_table_entry(r, mcastgrp, del_flag)
g->gt_next->gt_prev = NULL;
prev_g->gt_next = g->gt_next;
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
#ifdef RSRR
/* Send route change notification to reservation protocol. */
rsrr_cache_send(g,0);
@@ -995,48 +1106,70 @@ del_table_entry(r, mcastgrp, del_flag)
* update kernel table entry when a route entry changes
*/
void
-update_table_entry(r)
+update_table_entry(r, old_parent_gw)
struct rtentry *r;
+ u_int32 old_parent_gw;
{
struct gtable *g;
- struct ptable *pt, *prev_pt;
- vifi_t i;
+ struct ptable *pt, **ptnp;
for (g = r->rt_groups; g; g = g->gt_next) {
- pt = g->gt_pruntbl;
- while (pt) {
- prev_pt = pt->pt_next;
- free(pt);
- pt = prev_pt;
- }
- g->gt_pruntbl = NULL;
-
- g->gt_grpmems = 0;
+ ptnp = &g->gt_pruntbl;
+ /*
+ * Delete prune entries from non-children, or non-subordinates.
+ */
+ while ((pt = *ptnp)) {
+ if (!VIFM_ISSET(pt->pt_vifi, r->rt_children) ||
+ !NBRM_ISSET(pt->pt_index, r->rt_subordinates)) {
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "update_table_entry deleting prune for (%s %s) from %s on vif %d -%s%s",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi,
+ VIFM_ISSET(pt->pt_vifi, r->rt_children) ? "" : " not a child",
+ NBRM_ISSET(pt->pt_index, r->rt_subordinates) ? "" : " not a subordinate");
+
+ if (!NBRM_ISSET(pt->pt_index, g->gt_prunes)) {
+ log(LOG_WARNING, 0,
+ "gt_prunes lost track of (%s %s) from %s on vif %d",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi);
+ }
- /* obtain the multicast group membership list */
- for (i = 0; i < numvifs; i++) {
- if (VIFM_ISSET(i, r->rt_children) &&
- !(VIFM_ISSET(i, r->rt_leaves)))
- VIFM_SET(i, g->gt_grpmems);
-
- if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, g->gt_mcastgrp))
- VIFM_SET(i, g->gt_grpmems);
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+ continue;
+ }
+ ptnp = &((*ptnp)->pt_next);
}
- if (VIFM_ISSET(r->rt_parent, g->gt_scope))
- g->gt_scope = -1;
- g->gt_grpmems &= ~g->gt_scope;
- log(LOG_DEBUG, 0, "updating cache entries (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
- inet_fmt(g->gt_mcastgrp, s2),
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) old gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
g->gt_grpmems);
- if (g->gt_grpmems && g->gt_prsent_timer) {
- g->gt_grftsnt = 1;
- send_graft(g);
+ /*
+ * Forget about a prune or graft that we sent previously if we
+ * have a new parent router (since the new parent router will
+ * know nothing about what I sent to the previous parent). The
+ * old parent will forget any prune state it is keeping for us.
+ */
+ if (old_parent_gw != r->rt_gateway) {
g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
}
+ /* Recalculate membership */
+ determine_forwvifs(g);
+ /* send a prune or graft if needed. */
+ send_prune_or_graft(g);
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) new gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
/* update ttls and add entry into kernel */
prun_add_ttls(g);
update_kernel(g);
@@ -1044,12 +1177,6 @@ update_table_entry(r)
/* Send route change notification to reservation protocol. */
rsrr_cache_send(g,1);
#endif /* RSRR */
-
- /* Check if we want to prune this group */
- if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
- g->gt_timer = CACHE_LIFETIME(cache_lifetime);
- send_prune(g);
- }
}
}
@@ -1064,6 +1191,7 @@ update_lclgrp(vifi, mcastgrp)
struct rtentry *r;
struct gtable *g;
+ IF_DEBUG(DEBUG_MEMBER)
log(LOG_DEBUG, 0, "group %s joined on vif %d",
inet_fmt(mcastgrp, s1), vifi);
@@ -1076,13 +1204,14 @@ update_lclgrp(vifi, mcastgrp)
VIFM_ISSET(vifi, r->rt_children)) {
VIFM_SET(vifi, g->gt_grpmems);
- g->gt_grpmems &= ~g->gt_scope;
- if (g->gt_grpmems == 0)
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
continue;
prun_add_ttls(g);
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
update_kernel(g);
@@ -1102,9 +1231,9 @@ delete_lclgrp(vifi, mcastgrp)
vifi_t vifi;
u_int32 mcastgrp;
{
- struct rtentry *r;
struct gtable *g;
+ IF_DEBUG(DEBUG_MEMBER)
log(LOG_DEBUG, 0, "group %s left on vif %d",
inet_fmt(mcastgrp, s1), vifi);
@@ -1112,28 +1241,14 @@ delete_lclgrp(vifi, mcastgrp)
if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
break;
- if (g->gt_mcastgrp == mcastgrp) {
- int stop_sending = 1;
-
- r = g->gt_route;
- /*
- * If this is not a leaf, then we have router neighbors on this
- * vif. Only turn off forwarding if they have all pruned.
- */
- if (!VIFM_ISSET(vifi, r->rt_leaves)) {
- struct listaddr *vr;
-
- for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
- if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) {
- stop_sending = 0;
- break;
- }
- }
-
- if (stop_sending) {
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, g->gt_grpmems)) {
+ if (g->gt_route == NULL ||
+ SUBS_ARE_PRUNED(g->gt_route->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes)) {
VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(g->gt_route, s1),
inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
prun_add_ttls(g);
@@ -1147,7 +1262,7 @@ delete_lclgrp(vifi, mcastgrp)
* If there are no more members of this particular group,
* send prune upstream
*/
- if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway)
+ if (VIFM_ISEMPTY(g->gt_grpmems) && g->gt_route->rt_gateway)
send_prune(g);
}
}
@@ -1176,15 +1291,9 @@ accept_prune(src, dst, p, datalen)
u_int32 prun_tmr;
vifi_t vifi;
int i;
- int stop_sending;
struct rtentry *r;
struct gtable *g;
struct ptable *pt;
- struct listaddr *vr;
-
- /* Don't process any prunes if router is not pruning */
- if (pruning == 0)
- return;
if ((vifi = find_vif(src, dst)) == NO_VIF) {
log(LOG_INFO, 0,
@@ -1210,6 +1319,15 @@ accept_prune(src, dst, p, datalen)
((char *)&prun_tmr)[i] = *p++;
prun_tmr = ntohl(prun_tmr);
+ if (prun_tmr <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "ignoring prune from %s on vif %d for (%s %s)/%d because its lifetime is too short",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d",
inet_fmt(src, s1), vifi,
inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
@@ -1221,10 +1339,19 @@ accept_prune(src, dst, p, datalen)
g = gtp ? gtp->gt_gnext : kernel_table;
r = g->gt_route;
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "found grp state, (%s %s), metric is %d, children are %x, subords are %08x%08x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2), r->rt_metric,
+ r->rt_children, r->rt_subordinates.hi, r->rt_subordinates.lo);
if (!VIFM_ISSET(vifi, r->rt_children)) {
- log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)",
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s) (dominant on vif %d is %s)",
inet_fmt(src, s1), inet_fmt(prun_src, s2),
- inet_fmt(prun_grp, s3));
+ inet_fmt(prun_grp, s3), vifi,
+ inet_fmt(r->rt_dominants[vifi], s4));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
return;
}
if (VIFM_ISSET(vifi, g->gt_scope)) {
@@ -1234,19 +1361,22 @@ accept_prune(src, dst, p, datalen)
return;
}
if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) {
- /*
- * If it's about to expire, then it's only still around because
- * of timer granularity, so don't warn about it.
- */
- if (pt->pt_timer > 10) {
- log(LOG_WARNING, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x",
- "duplicate prune received on vif",
- vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2),
- inet_fmt(prun_grp, s3), prun_tmr,
- "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems);
- }
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x",
+ "duplicate prune received on vif",
+ vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3), prun_tmr,
+ "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems);
pt->pt_timer = prun_tmr;
} else {
+ struct listaddr *n = neighbor_info(vifi, src);
+
+ if (!n) {
+ log(LOG_WARNING, 0, "Prune from non-neighbor %s on vif %d!?",
+ inet_fmt(src, s1), vifi);
+ return;
+ }
+
/* allocate space for the prune structure */
pt = (struct ptable *)(malloc(sizeof(struct ptable)));
if (pt == NULL)
@@ -1258,28 +1388,37 @@ accept_prune(src, dst, p, datalen)
pt->pt_next = g->gt_pruntbl;
g->gt_pruntbl = pt;
- }
- /* Refresh the group's lifetime */
- g->gt_timer = CACHE_LIFETIME(cache_lifetime);
- if (g->gt_timer < prun_tmr)
- g->gt_timer = prun_tmr;
+ if (n) {
+ pt->pt_index = n->al_index;
+ NBRM_SET(n->al_index, g->gt_prunes);
+ }
+ }
/*
* check if any more packets need to be sent on the
* vif which sent this message
*/
- stop_sending = 1;
- for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
- if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) {
- stop_sending = 0;
- break;
- }
-
- if (stop_sending && !grplst_mem(vifi, prun_grp)) {
+ if (SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes) &&
+ !grplst_mem(vifi, prun_grp)) {
+ nbrbitmap_t tmp;
+
VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "vifnbrs=0x%08x%08x, subord=0x%08x%08x prunes=0x%08x%08x",
+ uvifs[vifi].uv_nbrmap.hi,uvifs[vifi].uv_nbrmap.lo,
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ g->gt_prunes.hi, g->gt_prunes.lo);
+ /* XXX debugging */
+ NBRM_COPY(r->rt_subordinates, tmp);
+ NBRM_MASK(tmp, uvifs[vifi].uv_nbrmap);
+ if (!NBRM_ISSETALLMASK(g->gt_prunes, tmp))
+ log(LOG_WARNING, 0, "subordinate error");
+ /* XXX end debugging */
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems);
prun_add_ttls(g);
@@ -1296,7 +1435,7 @@ accept_prune(src, dst, p, datalen)
* interface
* Send a prune message then upstream
*/
- if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
+ if (VIFM_ISEMPTY(g->gt_grpmems) && r->rt_gateway) {
send_prune(g);
}
} else {
@@ -1305,6 +1444,7 @@ accept_prune(src, dst, p, datalen)
* simply ignore the prune, as we are not forwarding this traffic
* downstream.
*/
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
log(LOG_DEBUG, 0, "%s (%s %s)/%d from %s",
"prune message received with no kernel entry for",
inet_fmt(prun_src, s1), inet_fmt(prun_grp, s2),
@@ -1338,22 +1478,19 @@ chkgrp_graft(vifi, mcastgrp)
* If the vif that was joined was a scoped vif,
* ignore it ; don't graft back
*/
- g->gt_grpmems &= ~g->gt_scope;
- if (g->gt_grpmems == 0)
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
continue;
- /* set the flag for graft retransmission */
- g->gt_grftsnt = 1;
-
/* send graft upwards */
send_graft(g);
- /* reset the prune timer and update cache timer*/
- g->gt_prsent_timer = 0;
- g->gt_timer = max_prune_lifetime;
+ /* update cache timer*/
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
prun_add_ttls(g);
@@ -1393,13 +1530,6 @@ accept_graft(src, dst, p, datalen)
struct gtable *g;
struct ptable *pt, **ptnp;
- if ((vifi = find_vif(src, dst)) == NO_VIF) {
- log(LOG_INFO, 0,
- "ignoring graft from non-neighbor %s",
- inet_fmt(src, s1));
- return;
- }
-
if (datalen < 8) {
log(LOG_WARNING, 0,
"received non-decipherable graft from %s",
@@ -1411,7 +1541,19 @@ accept_graft(src, dst, p, datalen)
((char *)&graft_src)[i] = *p++;
for (i = 0; i< 4; i++)
((char *)&graft_grp)[i] = *p++;
-
+
+ vifi = find_vif(src, dst);
+ send_graft_ack(dst, src, graft_src, graft_grp, vifi);
+
+ if (vifi == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft for (%s %s) from non-neighbor %s",
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3),
+ inet_fmt(src, s1));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)",
inet_fmt(src, s1), vifi,
inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3));
@@ -1433,12 +1575,14 @@ accept_graft(src, dst, p, datalen)
ptnp = &g->gt_pruntbl;
while ((pt = *ptnp) != NULL) {
if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
*ptnp = pt->pt_next;
free(pt);
VIFM_SET(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
prun_add_ttls(g);
@@ -1453,28 +1597,18 @@ accept_graft(src, dst, p, datalen)
}
}
- /* send ack downstream */
- send_graft_ack(dst, src, graft_src, graft_grp);
- g->gt_timer = max_prune_lifetime;
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
- if (g->gt_prsent_timer) {
- /* set the flag for graft retransmission */
- g->gt_grftsnt = 1;
-
+ if (g->gt_prsent_timer)
/* send graft upwards */
send_graft(g);
-
- /* reset the prune sent timer */
- g->gt_prsent_timer = 0;
- }
} else {
/*
* We have no state for the source and group in question.
- * We can simply acknowledge the graft, since we know
- * that we have no prune state, and grafts are requests
- * to remove prune state.
+ * This is fine, since we know that we have no prune state, and
+ * grafts are requests to remove prune state.
*/
- send_graft_ack(dst, src, graft_src, graft_grp);
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "%s (%s %s) from %s",
"graft received with no kernel entry for",
inet_fmt(graft_src, s1), inet_fmt(graft_grp, s2),
@@ -1523,6 +1657,7 @@ accept_g_ack(src, dst, p, datalen)
for (i = 0; i< 4; i++)
((char *)&grft_grp)[i] = *p++;
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s, %s)",
inet_fmt(src, s1), vifi,
inet_fmt(grft_src, s2), inet_fmt(grft_grp, s3));
@@ -1538,6 +1673,9 @@ accept_g_ack(src, dst, p, datalen)
"rcvd graft ack with no kernel entry for",
inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2),
inet_fmt(src, s3));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
return;
}
}
@@ -1576,6 +1714,8 @@ free_all_prunes()
prev_g = g;
g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
free(prev_g);
}
r->rt_groups = NULL;
@@ -1589,6 +1729,8 @@ free_all_prunes()
prev_g = g;
g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
free(prev_g);
}
kernel_no_route = NULL;
@@ -1612,27 +1754,32 @@ steal_sources(rt)
register struct stable *st, **stnp;
for (rp = rt->rt_next; rp; rp = rp->rt_next) {
+ if (rp->rt_groups == NULL)
+ continue;
if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
log(LOG_DEBUG, 0, "Route for %s stealing sources from %s",
- inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
- inet_fmts(rp->rt_origin, rp->rt_originmask, s2));
+ RT_FMT(rt, s1), RT_FMT(rp, s2));
for (gt = rp->rt_groups; gt; gt = gt->gt_next) {
stnp = &gt->gt_srctbl;
while ((st = *stnp) != NULL) {
if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
- inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ RT_FMT(rt, s1),
inet_fmt(st->st_origin, s3),
inet_fmt(gt->gt_mcastgrp, s4),
- inet_fmts(rp->rt_origin, rp->rt_originmask, s2));
- if (k_del_rg(st->st_origin, gt) < 0) {
- log(LOG_WARNING, errno, "%s (%s, %s)",
- "steal_sources trying to delete",
- inet_fmt(st->st_origin, s1),
- inet_fmt(gt->gt_mcastgrp, s2));
+ RT_FMT(rp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s, %s)",
+ "steal_sources trying to delete",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
*stnp = st->st_next;
- kroutes--;
free(st);
} else {
stnp = &st->st_next;
@@ -1646,22 +1793,27 @@ steal_sources(rt)
while ((gt = *gtnp) != NULL) {
if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask)
== rt->rt_origin)) {
+ IF_DEBUG(DEBUG_ROUTE)
log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
- inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ RT_FMT(rt, s1),
inet_fmt(gt->gt_srctbl->st_origin, s3),
inet_fmt(gt->gt_mcastgrp, s4),
"no_route table");
- if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
- log(LOG_WARNING, errno, "%s (%s %s)",
- "steal_sources trying to delete",
- inet_fmt(gt->gt_srctbl->st_origin, s1),
- inet_fmt(gt->gt_mcastgrp, s2));
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "steal_sources trying to delete",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
- kroutes--;
free(gt->gt_srctbl);
*gtnp = gt->gt_next;
if (gt->gt_next)
gt->gt_next->gt_prev = gt->gt_prev;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
free(gt);
} else {
gtnp = &gt->gt_next;
@@ -1683,31 +1835,98 @@ age_table_entry()
struct ptable *pt, **ptnp;
struct sioc_sg_req sg_req;
- log(LOG_DEBUG, 0, "ageing entries");
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "aging forwarding cache entries");
gtnptr = &kernel_table;
while ((gt = *gtnptr) != NULL) {
+ vifi_t i; /* XXX Debugging */
+ int fixit = 0; /* XXX Debugging */
+
r = gt->gt_route;
+ /* XXX Debugging... */
+ for (i = 0; i < numvifs; i++) {
+ /*
+ * If we're not sending on this vif,
+ * And this group isn't scoped on this vif,
+ * And I'm the parent for this route on this vif,
+ * And there are subordinates on this vif,
+ * And all of the subordinates haven't pruned,
+ * YELL LOUDLY
+ * and remember to fix it up later
+ */
+ if (!VIFM_ISSET(i, gt->gt_grpmems) &&
+ !VIFM_ISSET(i, gt->gt_scope) &&
+ VIFM_ISSET(i, r->rt_children) &&
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates) &&
+ !SUBS_ARE_PRUNED(r->rt_subordinates, uvifs[i].uv_nbrmap, gt->gt_prunes)) {
+ log(LOG_WARNING, 0, "(%s %s) is blackholing on vif %d",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), i);
+ fixit = 1;
+ }
+ }
+ if (fixit) {
+ log(LOG_WARNING, 0, "fixing membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+ determine_forwvifs(gt);
+ send_prune_or_graft(gt);
+ log(LOG_WARNING, 0, "fixed membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ /*DEBUG2*/
+ /* If there are group members,
+ * and there are recent sources,
+ * and we have a route,
+ * and it's not directly connected,
+ * and we haven't sent a prune,
+ * if there are any cache entries in the kernel
+ * [if there aren't we're probably waiting to rexmit],
+ * YELL LOUDLY
+ * and send a prune
+ */
+ if (VIFM_ISEMPTY(gt->gt_grpmems) && gt->gt_srctbl && r && r->rt_gateway && gt->gt_prsent_timer == 0) {
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (st->st_ctime != 0)
+ break;
+ if (st != NULL) {
+ log(LOG_WARNING, 0, "grpmems for (%s %s) is empty but no prune state!", RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ send_prune_or_graft(gt);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ }
+ /* XXX ...Debugging */
+
/* advance the timer for the kernel entry */
- gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
+ gt->gt_timer -= TIMER_INTERVAL;
/* decrement prune timer if need be */
if (gt->gt_prsent_timer > 0) {
- gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY;
+ gt->gt_prsent_timer -= TIMER_INTERVAL;
if (gt->gt_prsent_timer <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(gt->gt_mcastgrp, s2));
gt->gt_prsent_timer = -1;
+ /* Reset the prune retransmission timer to its initial value */
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
}
}
- /* retransmit graft if graft sent flag is still set */
+ /* retransmit graft with exponential backoff */
if (gt->gt_grftsnt) {
register int y;
- CHK_GS(gt->gt_grftsnt++, y);
- if (y)
+
+ y = ++gt->gt_grftsnt;
+ while (y && !(y & 1))
+ y >>= 1;
+ if (y == 1)
send_graft(gt);
}
@@ -1718,13 +1937,24 @@ age_table_entry()
*/
ptnp = &gt->gt_pruntbl;
while ((pt = *ptnp) != NULL) {
- if ((pt->pt_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) {
+ if ((pt->pt_timer -= TIMER_INTERVAL) <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(gt->gt_mcastgrp, s2),
inet_fmt(pt->pt_router, s3),
pt->pt_vifi);
+ if (gt->gt_prsent_timer > 0) {
+ log(LOG_WARNING, 0, "prune (%s %s) from %s on vif %d expires with %d left on prsent timer",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi, gt->gt_prsent_timer);
+ /* Send a graft to heal the tree. */
+ send_graft(gt);
+ }
+ NBRM_CLR(pt->pt_index, gt->gt_prunes);
expire_prune(pt->pt_vifi, gt);
/* remove the router's prune entry and await new one */
@@ -1742,31 +1972,47 @@ age_table_entry()
* Otherwise, the cache entry's timer is refreshed.
*/
if (gt->gt_timer <= 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "(%s %s) timed out, checking for traffic",
+ RT_FMT(gt->gt_route, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
/* Check for traffic before deleting source entries */
sg_req.grp.s_addr = gt->gt_mcastgrp;
stnp = &gt->gt_srctbl;
while ((st = *stnp) != NULL) {
- sg_req.src.s_addr = st->st_origin;
- if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
- log(LOG_WARNING, errno, "%s (%s %s)",
- "age_table_entry: SIOCGETSGCNT failing for",
- inet_fmt(st->st_origin, s1),
- inet_fmt(gt->gt_mcastgrp, s2));
- /* Make sure it gets deleted below */
+ /*
+ * Source entries with no ctime are not actually in the
+ * kernel; they have been removed by rexmit_prune() so
+ * are safe to remove from the list at this point.
+ */
+ if (st->st_ctime) {
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry: SIOCGETSGCNT failing for",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ /* Make sure it gets deleted below */
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ } else {
sg_req.pktcnt = st->st_pktcnt;
}
if (sg_req.pktcnt == st->st_pktcnt) {
*stnp = st->st_next;
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "age_table_entry deleting (%s %s)",
inet_fmt(st->st_origin, s1),
inet_fmt(gt->gt_mcastgrp, s2));
- if (k_del_rg(st->st_origin, gt) < 0) {
- log(LOG_WARNING, errno,
- "age_table_entry trying to delete (%s %s)",
- inet_fmt(st->st_origin, s1),
- inet_fmt(gt->gt_mcastgrp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno,
+ "age_table_entry trying to delete (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
- kroutes--;
free(st);
} else {
st->st_pktcnt = sg_req.pktcnt;
@@ -1777,22 +2023,39 @@ age_table_entry()
/*
* Retain the group entry if we have downstream prunes or if
* there is at least one source in the list that still has
- * traffic, or if our upstream prune timer is running.
+ * traffic, or if our upstream prune timer or graft
+ * retransmission timer is running.
*/
if (gt->gt_pruntbl != NULL || gt->gt_srctbl != NULL ||
- gt->gt_prsent_timer > 0) {
+ gt->gt_prsent_timer > 0 || gt->gt_grftsnt > 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "refresh lifetim of cache entry %s%s%s%s(%s, %s)",
+ gt->gt_pruntbl ? "(dstrm prunes) " : "",
+ gt->gt_srctbl ? "(trfc flow) " : "",
+ gt->gt_prsent_timer > 0 ? "(upstrm prune) " : "",
+ gt->gt_grftsnt > 0 ? "(grft rexmit) " : "",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
- if (gt->gt_prsent_timer == -1)
- if (gt->gt_grpmems == 0)
- send_prune(gt);
- else
- gt->gt_prsent_timer = 0;
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
gtnptr = &gt->gt_gnext;
continue;
}
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(gt->gt_mcastgrp, s2));
if (gt->gt_prev)
@@ -1817,13 +2080,23 @@ age_table_entry()
rsrr_cache_send(gt,0);
rsrr_cache_clean(gt);
#endif /* RSRR */
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
free((char *)gt);
} else {
- if (gt->gt_prsent_timer == -1)
- if (gt->gt_grpmems == 0)
- send_prune(gt);
- else
- gt->gt_prsent_timer = 0;
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
gtnptr = &gt->gt_gnext;
}
}
@@ -1835,15 +2108,18 @@ age_table_entry()
gtnptr = &kernel_no_route;
while ((gt = *gtnptr) != NULL) {
/* advance the timer for the kernel entry */
- gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
+ gt->gt_timer -= TIMER_INTERVAL;
if (gt->gt_timer < 0) {
if (gt->gt_srctbl) {
- if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
- log(LOG_WARNING, errno, "%s (%s %s)",
- "age_table_entry trying to delete no-route",
- inet_fmt(gt->gt_srctbl->st_origin, s1),
- inet_fmt(gt->gt_mcastgrp, s2));
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry trying to delete no-route",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
}
free(gt->gt_srctbl);
}
@@ -1851,6 +2127,9 @@ age_table_entry()
if (gt->gt_next)
gt->gt_next->gt_prev = gt->gt_prev;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
free((char *)gt);
} else {
gtnptr = &gt->gt_next;
@@ -1871,19 +2150,24 @@ expire_prune(vifi, gt)
/*
* No need to send a graft, any prunes that we sent
* will expire before any prunes that we have received.
+ * However, in the case that we did make a mistake,
+ * send a graft to compensate.
*/
- if (gt->gt_prsent_timer > 0) {
+ if (gt->gt_prsent_timer >= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
log(LOG_DEBUG, 0, "prune expired with %d left on %s",
gt->gt_prsent_timer, "prsent_timer");
gt->gt_prsent_timer = 0;
+ send_graft(gt);
}
/* modify the kernel entry to forward packets */
if (!VIFM_ISSET(vifi, gt->gt_grpmems)) {
struct rtentry *rt = gt->gt_route;
VIFM_SET(vifi, gt->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
- inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ RT_FMT(rt, s1),
inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems, vifi);
prun_add_ttls(gt);
@@ -1895,46 +2179,6 @@ expire_prune(vifi, gt)
}
}
-
-static char *
-scaletime(t)
- u_long t;
-{
- static char buf1[5];
- static char buf2[5];
- static char *buf=buf1;
- char s;
- char *p;
-
- p = buf;
- if (buf == buf1)
- buf = buf2;
- else
- buf = buf1;
-
- if (t < 120) {
- s = 's';
- } else if (t < 3600) {
- t /= 60;
- s = 'm';
- } else if (t < 86400) {
- t /= 3600;
- s = 'h';
- } else if (t < 864000) {
- t /= 86400;
- s = 'd';
- } else {
- t /= 604800;
- s = 'w';
- }
- if (t > 999)
- return "*** ";
-
- sprintf(p,"%3d%c", (int)t, s);
-
- return p;
-}
-
/*
* Print the contents of the cache table on file 'fp2'.
*/
@@ -1947,15 +2191,19 @@ dump_cache(fp2)
register struct stable *st;
register struct ptable *pt;
register vifi_t i;
+ char c;
register time_t thyme = time(0);
fprintf(fp2,
"Multicast Routing Cache Table (%d entries)\n%s", kroutes,
- " Origin Mcast-group CTmr Age Ptmr IVif Forwvifs\n");
+ " Origin Mcast-group CTmr Age Ptmr Rx IVif Forwvifs\n");
+ fprintf(fp2,
+ "<(prunesrc:vif[idx]/tmr) prunebitmap\n%s",
+ ">Source Lifetime SavPkt Pkts Bytes RPFf\n");
for (gt = kernel_no_route; gt; gt = gt->gt_next) {
if (gt->gt_srctbl) {
- fprintf(fp2, " %-18s %-15s %-4s %-4s - -1\n",
+ fprintf(fp2, " %-18s %-15s %-8s %-8s - -1 (no route)\n",
inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff, s1),
inet_fmt(gt->gt_mcastgrp, s2), scaletime(gt->gt_timer),
scaletime(thyme - gt->gt_ctime));
@@ -1966,37 +2214,79 @@ dump_cache(fp2)
for (gt = kernel_table; gt; gt = gt->gt_gnext) {
r = gt->gt_route;
fprintf(fp2, " %-18s %-15s",
- inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ RT_FMT(r, s1),
inet_fmt(gt->gt_mcastgrp, s2));
- fprintf(fp2, " %-4s", scaletime(gt->gt_timer));
+ fprintf(fp2, " %-8s", scaletime(gt->gt_timer));
- fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime),
+ fprintf(fp2, " %-8s %-8s ", scaletime(thyme - gt->gt_ctime),
gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
- " -");
+ " -");
+
+ if (gt->gt_prune_rexmit) {
+ int i = gt->gt_prune_rexmit;
+ int n = 0;
+
+ while (i > PRUNE_REXMIT_VAL) {
+ n++;
+ i /= 2;
+ }
+ if (n == 0 && gt->gt_prsent_timer == 0)
+ fprintf(fp2, " -");
+ else
+ fprintf(fp2, "%2d", n);
+ } else {
+ fprintf(fp2, " -");
+ }
- fprintf(fp2, "%2u%c%c ", r->rt_parent,
- gt->gt_prsent_timer ? 'P' : ' ',
+ fprintf(fp2, " %2u%c%c", r->rt_parent,
+ gt->gt_prsent_timer ? 'P' :
+ gt->gt_grftsnt ? 'G' : ' ',
VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' ');
for (i = 0; i < numvifs; ++i) {
if (VIFM_ISSET(i, gt->gt_grpmems))
fprintf(fp2, " %u ", i);
else if (VIFM_ISSET(i, r->rt_children) &&
- !VIFM_ISSET(i, r->rt_leaves))
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
fprintf(fp2, " %u%c", i,
- VIFM_ISSET(i, gt->gt_scope) ? 'b' : 'p');
+ VIFM_ISSET(i, gt->gt_scope) ? 'b' :
+ SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[i].uv_nbrmap, gt->gt_prunes) ? 'p' : '!');
}
fprintf(fp2, "\n");
- for (st = gt->gt_srctbl; st; st = st->st_next) {
- fprintf(fp2, ">%s\n", inet_fmt(st->st_origin, s1));
+ if (gt->gt_pruntbl) {
+ fprintf(fp2, "<");
+ c = '(';
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) {
+ fprintf(fp2, "%c%s:%d[%d]/%d", c, inet_fmt(pt->pt_router, s1),
+ pt->pt_vifi, pt->pt_index, pt->pt_timer);
+ c = ',';
+ }
+ fprintf(fp2, ")");
+ fprintf(fp2, " 0x%08lx%08lx\n",/*XXX*/
+ gt->gt_prunes.hi, gt->gt_prunes.lo);
}
-#ifdef DEBUG_PRUNES
- for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) {
- fprintf(fp2, "<r:%s v:%d t:%d\n", inet_fmt(pt->pt_router, s1),
- pt->pt_vifi, pt->pt_timer);
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ fprintf(fp2, ">%-18s %-8s %6ld", inet_fmt(st->st_origin, s1),
+ st->st_ctime ? scaletime(thyme - st->st_ctime) : "-",
+ st->st_savpkt);
+ if (st->st_ctime) {
+ struct sioc_sg_req sg_req;
+
+ sg_req.src.s_addr = st->st_origin;
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ } else {
+ fprintf(fp2, " %8ld %8ld %4ld", sg_req.pktcnt,
+ sg_req.bytecnt, sg_req.wrong_if);
+ }
+ }
+ fprintf(fp2, "\n");
}
-#endif
}
}
@@ -2038,14 +2328,17 @@ accept_mtrace(src, dst, group, data, no, datalen)
*/
if (datalen == QLEN) {
type = QUERY;
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
else if ((datalen - QLEN) % RLEN == 0) {
type = RESP;
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
if (IN_MULTICAST(ntohl(dst))) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Dropping multicast response");
return;
}
@@ -2063,23 +2356,27 @@ accept_mtrace(src, dst, group, data, no, datalen)
* if it is a packet with all reports filled, drop it
*/
if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "packet with all reports filled in");
return;
}
+ IF_DEBUG(DEBUG_TRACE) {
log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1),
inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3));
log(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl,
inet_fmt(qry->tr_raddr, s1));
log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
+ }
/* determine the routing table entry for this traceroute */
rt = determine_route(qry->tr_src);
+ IF_DEBUG(DEBUG_TRACE)
if (rt) {
log(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d",
rt->rt_parent, inet_fmt(rt->rt_gateway, s1), rt->rt_metric);
log(LOG_DEBUG, 0, "rt origin %s",
- inet_fmts(rt->rt_origin, rt->rt_originmask, s1));
+ RT_FMT(rt, s1));
} else
log(LOG_DEBUG, 0, "...no route");
@@ -2100,11 +2397,13 @@ accept_mtrace(src, dst, group, data, no, datalen)
* only get N copies, N <= the number of interfaces on the router,
* it is not fatal.
*/
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
return;
}
if (rt == NULL) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s",
inet_fmt(qry->tr_src, s1));
if (IN_MULTICAST(ntohl(dst)))
@@ -2114,12 +2413,14 @@ accept_mtrace(src, dst, group, data, no, datalen)
if (vifi == NO_VIF) {
/* The traceroute destination is not on one of my subnet vifs. */
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Destination %s not an interface",
inet_fmt(qry->tr_dst, s1));
if (IN_MULTICAST(ntohl(dst)))
return;
errcode = TR_WRONG_IF;
} else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
if (IN_MULTICAST(ntohl(dst)))
@@ -2134,6 +2435,7 @@ accept_mtrace(src, dst, group, data, no, datalen)
* a tunnel or came from a directly attached mrouter.
*/
if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Wrong interface for packet");
errcode = TR_WRONG_IF;
}
@@ -2142,6 +2444,7 @@ accept_mtrace(src, dst, group, data, no, datalen)
/* Now that we've decided to send a response, save the qid */
oqid = qry->tr_qid;
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Sending traceroute response");
/* copy the packet to the sending buffer */
@@ -2169,25 +2472,22 @@ accept_mtrace(src, dst, group, data, no, datalen)
bzero(resp, sizeof(struct tr_resp));
datalen += RLEN;
- resp->tr_qarr = htonl((tp.tv_sec + JAN_1970) << 16) +
- ((tp.tv_usec >> 4) & 0xffff);
+ resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec << 10) / 15625));
resp->tr_rproto = PROTO_DVMRP;
- if (errcode != TR_NO_ERR) {
- resp->tr_rflags = errcode;
- rt = NULL; /* hack to enforce send straight to requestor */
- goto sendit;
- }
- resp->tr_outaddr = uvifs[vifi].uv_lcl_addr;
- resp->tr_fttl = uvifs[vifi].uv_threshold;
- resp->tr_rflags = TR_NO_ERR;
+ resp->tr_outaddr = (vifi == NO_VIF) ? dst : uvifs[vifi].uv_lcl_addr;
+ resp->tr_fttl = (vifi == NO_VIF) ? 0 : uvifs[vifi].uv_threshold;
+ resp->tr_rflags = errcode;
/*
* obtain # of packets out on interface
*/
v_req.vifi = vifi;
- if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ if (vifi != NO_VIF && ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
resp->tr_vifout = htonl(v_req.ocount);
+ else
+ resp->tr_vifout = 0xffffffff;
/*
* fill in scoping & pruning information
@@ -2201,23 +2501,34 @@ accept_mtrace(src, dst, group, data, no, datalen)
gt = NULL;
if (gt && gt->gt_mcastgrp == group) {
+ struct stable *st;
+
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (qry->tr_src == st->st_origin)
+ break;
+
sg_req.src.s_addr = qry->tr_src;
sg_req.grp.s_addr = group;
- if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
- resp->tr_pktcnt = htonl(sg_req.pktcnt);
+ if (st && st->st_ctime != 0 &&
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
+ else
+ resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
if (VIFM_ISSET(vifi, gt->gt_scope))
resp->tr_rflags = TR_SCOPED;
else if (gt->gt_prsent_timer)
resp->tr_rflags = TR_PRUNED;
else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
- if (VIFM_ISSET(vifi, rt->rt_children) &&
- !VIFM_ISSET(vifi, rt->rt_leaves))
+ if (!NBRM_ISEMPTY(uvifs[vifi].uv_nbrmap) &&
+ SUBS_ARE_PRUNED(rt->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, gt->gt_prunes))
resp->tr_rflags = TR_OPRUNED;
else
resp->tr_rflags = TR_NO_FWD;
} else {
- if (scoped_addr(vifi, group))
+ if ((vifi != NO_VIF && scoped_addr(vifi, group)) ||
+ (rt && scoped_addr(rt->rt_parent, group)))
resp->tr_rflags = TR_SCOPED;
else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
resp->tr_rflags = TR_NO_FWD;
@@ -2236,12 +2547,15 @@ accept_mtrace(src, dst, group, data, no, datalen)
v_req.vifi = rt->rt_parent;
if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
resp->tr_vifin = htonl(v_req.icount);
+ else
+ resp->tr_vifin = 0xffffffff;
MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
src = uvifs[rt->rt_parent].uv_lcl_addr;
resp->tr_inaddr = src;
resp->tr_rmtaddr = rt->rt_gateway;
if (!VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
resp->tr_rflags = TR_WRONG_IF;
@@ -2259,6 +2573,7 @@ sendit:
* else send to upstream router. If the upstream router can't handle
* mtrace, set an error code and send to requestor anyway.
*/
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) {
@@ -2280,6 +2595,7 @@ sendit:
* If we don't have one, we can't source any multicasts anyway.
*/
if (phys_vif != -1) {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Sending reply to %s from %s",
inet_fmt(dst, s1), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2));
k_set_ttl(qry->tr_rttl);
@@ -2291,6 +2607,7 @@ sendit:
log(LOG_INFO, 0, "No enabled phyints -- %s",
"dropping traceroute reply");
} else {
+ IF_DEBUG(DEBUG_TRACE)
log(LOG_DEBUG, 0, "Sending %s to %s from %s",
resptype == IGMP_MTRACE_RESP ? "reply" : "request on",
inet_fmt(dst, s1), inet_fmt(src, s2));
diff --git a/usr.sbin/mrouted/prune.h b/usr.sbin/mrouted/prune.h
index 57ae067ce74f..11740cf94c21 100644
--- a/usr.sbin/mrouted/prune.h
+++ b/usr.sbin/mrouted/prune.h
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: prune.h,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * prune.h,v 3.8.4.5 1998/02/27 22:45:43 fenner Exp
*/
/*
@@ -32,11 +32,14 @@ struct gtable {
vifbitmap_t gt_grpmems; /* forw. vifs for src, grp */
int gt_prsent_timer; /* prune timer for this group */
int gt_timer; /* timer for this group entry */
- time_t gt_ctime; /* time of entry creation */
+ time_t gt_ctime; /* time of entry creation */
u_char gt_grftsnt; /* graft sent/retransmit timer */
+ nbrbitmap_t gt_prunes; /* bitmap of neighbors who pruned */
struct stable *gt_srctbl; /* source table */
struct ptable *gt_pruntbl; /* prune table */
struct rtentry *gt_route; /* parent route */
+ int gt_rexmit_timer; /* timer for prune retransmission */
+ int gt_prune_rexmit; /* time til prune retransmission */
#ifdef RSRR
struct rsrr_cache *gt_rsrr_cache; /* RSRR cache */
#endif /* RSRR */
@@ -52,6 +55,8 @@ struct stable
struct stable *st_next; /* pointer to the next entry */
u_int32 st_origin; /* host origin of multicasts */
u_long st_pktcnt; /* packet count for src-grp entry */
+ u_long st_savpkt; /* saved pkt cnt when no krnl entry */
+ time_t st_ctime; /* kernel entry creation time */
};
/*
@@ -62,9 +67,12 @@ struct ptable
struct ptable *pt_next; /* pointer to the next entry */
u_int32 pt_router; /* router that sent this prune */
vifi_t pt_vifi; /* vif prune received on */
+ int pt_index; /* neighbor index of router */
int pt_timer; /* timer for prune */
};
+#define MIN_PRUNE_LIFE TIMER_INTERVAL /* min prune lifetime to bother with */
+
/*
* The packet format for a traceroute request.
*/
@@ -137,7 +145,7 @@ struct tr_resp {
};
#define VAL_TO_MASK(x, i) { \
- x = htonl(~((1 << (32 - (i))) - 1)); \
+ x = i ? htonl(~((1 << (32 - (i))) - 1)) : 0; \
};
#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv)
diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c
index c2b6b9e8b932..e43dac68f104 100644
--- a/usr.sbin/mrouted/route.c
+++ b/usr.sbin/mrouted/route.c
@@ -7,12 +7,16 @@
* Leland Stanford Junior University.
*
*
- * $Id: route.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp
*/
#include "defs.h"
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp $";
+#endif
/*
* This define statement saves a lot of space later
@@ -42,13 +46,89 @@ unsigned int nroutes; /* current number of route entries */
* Private functions.
*/
static int init_children_and_leaves __P((struct rtentry *r,
- vifi_t parent));
+ vifi_t parent, int first));
static int find_route __P((u_int32 origin, u_int32 mask));
static void create_route __P((u_int32 origin, u_int32 mask));
static void discard_route __P((struct rtentry *prev_r));
static int compare_rts __P((const void *rt1, const void *rt2));
-static int report_chunk __P((struct rtentry *start_rt, vifi_t vifi,
+static int report_chunk __P((int, struct rtentry *start_rt, vifi_t vifi,
u_int32 dst));
+static void queue_blaster_report __P((vifi_t, u_int32, u_int32, char *,
+ int, u_int32));
+static void process_blaster_report __P((void *));
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Return pointer to a specific route entry. This must be a separate
+ * function from find_route() which modifies rtp.
+ */
+struct rtentry *
+snmp_find_route(src, mask)
+ register u_int32 src, mask;
+{
+ register struct rtentry *rt;
+
+ for (rt = routing_table; rt; rt = rt->rt_next) {
+ if (src == rt->rt_origin && mask == rt->rt_originmask)
+ return rt;
+ }
+ return NULL;
+}
+
+/*
+ * Find next route entry > specification
+ */
+int
+next_route(rtpp, src, mask)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+{
+ struct rtentry *rt, *rbest = NULL;
+
+ /* Among all entries > spec, find "lowest" one in order */
+ for (rt = routing_table; rt; rt=rt->rt_next) {
+ if ((ntohl(rt->rt_origin) > ntohl(src)
+ || (ntohl(rt->rt_origin) == ntohl(src)
+ && ntohl(rt->rt_originmask) > ntohl(mask)))
+ && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
+ || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
+ && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
+ rbest = rt;
+ }
+ (*rtpp) = rbest;
+ return (*rtpp)!=0;
+}
+
+/*
+ * Given a routing table entry, and a vifi, find the next vifi/entry
+ */
+int
+next_route_child(rtpp, src, mask, vifi)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ /* Get (S,M) entry */
+ if (!((*rtpp) = snmp_find_route(src,mask)))
+ if (!next_route(rtpp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
+ return 1;
+ *vifi = 0;
+ } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
+
+ return 0;
+}
+#endif
/*
* Initialize the routing table and associated variables.
@@ -65,54 +145,50 @@ init_routes()
/*
- * Initialize the children and leaf bits for route 'r', along with the
- * associated dominant, subordinate, and leaf timing data structures.
- * Return TRUE if this changes the value of either the children or
- * leaf bitmaps for 'r'.
+ * Initialize the children bits for route 'r', along with the
+ * associated dominant and subordinate data structures.
+ * If first is set, initialize dominants, otherwise keep old
+ * dominants on non-parent interfaces.
+ * XXX Does this need a return value?
*/
static int
-init_children_and_leaves(r, parent)
+init_children_and_leaves(r, parent, first)
register struct rtentry *r;
register vifi_t parent;
+ int first;
{
register vifi_t vifi;
register struct uvif *v;
- vifbitmap_t old_children, old_leaves;
+ vifbitmap_t old_children;
+ nbrbitmap_t old_subords;
VIFM_COPY(r->rt_children, old_children);
- VIFM_COPY(r->rt_leaves, old_leaves );
+ NBRM_COPY(r->rt_subordinates, old_subords);
VIFM_CLRALL(r->rt_children);
- VIFM_CLRALL(r->rt_leaves);
- r->rt_flags &= ~RTF_LEAF_TIMING;
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
- r->rt_dominants [vifi] = 0;
- r->rt_subordinates[vifi] = 0;
-
- if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ if (first || vifi == parent)
+ r->rt_dominants [vifi] = 0;
+ if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD ||
+ AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi]))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+
+ if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) &&
+ !(!first && r->rt_dominants[vifi])) {
VIFM_SET(vifi, r->rt_children);
- if (v->uv_neighbors == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = 0;
- }
- else {
- r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
- }
- }
- else {
- r->rt_leaf_timers[vifi] = 0;
}
}
return (!VIFM_SAME(r->rt_children, old_children) ||
- !VIFM_SAME(r->rt_leaves, old_leaves));
+ !NBRM_SAME(r->rt_subordinates, old_subords));
}
/*
- * A new vif has come up -- update the children and leaf bitmaps in all route
+ * A new vif has come up -- update the children bitmaps in all route
* entries to take that into account.
*/
void
@@ -128,17 +204,9 @@ add_vif_to_routes(vifi)
!VIFM_ISSET(vifi, r->rt_children)) {
VIFM_SET(vifi, r->rt_children);
r->rt_dominants [vifi] = 0;
- r->rt_subordinates[vifi] = 0;
- if (v->uv_neighbors == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = 0;
- }
- else {
- VIFM_CLR(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
- }
- update_table_entry(r);
+ /*XXX isn't uv_nbrmap going to be empty?*/
+ NBRM_CLRMASK(r->rt_subordinates, v->uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
}
}
}
@@ -166,10 +234,8 @@ delete_vif_from_routes(vifi)
}
else if (VIFM_ISSET(vifi, r->rt_children)) {
VIFM_CLR(vifi, r->rt_children);
- VIFM_CLR(vifi, r->rt_leaves);
- r->rt_subordinates[vifi] = 0;
- r->rt_leaf_timers [vifi] = 0;
- update_table_entry(r);
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
}
else {
r->rt_dominants[vifi] = 0;
@@ -180,14 +246,42 @@ delete_vif_from_routes(vifi)
/*
+ * A new neighbor has come up. If we're flooding on the neighbor's
+ * vif, mark that neighbor as subordinate for all routes whose parent
+ * is not this vif.
+ */
+void
+add_neighbor_to_routes(vifi, index)
+ register vifi_t vifi;
+ register int index;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_flags & VIFF_NOFLOOD)
+ return;
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi &&
+ !AVOID_TRANSIT(vifi, r)) {
+ NBRM_SET(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+}
+
+
+/*
* A neighbor has failed or become unreachable. If that neighbor was
* considered a dominant or subordinate router in any route entries,
- * take appropriate action.
+ * take appropriate action. Expire all routes this neighbor advertised
+ * to us.
*/
void
-delete_neighbor_from_routes(addr, vifi)
+delete_neighbor_from_routes(addr, vifi, index)
register u_int32 addr;
register vifi_t vifi;
+ int index;
{
register struct rtentry *r;
register struct uvif *v;
@@ -195,37 +289,24 @@ delete_neighbor_from_routes(addr, vifi)
v = &uvifs[vifi];
for (r = routing_table; r != NULL; r = r->rt_next) {
if (r->rt_metric != UNREACHABLE) {
- if (r->rt_dominants[vifi] == addr) {
+ if (r->rt_parent == vifi && r->rt_gateway == addr) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ } else if (r->rt_dominants[vifi] == addr) {
VIFM_SET(vifi, r->rt_children);
- r->rt_dominants [vifi] = 0;
- r->rt_subordinates[vifi] = 0;
- if (v->uv_neighbors == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = 0;
- }
- else {
- VIFM_CLR(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
- }
- update_table_entry(r);
- }
- else if (r->rt_subordinates[vifi] == addr) {
- r->rt_subordinates[vifi] = 0;
- if (v->uv_neighbors == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- update_table_entry(r);
- }
- else {
- r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
- }
- }
- else if (v->uv_neighbors == NULL &&
- r->rt_leaf_timers[vifi] != 0) {
- VIFM_SET(vifi, r->rt_leaves);
- r->rt_leaf_timers[vifi] = 0;
- update_table_entry(r);
+ r->rt_dominants[vifi] = 0;
+ if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) ||
+ AVOID_TRANSIT(vifi, r))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ } else if (NBRM_ISSET(index, r->rt_subordinates)) {
+ NBRM_CLR(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
}
}
}
@@ -296,8 +377,7 @@ create_route(origin, mask)
register struct rtentry *r;
if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) +
- (2 * numvifs * sizeof(u_int32)) +
- (numvifs * sizeof(u_int)))) == NULL) {
+ (numvifs * sizeof(u_int32)))) == NULL) {
log(LOG_ERR, 0, "ran out of memory"); /* fatal */
}
r->rt_origin = origin;
@@ -308,9 +388,11 @@ create_route(origin, mask)
else r->rt_originwidth = 1;
r->rt_flags = 0;
r->rt_dominants = (u_int32 *)(r + 1);
- r->rt_subordinates = (u_int32 *)(r->rt_dominants + numvifs);
- r->rt_leaf_timers = (u_int *)(r->rt_subordinates + numvifs);
+ bzero(r->rt_dominants, numvifs * sizeof(u_int32));
r->rt_groups = NULL;
+ VIFM_CLRALL(r->rt_children);
+ NBRM_CLRALL(r->rt_subordinates);
+ NBRM_CLRALL(r->rt_subordadv);
r->rt_next = rtp->rt_next;
rtp->rt_next = r;
@@ -334,6 +416,8 @@ discard_route(prev_r)
register struct rtentry *r;
r = prev_r->rt_next;
+ uvifs[r->rt_parent].uv_nroutes--;
+ /*???nbr???.al_nroutes--;*/
prev_r->rt_next = r->rt_next;
if (prev_r->rt_next != NULL)
(prev_r->rt_next)->rt_prev = prev_r;
@@ -351,11 +435,12 @@ discard_route(prev_r)
* to indicate a change of status of one of our own interfaces.
*/
void
-update_route(origin, mask, metric, src, vifi)
+update_route(origin, mask, metric, src, vifi, n)
u_int32 origin, mask;
u_int metric;
u_int32 src;
vifi_t vifi;
+ struct listaddr *n;
{
register struct rtentry *r;
u_int adj_metric;
@@ -393,18 +478,17 @@ update_route(origin, mask, metric, src, vifi)
return;
}
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises new route %s",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2));
+
/*
* OK, create the new routing entry. 'rtp' will be left pointing
* to the new entry.
*/
create_route(origin, mask);
-
- /*
- * Now "steal away" any sources that belong under this route
- * by deleting any cache entries they might have created
- * and allowing the kernel to re-request them.
- */
- steal_sources(rtp);
+ uvifs[vifi].uv_nroutes++;
+ /*n->al_nroutes++;*/
rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
}
@@ -422,15 +506,32 @@ update_route(origin, mask, metric, src, vifi)
if (adj_metric == UNREACHABLE)
return;
- r->rt_parent = vifi;
- init_children_and_leaves(r, vifi);
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+ /*
+ * Now "steal away" any sources that belong under this route
+ * by deleting any cache entries they might have created
+ * and allowing the kernel to re-request them.
+ *
+ * If we haven't performed final initialization yet and are
+ * just collecting the routing table, we can't have any
+ * sources so we don't perform this step.
+ */
+ if (did_final_init)
+ steal_sources(rtp);
+
+ r->rt_parent = vifi;
r->rt_gateway = src;
+ init_children_and_leaves(r, vifi, 1);
+
r->rt_timer = 0;
r->rt_metric = adj_metric;
r->rt_flags |= RTF_CHANGED;
routes_changed = TRUE;
- update_table_entry(r);
+ update_table_entry(r, r->rt_gateway);
}
else if (src == r->rt_gateway) {
/*
@@ -442,6 +543,12 @@ update_route(origin, mask, metric, src, vifi)
* our entry accordingly.
*/
r->rt_timer = 0;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+
if (adj_metric == r->rt_metric)
return;
@@ -449,11 +556,6 @@ update_route(origin, mask, metric, src, vifi)
del_table_entry(r, 0, DEL_ALL_ROUTES);
r->rt_timer = ROUTE_EXPIRE_TIME;
}
- else if (adj_metric < r->rt_metric) {
- if (init_children_and_leaves(r, vifi)) {
- update_table_entry(r);
- }
- }
r->rt_metric = adj_metric;
r->rt_flags |= RTF_CHANGED;
routes_changed = TRUE;
@@ -479,16 +581,29 @@ update_route(origin, mask, metric, src, vifi)
* IP address than the gateway associated with the route entry.
* Did you get all that?
*/
- if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
- /*
- * XXX Why do we do this if we are just changing the metric?
- */
- r->rt_parent = vifi;
- if (init_children_and_leaves(r, vifi)) {
- update_table_entry(r);
- }
+ u_int32 old_gateway;
+ vifi_t old_parent;
+ old_gateway = r->rt_gateway;
+ old_parent = r->rt_parent;
+ r->rt_gateway = src;
+ r->rt_parent = vifi;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ adj_metric, inet_fmt(old_gateway, s3), old_parent,
+ r->rt_metric);
+
+ if (old_parent != vifi) {
+ init_children_and_leaves(r, vifi, 0);
+ uvifs[old_parent].uv_nroutes--;
+ uvifs[vifi].uv_nroutes++;
+ }
+ if (old_gateway != src) {
+ update_table_entry(r, old_gateway);
+ /*???old_gateway???->al_nroutes--;*/
+ /*n->al_nroutes++;*/
}
- r->rt_gateway = src;
r->rt_timer = 0;
r->rt_metric = adj_metric;
r->rt_flags |= RTF_CHANGED;
@@ -497,9 +612,18 @@ update_route(origin, mask, metric, src, vifi)
else if (vifi != r->rt_parent) {
/*
* The report came from a vif other than the route's parent vif.
- * Update the children and leaf info, if necessary.
+ * Update the children info, if necessary.
*/
- if (VIFM_ISSET(vifi, r->rt_children)) {
+ if (AVOID_TRANSIT(vifi, r)) {
+ /*
+ * The route's parent is a vif from which we're not supposed
+ * to transit onto this vif. Simply ignore the update.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ } else if (VIFM_ISSET(vifi, r->rt_children)) {
/*
* Vif is a child vif for this route.
*/
@@ -512,42 +636,51 @@ update_route(origin, mask, metric, src, vifi)
* and vif is no longer a child for me.
*/
VIFM_CLR(vifi, r->rt_children);
- VIFM_CLR(vifi, r->rt_leaves);
r->rt_dominants [vifi] = src;
- r->rt_subordinates[vifi] = 0;
- r->rt_leaf_timers [vifi] = 0;
- update_table_entry(r);
+ /* XXX
+ * We don't necessarily want to forget about subordinateness
+ * so that we can become the dominant quickly if the current
+ * dominant fails.
+ */
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
}
else if (metric > UNREACHABLE) { /* "poisoned reverse" */
/*
* Neighbor considers this vif to be on path to route's
- * origin; if no subordinate recorded, record this neighbor
- * as subordinate and clear the leaf flag.
+ * origin; record this neighbor as subordinate
*/
- if (r->rt_subordinates[vifi] == 0) {
- VIFM_CLR(vifi, r->rt_leaves);
- r->rt_subordinates[vifi] = src;
- r->rt_leaf_timers [vifi] = 0;
- update_table_entry(r);
+ if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
}
+ NBRM_SET(n->al_index, r->rt_subordadv);
}
- else if (src == r->rt_subordinates[vifi]) {
+ else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
/*
* Current subordinate no longer considers this vif to be on
* path to route's origin; it is no longer a subordinate
- * router, and we set the leaf confirmation timer to give
- * us time to hear from other subordinates.
+ * router.
*/
- r->rt_subordinates[vifi] = 0;
- if (uvifs[vifi].uv_neighbors == NULL ||
- uvifs[vifi].uv_neighbors->al_next == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- update_table_entry(r);
- }
- else {
- r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
- }
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ NBRM_CLR(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
}
}
@@ -560,20 +693,26 @@ update_route(origin, mask, metric, src, vifi)
* (or same metric and lower IP address); we adopt the vif
* as our own child.
*/
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
VIFM_SET(vifi, r->rt_children);
- r->rt_dominants [vifi] = 0;
+ r->rt_dominants[vifi] = 0;
+ if (uvifs[vifi].uv_flags & VIFF_NOFLOOD)
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
if (metric > UNREACHABLE) {
- r->rt_subordinates[vifi] = src;
- }
- else if (uvifs[vifi].uv_neighbors == NULL ||
- uvifs[vifi].uv_neighbors->al_next == NULL) {
- VIFM_SET(vifi, r->rt_leaves);
- }
- else {
- r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
- r->rt_flags |= RTF_LEAF_TIMING;
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ NBRM_SET(n->al_index, r->rt_subordadv);
}
- update_table_entry(r);
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
}
}
}
@@ -587,47 +726,13 @@ age_routes()
{
register struct rtentry *r;
register struct rtentry *prev_r;
- register vifi_t vifi;
+ extern u_long virtual_time; /* from main.c */
for (prev_r = RT_ADDR, r = routing_table;
r != NULL;
prev_r = r, r = r->rt_next) {
- if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
- /*
- * Route is still good; see if any leaf timers need to be
- * advanced.
- */
- if (r->rt_flags & RTF_LEAF_TIMING) {
- r->rt_flags &= ~RTF_LEAF_TIMING;
- for (vifi = 0; vifi < numvifs; ++vifi) {
- if (r->rt_leaf_timers[vifi] != 0) {
- /*
- * Unlike other timers, leaf timers decrement.
- */
- if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
-#ifdef NOTYET
- /* If the vif is a physical leaf but has neighbors,
- * it is not a tree leaf. If I am a leaf, then no
- * interface with neighbors is a tree leaf. */
- if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) ||
- (vifs_with_neighbors == 1)) &&
- (uvifs[vifi].uv_neighbors != NULL))) {
-#endif
- VIFM_SET(vifi, r->rt_leaves);
- update_table_entry(r);
-#ifdef NOTYET
- }
-#endif
- }
- else {
- r->rt_flags |= RTF_LEAF_TIMING;
- }
- }
- }
- }
- }
- else if (r->rt_timer >= ROUTE_DISCARD_TIME) {
+ if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) {
/*
* Time to garbage-collect the route entry.
*/
@@ -635,7 +740,8 @@ age_routes()
discard_route(prev_r);
r = prev_r;
}
- else if (r->rt_metric != UNREACHABLE) {
+ else if (r->rt_timer >= ROUTE_EXPIRE_TIME &&
+ r->rt_metric != UNREACHABLE) {
/*
* Time to expire the route entry. If the gateway is zero,
* i.e., it is a route to a directly-connected subnet, just
@@ -652,6 +758,22 @@ age_routes()
routes_changed = TRUE;
}
}
+ else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) {
+ /*
+ * Time out subordinateness that hasn't been reported in
+ * the last 2 intervals.
+ */
+ if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d",
+ RT_FMT(r, s1),
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric);
+ NBRM_MASK(r->rt_subordinates, r->rt_subordadv);
+ update_table_entry(r, r->rt_gateway);
+ }
+ NBRM_CLRALL(r->rt_subordadv);
+ }
}
}
@@ -704,10 +826,41 @@ accept_probe(src, dst, p, datalen, level)
u_int32 level;
{
vifi_t vifi;
+ static struct listaddr *unknowns = NULL;
if ((vifi = find_vif(src, dst)) == NO_VIF) {
- log(LOG_INFO, 0,
- "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
+ struct listaddr *a, **prev;
+ struct listaddr *match = NULL;
+ time_t now = time(0);
+
+ for (prev = &unknowns, a = *prev; a; a = *prev) {
+ if (a->al_addr == src)
+ match = a;
+ if (a->al_ctime + 2 * a->al_timer < now) {
+ /* We haven't heard from it in a long time */
+ *prev = a->al_next;
+ free(a);
+ } else {
+ prev = &a->al_next;
+ }
+ }
+ if (match == NULL) {
+ match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr));
+ match->al_next = NULL;
+ match->al_addr = src;
+ match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME;
+ match->al_ctime = now - match->al_timer;
+ }
+
+ if (match->al_ctime + match->al_timer <= now) {
+ log(LOG_WARNING, 0,
+ "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
+ inet_fmt(src, s1), s1);
+ match->al_timer *= 2;
+ } else
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "ignoring probe from non-neighbor %s (%d seconds until next warning)", inet_fmt(src, s1), match->al_ctime + match->al_timer - now);
return;
}
@@ -747,8 +900,137 @@ compare_rts(rt1, rt2)
return (0);
}
+void
+blaster_alloc(vifi)
+ vifi_t vifi;
+{
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterbuf)
+ free(v->uv_blasterbuf);
+
+ v->uv_blasterlen = 64*1024;
+ v->uv_blasterbuf = malloc(v->uv_blasterlen);
+ v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf;
+ if (v->uv_blastertimer)
+ timer_clearTimer(v->uv_blastertimer);
+ v->uv_blastertimer = 0;
+}
+
+struct blaster_hdr {
+ u_int32 bh_src;
+ u_int32 bh_dst;
+ u_int32 bh_level;
+ int bh_datalen;
+};
+
+/*
+ * Queue a route report from a route-blaster.
+ * If the timer isn't running to process these reports,
+ * start it.
+ */
+static void
+queue_blaster_report(vifi, src, dst, p, datalen, level)
+ vifi_t vifi;
+ u_int32 src, dst, level;
+ register char *p;
+ register int datalen;
+{
+ register struct blaster_hdr *bh;
+ register struct uvif *v;
+ int bblen = sizeof(*bh) + ((datalen + 3) & ~3);
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterend - v->uv_blasterbuf +
+ bblen > v->uv_blasterlen) {
+ int end = v->uv_blasterend - v->uv_blasterbuf;
+ int cur = v->uv_blastercur - v->uv_blasterbuf;
+
+ v->uv_blasterlen *= 2;
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG, 0, "increasing blasterbuf to %d bytes",
+ v->uv_blasterlen);
+ v->uv_blasterbuf = realloc(v->uv_blasterbuf,
+ v->uv_blasterlen);
+ if (v->uv_blasterbuf == NULL) {
+ log(LOG_WARNING, ENOMEM, "turning off blaster on vif %d", vifi);
+ v->uv_blasterlen = 0;
+ v->uv_blasterend = v->uv_blastercur = NULL;
+ v->uv_flags &= ~VIFF_BLASTER;
+ return;
+ }
+ v->uv_blasterend = v->uv_blasterbuf + end;
+ v->uv_blastercur = v->uv_blasterbuf + cur;
+ }
+ bh = (struct blaster_hdr *)v->uv_blasterend;
+ bh->bh_src = src;
+ bh->bh_dst = dst;
+ bh->bh_level = level;
+ bh->bh_datalen = datalen;
+ bcopy(p, (char *)(bh + 1), datalen);
+ v->uv_blasterend += bblen;
+
+ if (v->uv_blastertimer == 0) {
+ int *i = (int *)malloc(sizeof(int *));
+
+ if (i == NULL)
+ log(LOG_ERR, 0, "out of memory");
+
+ *i = vifi;
+
+ v->uv_blastertimer = timer_setTimer(5,
+ process_blaster_report, i);
+ }
+}
+
+/*
+ * Periodic process; process up to 5 of the routes in the route-blaster
+ * queue. If there are more routes remaining, reschedule myself to run
+ * in 1 second.
+ */
+static void
+process_blaster_report(vifip)
+ void *vifip;
+{
+ vifi_t vifi = *(int *)vifip;
+ register struct uvif *v;
+ register struct blaster_hdr *bh;
+ int i;
+
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "processing vif %d blasted routes", vifi);
+ v = &uvifs[vifi];
+ for (i = 0; i < 5; i++) {
+ if (v->uv_blastercur >= v->uv_blasterend)
+ break;
+ bh = (struct blaster_hdr *)v->uv_blastercur;
+ v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3);
+ accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1),
+ -bh->bh_datalen, bh->bh_level);
+ }
+
+ if (v->uv_blastercur >= v->uv_blasterend) {
+ v->uv_blastercur = v->uv_blasterbuf;
+ v->uv_blasterend = v->uv_blasterbuf;
+ v->uv_blastertimer = 0;
+ free(vifip);
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "finish processing vif %d blaster", vifi);
+ } else {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "more blasted routes to come on vif %d", vifi);
+ v->uv_blastertimer = timer_setTimer(1,
+ process_blaster_report, vifip);
+ }
+}
+
/*
* Process an incoming route report message.
+ * If the report arrived on a vif marked as a "blaster", then just
+ * queue it and return; queue_blaster_report() will schedule it for
+ * processing later. If datalen is negative, then this is actually
+ * a queued report so actually process it instead of queueing it.
*/
void
accept_report(src, dst, p, datalen, level)
@@ -762,6 +1044,7 @@ accept_report(src, dst, p, datalen, level)
u_int32 mask;
u_int32 origin;
struct newrt rt[4096];
+ struct listaddr *nbr;
if ((vifi = find_vif(src, dst)) == NO_VIF) {
log(LOG_INFO, 0,
@@ -769,7 +1052,15 @@ accept_report(src, dst, p, datalen, level)
return;
}
- if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level))
+ if (uvifs[vifi].uv_flags & VIFF_BLASTER)
+ if (datalen > 0) {
+ queue_blaster_report(vifi, src, dst, p, datalen, level);
+ return;
+ } else {
+ datalen = -datalen;
+ }
+
+ if (!(nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)))
return;
if (datalen > 2*4096) {
@@ -826,6 +1117,7 @@ accept_report(src, dst, p, datalen, level)
if (rt[nrt-1].origin == 0)
rt[nrt-1].mask = 0;
+ IF_DEBUG(DEBUG_ROUTE)
log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
inet_fmt(src, s1), inet_fmt(dst, s2));
for (i = 0; i < nrt; ++i) {
@@ -835,8 +1127,43 @@ accept_report(src, dst, p, datalen, level)
inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
continue;
}
+ /* Only filter non-poisoned updates. */
+ if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == rt[i].origin) &&
+ (vfe->vfe_mask == rt[i].mask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s",
+ inet_fmts(rt[i].origin, rt[i].mask, s1),
+ vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+#if 0
+ rt[i].metric += vfe->vfe_addmetric;
+ if (rt[i].metric > UNREACHABLE)
+#endif
+ rt[i].metric = UNREACHABLE;
+ }
+ }
update_route(rt[i].origin, rt[i].mask, rt[i].metric,
- src, vifi);
+ src, vifi, nbr);
}
if (routes_changed && !delay_change_reports)
@@ -855,80 +1182,13 @@ report(which_routes, vifi, dst)
u_int32 dst;
{
register struct rtentry *r;
- register char *p;
register int i;
- int datalen = 0;
- int width = 0;
- u_int32 mask = 0;
- u_int32 src;
- u_int32 nflags;
-
- src = uvifs[vifi].uv_lcl_addr;
-
- p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
-#ifdef NOTYET
- /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */
- if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) {
- *p++ = 0; /* 0xff000000 mask */
- *p++ = 0;
- *p++ = 0;
- *p++ = 0; /* class A net 0.0.0.0 == default */
- *p++ = 0x81; /*XXX metric 1, is this safe? */
- datalen += 5;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
- htonl(MROUTED_LEVEL), datalen);
- return;
- }
-#endif
-
- nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
-
- for (r = rt_end; r != RT_ADDR; r = r->rt_prev) {
-
- if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
- continue;
-
- /*
- * If there is no room for this route in the current message,
- * send the message and start a new one.
- */
- if (datalen + ((r->rt_originmask == mask) ?
- (width + 1) :
- (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
- *(p-1) |= 0x80;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
- htonl(MROUTED_LEVEL | nflags), datalen);
-
- p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
- datalen = 0;
- mask = 0;
- }
-
- if (r->rt_originmask != mask || datalen == 0) {
- mask = r->rt_originmask;
- width = r->rt_originwidth;
- if (datalen != 0) *(p-1) |= 0x80;
- *p++ = ((char *)&mask)[1];
- *p++ = ((char *)&mask)[2];
- *p++ = ((char *)&mask)[3];
- datalen += 3;
- }
-
- for (i = 0; i < width; ++i)
- *p++ = ((char *)&(r->rt_origin))[i];
-
- *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
- (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
- (char)(r->rt_metric);
-
- datalen += width + 1;
- }
-
- if (datalen != 0) {
- *(p-1) |= 0x80;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
- htonl(MROUTED_LEVEL | nflags), datalen);
+ r = rt_end;
+ while (r != RT_ADDR) {
+ i = report_chunk(which_routes, r, vifi, dst);
+ while (i-- > 0)
+ r = r->rt_prev;
}
}
@@ -955,10 +1215,8 @@ report_to_all_neighbors(which_routes)
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
- if (v->uv_neighbors != NULL) {
- report(which_routes, vifi,
- (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
- : dvmrp_group);
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ report(which_routes, vifi, v->uv_dst_addr);
}
}
@@ -987,7 +1245,8 @@ report_to_all_neighbors(which_routes)
* 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
*/
static int
-report_chunk(start_rt, vifi, dst)
+report_chunk(which_routes, start_rt, vifi, dst)
+ int which_routes;
register struct rtentry *start_rt;
vifi_t vifi;
u_int32 dst;
@@ -996,27 +1255,63 @@ report_chunk(start_rt, vifi, dst)
register char *p;
register int i;
register int nrt = 0;
+ struct uvif *v = &uvifs[vifi];
int datalen = 0;
int width = 0;
u_int32 mask = 0;
u_int32 src;
- u_int32 nflags;
+ int admetric = v->uv_admetric;
+ int metric;
- src = uvifs[vifi].uv_lcl_addr;
+ src = v->uv_lcl_addr;
p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
- nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
-
for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
+ if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) {
+ nrt++;
+ continue;
+ }
-#ifdef NOTYET
- /* Don't send poisoned routes back to parents if I am a leaf */
- if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi)
- && (r->rt_metric > 1)) {
- ++nrt;
+ /*
+ * Do not poison-reverse a route for a directly-connected
+ * subnetwork on that subnetwork. This can cause loops when
+ * some router on the subnetwork is misconfigured.
+ */
+ if (r->rt_gateway == 0 && r->rt_parent == vifi) {
+ nrt++;
continue;
}
-#endif
+
+ if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == r->rt_origin) &&
+ (vfe->vfe_mask == r->rt_originmask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (v->uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s",
+ RT_FMT(r, s1), vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+ nrt++;
+ continue;
+ }
+ }
/*
* If there is no room for this route in the current message,
@@ -1026,10 +1321,10 @@ report_chunk(start_rt, vifi, dst)
(width + 1) :
(r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
*(p-1) |= 0x80;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
- htonl(MROUTED_LEVEL | nflags), datalen);
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
return (nrt);
}
+
if (r->rt_originmask != mask || datalen == 0) {
mask = r->rt_originmask;
width = r->rt_originwidth;
@@ -1042,16 +1337,20 @@ report_chunk(start_rt, vifi, dst)
for (i = 0; i < width; ++i)
*p++ = ((char *)&(r->rt_origin))[i];
- *p++ = (r->rt_parent == vifi && r->rt_metric != UNREACHABLE) ?
- (char)(r->rt_metric + UNREACHABLE) : /* "poisoned reverse" */
- (char)(r->rt_metric);
+ metric = r->rt_metric + admetric;
+ if (metric > UNREACHABLE)
+ metric = UNREACHABLE;
+ if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r))
+ metric = UNREACHABLE;
+ *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ?
+ (char)(metric + UNREACHABLE) : /* "poisoned reverse" */
+ (char)(metric);
++nrt;
datalen += width + 1;
}
if (datalen != 0) {
*(p-1) |= 0x80;
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
- htonl(MROUTED_LEVEL | nflags), datalen);
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
}
return (nrt);
}
@@ -1086,14 +1385,8 @@ report_next_chunk()
* all our neighbors.
*/
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
- if ((v->uv_neighbors != NULL)
-#ifdef NOTYET
- && !(v->uv_flags & VIFF_LEAF)
-#endif
- ) {
- n = report_chunk(sr, vifi,
- (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
- : dvmrp_group);
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr);
if (n < min)
min = n;
}
@@ -1102,6 +1395,7 @@ report_next_chunk()
min = 0; /* Neighborless router didn't send any routes */
n = min;
+ IF_DEBUG(DEBUG_ROUTE)
log(LOG_INFO, 0, "update %d starting at %d of %d",
n, (nroutes - start_rt), nroutes);
@@ -1124,7 +1418,7 @@ dump_routes(fp)
fprintf(fp,
"Multicast Routing Table (%u %s)\n%s\n",
nroutes, (nroutes == 1) ? "entry" : "entries",
- " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs");
+ " Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs");
for (r = routing_table; r != NULL; r = r->rt_next) {
@@ -1135,12 +1429,31 @@ dump_routes(fp)
fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
r->rt_metric);
- fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent);
+ fprintf(fp, " %3u %c%c %3u ", r->rt_timer,
+ (r->rt_flags & RTF_CHANGED) ? 'C' : '.',
+ (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.',
+ r->rt_parent);
for (i = 0; i < numvifs; ++i) {
+ struct listaddr *n;
+ char l = '[';
+
if (VIFM_ISSET(i, r->rt_children)) {
- fprintf(fp, " %u%c",
- i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
+ if ((uvifs[i].uv_flags & VIFF_TUNNEL) &&
+ !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ /* Don't print out parenthood of a leaf tunnel. */
+ continue;
+ fprintf(fp, " %u", i);
+ if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ fprintf(fp, "*");
+ for (n = uvifs[i].uv_neighbors; n; n = n->al_next) {
+ if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ fprintf(fp, "%c%d", l, n->al_index);
+ l = ',';
+ }
+ }
+ if (l == ',')
+ fprintf(fp, "]");
}
}
fprintf(fp, "\n");
@@ -1155,7 +1468,8 @@ determine_route(src)
struct rtentry *rt;
for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
- if (rt->rt_origin == (src & rt->rt_originmask))
+ if (rt->rt_origin == (src & rt->rt_originmask) &&
+ rt->rt_metric != UNREACHABLE)
break;
}
return rt;
diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h
index 73159d50261e..136b2e685cb7 100644
--- a/usr.sbin/mrouted/route.h
+++ b/usr.sbin/mrouted/route.h
@@ -7,7 +7,7 @@
* Leland Stanford Junior University.
*
*
- * $Id: route.h,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * route.h,v 3.8.4.6 1997/07/01 23:02:35 fenner Exp
*/
/*
@@ -35,17 +35,18 @@ struct rtentry {
u_int32 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_int32 *rt_dominants; /* per vif dominant gateways */
- u_int32 *rt_subordinates; /* per vif subordinate gateways */
- u_int *rt_leaf_timers; /* per vif leaf confirmation timers */
+ nbrbitmap_t rt_subordinates; /* bitmap of subordinate gateways */
+ nbrbitmap_t rt_subordadv; /* recently advertised subordinates */
u_int rt_timer; /* for timing out the route entry */
struct rtentry *rt_prev; /* link to previous entry */
struct gtable *rt_groups; /* link to active groups */
};
#define RTF_CHANGED 0x01 /* route changed but not reported */
-#define RTF_LEAF_TIMING 0x02 /* some leaf timers are running */
+#define RTF_HOLDDOWN 0x04 /* this route is in holddown */
#define ALL_ROUTES 0 /* possible arguments to report() */
#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */
+
+#define RT_FMT(r, s) inet_fmts((r)->rt_origin, (r)->rt_originmask, s)
diff --git a/usr.sbin/mrouted/rsrr.c b/usr.sbin/mrouted/rsrr.c
index ea9628f55aea..48cf5727b6ac 100644
--- a/usr.sbin/mrouted/rsrr.c
+++ b/usr.sbin/mrouted/rsrr.c
@@ -34,21 +34,14 @@
#include "defs.h"
#include <sys/param.h>
-#if (defined(BSD) && (BSD >= 199103))
-#include <stddef.h>
+#ifdef HAVE_SA_LEN
+#include <stddef.h> /* for offsetof */
#endif
-/* Taken from prune.c */
-/*
- * checks for scoped multicast addresses
- */
-#define GET_SCOPE(gt) { \
- register int _i; \
- if (((gt)->gt_mcastgrp & 0xff000000) == 0xef000000) \
- for (_i = 0; _i < numvifs; _i++) \
- if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
- VIFM_SET(_i, (gt)->gt_scope); \
- }
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+rsrr.c,v 3.8.4.8 1998/01/06 01:57:58 fenner Exp $";
+#endif
/*
* Exported variables.
@@ -72,6 +65,7 @@ static void rsrr_accept __P((int recvlen));
static void rsrr_accept_iq __P((void));
static int rsrr_accept_rq __P((struct rsrr_rq *route_query, int flags,
struct gtable *gt_notify));
+static void rsrr_read __P((int, fd_set *));
static int rsrr_send __P((int sendlen));
static void rsrr_cache __P((struct gtable *gt,
struct rsrr_rq *route_query));
@@ -90,7 +84,7 @@ rsrr_init()
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, RSRR_SERV_PATH);
-#if (defined(BSD) && (BSD >= 199103))
+#ifdef HAVE_SA_LEN
servlen = offsetof(struct sockaddr_un, sun_path) +
strlen(serv_addr.sun_path);
serv_addr.sun_len = servlen;
@@ -101,18 +95,17 @@ rsrr_init()
if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0)
log(LOG_ERR, errno, "Can't bind RSRR socket");
- if (register_input_handler(rsrr_socket,rsrr_read) < 0)
+ if (register_input_handler(rsrr_socket, rsrr_read) < 0)
log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
}
/* Read a message from the RSRR socket */
-void
+static void
rsrr_read(f, rfd)
int f;
fd_set *rfd;
{
register int rsrr_recvlen;
- register int omask;
bzero((char *) &client_addr, sizeof(client_addr));
rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
@@ -122,10 +115,7 @@ rsrr_read(f, rfd)
log(LOG_ERR, errno, "RSRR recvfrom");
return;
}
- /* Use of omask taken from main() */
- omask = sigblock(sigmask(SIGALRM));
rsrr_accept(rsrr_recvlen);
- (void)sigsetmask(omask);
}
/* Accept a message from the reservation protocol and take
@@ -159,7 +149,8 @@ rsrr_accept(recvlen)
switch (rsrr->type) {
case RSRR_INITIAL_QUERY:
/* Send Initial Reply to client */
- log(LOG_INFO, 0, "Received Initial Query\n");
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Received Initial Query\n");
rsrr_accept_iq();
break;
case RSRR_ROUTE_QUERY:
@@ -172,7 +163,8 @@ rsrr_accept(recvlen)
}
/* Get the query */
route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
- log(LOG_INFO, 0,
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0,
"Received Route Query for src %s grp %s notification %d",
inet_fmt(route_query->source_addr.s_addr, s1),
inet_fmt(route_query->dest_addr.s_addr,s2),
@@ -238,7 +230,8 @@ rsrr_accept_iq()
sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
/* Send it. */
- log(LOG_INFO, 0, "Send RSRR Initial Reply");
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Send RSRR Initial Reply");
rsrr_send(sendlen);
}
@@ -259,7 +252,7 @@ rsrr_accept_rq(route_query,flags,gt_notify)
struct rsrr_rr *route_reply;
struct gtable *gt,local_g;
struct rtentry *r;
- int sendlen,i;
+ int sendlen;
u_long mcastgrp;
/* Set up message */
@@ -291,8 +284,10 @@ rsrr_accept_rq(route_query,flags,gt_notify)
rsrr->flags = flags;
/* Include the routing entry. */
route_reply->in_vif = gt_notify->gt_route->rt_parent;
- route_reply->out_vif_bm = gt_notify->gt_grpmems;
-
+ if (BIT_TST(flags,RSRR_NOTIFICATION_BIT))
+ route_reply->out_vif_bm = gt_notify->gt_grpmems;
+ else
+ route_reply->out_vif_bm = 0;
} else if (find_src_grp(route_query->source_addr.s_addr, 0,
route_query->dest_addr.s_addr)) {
@@ -331,17 +326,7 @@ rsrr_accept_rq(route_query,flags,gt_notify)
gt->gt_route = r;
/* obtain the multicast group membership list */
- for (i = 0; i < numvifs; i++) {
- if (VIFM_ISSET(i, r->rt_children) &&
- !(VIFM_ISSET(i, r->rt_leaves)))
- VIFM_SET(i, gt->gt_grpmems);
-
- if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
- VIFM_SET(i, gt->gt_grpmems);
- }
-
- GET_SCOPE(gt);
- gt->gt_grpmems &= ~gt->gt_scope;
+ determine_forwvifs(gt);
/* Include the routing entry. */
route_reply->in_vif = gt->gt_route->rt_parent;
@@ -353,13 +338,9 @@ rsrr_accept_rq(route_query,flags,gt_notify)
}
}
- if (gt_notify)
- log(LOG_INFO, 0, "Route Change: Send RSRR Route Reply");
-
- else
- log(LOG_INFO, 0, "Send RSRR Route Reply");
-
- log(LOG_INFO, 0, "for src %s dst %s in vif %d out vif %d\n",
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n",
+ gt_notify ? "Route Change: " : "",
inet_fmt(route_reply->source_addr.s_addr,s1),
inet_fmt(route_reply->dest_addr.s_addr,s2),
route_reply->in_vif,route_reply->out_vif_bm);
@@ -419,6 +400,7 @@ rsrr_cache(gt,route_query)
} else {
/* Update */
rc->route_query.query_id = route_query->query_id;
+ IF_DEBUG(DEBUG_RSRR)
log(LOG_DEBUG, 0,
"Update cached query id %ld from client %s\n",
rc->route_query.query_id, rc->client_addr.sun_path);
@@ -441,6 +423,7 @@ rsrr_cache(gt,route_query)
rc->client_length = client_length;
rc->next = gt->gt_rsrr_cache;
gt->gt_rsrr_cache = rc;
+ IF_DEBUG(DEBUG_RSRR)
log(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
rc->route_query.query_id,rc->client_addr.sun_path);
}
@@ -462,6 +445,7 @@ rsrr_cache_send(gt,notify)
rcnp = &gt->gt_rsrr_cache;
while ((rc = *rcnp) != NULL) {
if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
+ IF_DEBUG(DEBUG_RSRR)
log(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
rc->route_query.query_id,rc->client_addr.sun_path);
/* Delete cache entry. */
@@ -480,7 +464,9 @@ rsrr_cache_clean(gt)
{
struct rsrr_cache *rc,*rc_next;
- printf("cleaning cache for group %s\n",inet_fmt(gt->gt_mcastgrp, s1));
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "cleaning cache for group %s\n",
+ inet_fmt(gt->gt_mcastgrp, s1));
rc = gt->gt_rsrr_cache;
while (rc) {
rc_next = rc->next;
diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c
index 3716292d1cab..1cebd0ec0014 100644
--- a/usr.sbin/mrouted/vif.c
+++ b/usr.sbin/mrouted/vif.c
@@ -7,13 +7,18 @@
* Leland Stanford Junior University.
*
*
- * $Id: vif.c,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * vif.c,v 3.8.4.56.2.1 1999/01/20 05:18:50 fenner Exp
*/
#include "defs.h"
#include <fcntl.h>
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+vif.c,v 3.8.4.56.2.1 1999/01/20 05:18:50 fenner Exp $";
+#endif
+
/*
* Exported variables.
*/
@@ -27,6 +32,11 @@ int udp_socket; /* Since the honkin' kernel doesn't support */
/* How dumb. */
int vifs_with_neighbors; /* == 1 if I am a leaf */
+/*
+ * Private variables.
+ */
+struct listaddr *nbrs[MAXNBRS]; /* array of neighbors */
+
typedef struct {
vifi_t vifi;
struct listaddr *g;
@@ -41,7 +51,8 @@ static void start_vif2 __P((vifi_t vifi));
static void stop_vif __P((vifi_t vifi));
static void age_old_hosts __P((void));
static void send_probe_on_vif __P((struct uvif *v));
-static int info_version __P((char *p));
+static void send_query __P((struct uvif *v));
+static int info_version __P((u_char *p));
static void DelVif __P((void *arg));
static int SetTimer __P((int vifi, struct listaddr *g));
static int DeleteTimer __P((int id));
@@ -70,10 +81,15 @@ init_vifs()
/*
* Configure the vifs based on the interface configuration of the
* the kernel and the contents of the configuration file.
- * (Open a UDP socket for ioctl use in the config procedures.)
+ * (Open a UDP socket for ioctl use in the config procedures if
+ * the kernel can't handle IOCTL's on the IGMP socket.)
*/
+#ifdef IOCTL_OK_ON_RAW_SOCKET
+ udp_socket = igmp_socket;
+#else
if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
log(LOG_ERR, errno, "UDP socket");
+#endif
log(LOG_INFO,0,"Getting vifs from kernel interfaces");
config_vifs_from_kernel();
log(LOG_INFO,0,"Getting vifs from %s",configfilename);
@@ -123,6 +139,47 @@ init_vifs()
}
/*
+ * Initialize the passed vif with all appropriate default values.
+ * "t" is true if a tunnel, or false if a phyint.
+ */
+void
+zero_vif(v, t)
+ struct uvif *v;
+ int t;
+{
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_threshold = DEFAULT_THRESHOLD;
+ v->uv_rate_limit = t ? DEFAULT_TUN_RATE_LIMIT : DEFAULT_PHY_RATE_LIMIT;
+ v->uv_lcl_addr = 0;
+ v->uv_rmt_addr = 0;
+ v->uv_dst_addr = t ? 0 : dvmrp_group;
+ v->uv_subnet = 0;
+ v->uv_subnetmask = 0;
+ v->uv_subnetbcast = 0;
+ v->uv_name[0] = '\0';
+ v->uv_groups = NULL;
+ v->uv_neighbors = NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = NULL;
+ v->uv_igmpv1_warn = 0;
+ v->uv_prune_lifetime = 0;
+ v->uv_leaf_timer = 0;
+ v->uv_acl = NULL;
+ v->uv_addrs = NULL;
+ v->uv_filter = NULL;
+ v->uv_blasterbuf = NULL;
+ v->uv_blastercur = NULL;
+ v->uv_blasterend = NULL;
+ v->uv_blasterlen = 0;
+ v->uv_blastertimer = 0;
+ v->uv_nbrup = 0;
+ v->uv_icmp_warn = 0;
+ v->uv_nroutes = 0;
+}
+
+/*
* Start routing on all virtual interfaces that are not down or
* administratively disabled.
*/
@@ -163,8 +220,18 @@ check_vif_state()
register vifi_t vifi;
register struct uvif *v;
struct ifreq ifr;
+ static int checking_vifs = 0;
+
+ /*
+ * If we get an error while checking, (e.g. two interfaces go down
+ * at once, and we decide to send a prune out one of the failed ones)
+ * then don't go into an infinite loop!
+ */
+ if (checking_vifs)
+ return;
vifs_down = FALSE;
+ checking_vifs = 1;
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
if (v->uv_flags & VIFF_DISABLED) continue;
@@ -176,27 +243,59 @@ check_vif_state()
if (v->uv_flags & VIFF_DOWN) {
if (ifr.ifr_flags & IFF_UP) {
- v->uv_flags &= ~VIFF_DOWN;
- start_vif(vifi);
- log(LOG_INFO, 0,
+ log(LOG_NOTICE, 0,
"%s has come up; vif #%u now in service",
v->uv_name, vifi);
+ v->uv_flags &= ~VIFF_DOWN;
+ start_vif(vifi);
}
else vifs_down = TRUE;
}
else {
if (!(ifr.ifr_flags & IFF_UP)) {
- stop_vif(vifi);
- v->uv_flags |= VIFF_DOWN;
- log(LOG_INFO, 0,
+ log(LOG_NOTICE, 0,
"%s has gone down; vif #%u taken out of service",
v->uv_name, vifi);
+ stop_vif(vifi);
+ v->uv_flags |= VIFF_DOWN;
vifs_down = TRUE;
}
}
}
+ checking_vifs = 0;
+}
+
+/*
+ * Send a DVMRP message on the specified vif. If DVMRP messages are
+ * to be encapsulated and sent "inside" the tunnel, use the special
+ * encapsulator. If it's not a tunnel or DVMRP messages are to be
+ * sent "beside" the tunnel, as required by earlier versions of mrouted,
+ * then just send the message.
+ */
+void
+send_on_vif(v, dst, code, datalen)
+ register struct uvif *v;
+ u_int32 dst;
+ int code;
+ int datalen;
+{
+ u_int32 group = htonl(MROUTED_LEVEL |
+ ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS));
+
+ /*
+ * The UNIX kernel will not decapsulate unicasts.
+ * Therefore, we don't send encapsulated unicasts.
+ */
+ if ((v->uv_flags & (VIFF_TUNNEL|VIFF_OTUNNEL)) == VIFF_TUNNEL &&
+ ((dst == 0) || IN_MULTICAST(ntohl(dst))))
+ send_ipip(v->uv_lcl_addr, dst ? dst : dvmrp_group, IGMP_DVMRP,
+ code, group, datalen, v);
+ else
+ send_igmp(v->uv_lcl_addr, dst ? dst : v->uv_dst_addr, IGMP_DVMRP,
+ code, group, datalen);
}
+
/*
* Send a probe message on vif v
*/
@@ -209,6 +308,10 @@ send_probe_on_vif(v)
struct listaddr *nbr;
int i;
+ if ((v->uv_flags & VIFF_PASSIVE && v->uv_neighbors == NULL) ||
+ (v->uv_flags & VIFF_FORCE_LEAF))
+ return;
+
p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
for (i = 0; i < 4; i++)
@@ -227,13 +330,21 @@ send_probe_on_vif(v)
nbr = nbr->al_next;
}
- send_igmp(v->uv_lcl_addr,
- (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
- : dvmrp_group,
- IGMP_DVMRP, DVMRP_PROBE,
- htonl(MROUTED_LEVEL |
- ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS)),
- datalen);
+ send_on_vif(v, 0, DVMRP_PROBE, datalen);
+}
+
+static void
+send_query(v)
+ register struct uvif *v;
+{
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "sending %squery on vif %d",
+ (v->uv_flags & VIFF_IGMPV1) ? "v1 " : "",
+ v - uvifs);
+ send_igmp(v->uv_lcl_addr, allhosts_group,
+ IGMP_MEMBERSHIP_QUERY,
+ (v->uv_flags & VIFF_IGMPV1) ? 0 :
+ IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
}
/*
@@ -293,10 +404,10 @@ start_vif2(vifi)
* the interface is connected.
*/
start_route_updates();
- update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi);
+ update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi, NULL);
for (p = v->uv_addrs; p; p = p->pa_next) {
start_route_updates();
- update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi);
+ update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi, NULL);
}
/*
@@ -305,10 +416,9 @@ start_vif2(vifi)
* query.
*/
v->uv_flags |= VIFF_QUERIER;
- send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY,
- (v->uv_flags & VIFF_IGMPV1) ? 0 :
- IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
- age_old_hosts();
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "assuming querier duties on vif %d", vifi);
+ send_query(v);
}
v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
@@ -349,10 +459,10 @@ stop_vif(vifi)
* failure.
*/
start_route_updates();
- update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi);
+ update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi, NULL);
for (p = v->uv_addrs; p; p = p->pa_next) {
start_route_updates();
- update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi);
+ update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi, NULL);
}
/*
@@ -365,6 +475,8 @@ stop_vif(vifi)
free((char *)a);
}
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "releasing querier duties on vif %d", vifi);
v->uv_flags &= ~VIFF_QUERIER;
}
@@ -381,14 +493,16 @@ stop_vif(vifi)
/*
* Discard all neighbor addresses.
*/
- if (v->uv_neighbors)
+ if (!NBRM_ISEMPTY(v->uv_nbrmap))
vifs_with_neighbors--;
while (v->uv_neighbors != NULL) {
a = v->uv_neighbors;
v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
free((char *)a);
}
+ NBRM_CLRALL(v->uv_nbrmap);
}
@@ -413,6 +527,7 @@ stop_all_vifs()
while (v->uv_neighbors != NULL) {
a = v->uv_neighbors;
v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
free((char *)a);
}
while (v->uv_acl != NULL) {
@@ -440,7 +555,8 @@ find_vif(src, dst)
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
if (v->uv_flags & VIFF_TUNNEL) {
- if (src == v->uv_rmt_addr && dst == v->uv_lcl_addr)
+ if (src == v->uv_rmt_addr && (dst == v->uv_lcl_addr ||
+ dst == dvmrp_group))
return(vifi);
}
else {
@@ -479,7 +595,13 @@ age_old_hosts()
/*
- * Send group membership queries to all subnets for which I am querier.
+ * Send group membership queries on each interface for which I am querier.
+ * Note that technically, there should be a timer per interface, as the
+ * dynamics of querier election can cause the "right" time to send a
+ * query to be different on different interfaces. However, this simple
+ * implementation only ever sends queries sooner than the "right" time,
+ * so can not cause loss of membership (but can send more packets than
+ * necessary)
*/
void
query_groups()
@@ -489,17 +611,16 @@ query_groups()
for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
if (v->uv_flags & VIFF_QUERIER) {
- send_igmp(v->uv_lcl_addr, allhosts_group,
- IGMP_HOST_MEMBERSHIP_QUERY,
- (v->uv_flags & VIFF_IGMPV1) ? 0 :
- IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
+ send_query(v);
}
}
age_old_hosts();
}
/*
- * Process an incoming host membership query
+ * Process an incoming host membership query. Warn about
+ * IGMP version mismatches, perform querier election, and
+ * handle group-specific queries when we're not the querier.
*/
void
accept_membership_query(src, dst, group, tmo)
@@ -519,20 +640,97 @@ accept_membership_query(src, dst, group, tmo)
v = &uvifs[vifi];
+ if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
+ (tmo != 0 && (v->uv_flags & VIFF_IGMPV1))) {
+ int i;
+
+ /*
+ * Exponentially back-off warning rate
+ */
+ i = ++v->uv_igmpv1_warn;
+ while (i && !(i & 1))
+ i >>= 1;
+ if (i == 1)
+ log(LOG_WARNING, 0, "%s %s on vif %d, %s",
+ tmo == 0 ? "Received IGMPv1 report from"
+ : "Received IGMPv2 report from",
+ inet_fmt(src, s1),
+ vifi,
+ tmo == 0 ? "please configure vif for IGMPv1"
+ : "but I am configured for IGMPv1");
+ }
+
+ if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
+ /*
+ * This might be:
+ * - A query from a new querier, with a lower source address
+ * than the current querier (who might be me)
+ * - A query from a new router that just started up and doesn't
+ * know who the querier is.
+ */
+ if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
+ : ntohl(v->uv_lcl_addr))) {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
+ inet_fmt(src, s1),
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me", vifi);
+ if (!v->uv_querier) {
+ v->uv_querier = (struct listaddr *)
+ malloc(sizeof(struct listaddr));
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ time(&v->uv_querier->al_ctime);
+ v->uv_querier->al_addr = src;
+ } else {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "ignoring query from %s; querier on vif %d is still %s",
+ inet_fmt(src, s1), vifi,
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me");
+
+ return;
+ }
+ }
+
/*
- * If we consider ourselves the querier for this vif, but hear a
- * query from a router with a lower IP address, yield to them.
- *
- * This is done here as well as in the neighbor discovery in case
- * there is a querier that doesn't speak DVMRP.
- *
- * XXX If this neighbor doesn't speak DVMRP, then we need to create
- * some neighbor state for him so that we can time him out!
+ * Reset the timer since we've received a query.
*/
- if ((v->uv_flags & VIFF_QUERIER) &&
- (ntohl(src) < ntohl(v->uv_lcl_addr))) {
- v->uv_flags &= ~VIFF_QUERIER;
+ if (v->uv_querier && src == v->uv_querier->al_addr)
+ v->uv_querier->al_timer = 0;
+ /*
+ * If this is a Group-Specific query which we did not source,
+ * we must set our membership timer to [Last Member Query Count] *
+ * the [Max Response Time] in the packet.
+ */
+ if (!(v->uv_flags & (VIFF_IGMPV1|VIFF_QUERIER)) && group != 0 &&
+ src != v->uv_lcl_addr) {
+ register struct listaddr *g;
+
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "%s for %s from %s on vif %d, timer %d",
+ "Group-specific membership query",
+ inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr && g->al_query == 0) {
+ /* setup a timeout to remove the group membership */
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
+ tmo / IGMP_TIMER_SCALE;
+ /* use al_query to record our presence in last-member state */
+ g->al_query = -1;
+ g->al_timerid = SetTimer(vifi, g);
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "timer for grp %s on vif %d set to %d",
+ inet_fmt(group, s2), vifi, g->al_timer);
+ break;
+ }
+ }
}
}
@@ -563,14 +761,13 @@ accept_group_report(src, dst, group, r_type)
*/
for (g = v->uv_groups; g != NULL; g = g->al_next) {
if (group == g->al_addr) {
- if (r_type == IGMP_HOST_MEMBERSHIP_REPORT)
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
g->al_old = OLD_AGE_THRESHOLD;
-#ifdef SNMP
- g->al_genid = src;
-#endif /* SNMP */
+
+ g->al_reporter = src;
/** delete old timers, set a timer for expiration **/
- g->al_timer = GROUP_EXPIRE_TIME;
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
if (g->al_query)
g->al_query = DeleteTimer(g->al_query);
if (g->al_timerid)
@@ -589,21 +786,19 @@ accept_group_report(src, dst, group, r_type)
log(LOG_ERR, 0, "ran out of memory"); /* fatal */
g->al_addr = group;
- if (r_type == IGMP_HOST_NEW_MEMBERSHIP_REPORT)
- g->al_old = 0;
- else
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
g->al_old = OLD_AGE_THRESHOLD;
-#ifdef SNMP
- g->al_genid = src;
-#endif
+ else
+ g->al_old = 0;
/** set a timer for expiration **/
- g->al_query = 0;
- g->al_timer = GROUP_EXPIRE_TIME;
+ g->al_query = 0;
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ g->al_reporter = src;
+ g->al_timerid = SetTimer(vifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
time(&g->al_ctime);
- g->al_timerid = SetTimer(vifi, g);
- g->al_next = v->uv_groups;
- v->uv_groups = g;
update_lclgrp(vifi, group);
}
@@ -614,7 +809,9 @@ accept_group_report(src, dst, group, r_type)
chkgrp_graft(vifi, group);
}
-
+/*
+ * Process an incoming IGMPv2 Leave Group message.
+ */
void
accept_leave_message(src, dst, group)
u_int32 src, dst, group;
@@ -642,6 +839,7 @@ accept_leave_message(src, dst, group)
*/
for (g = v->uv_groups; g != NULL; g = g->al_next) {
if (group == g->al_addr) {
+ IF_DEBUG(DEBUG_IGMP)
log(LOG_DEBUG, 0,
"[vif.c, _accept_leave_message] %d %d \n",
g->al_old, g->al_query);
@@ -658,14 +856,20 @@ accept_leave_message(src, dst, group)
if (g->al_timerid)
g->al_timerid = DeleteTimer(g->al_timerid);
+#if IGMP_LAST_MEMBER_QUERY_COUNT != 2
+This code needs to be updated to keep a counter of the number
+of queries remaining.
+#endif
/** send a group specific querry **/
- g->al_timer = LEAVE_EXPIRE_TIME;
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
+ (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
send_igmp(v->uv_lcl_addr, g->al_addr,
- IGMP_HOST_MEMBERSHIP_QUERY,
- LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE,
- g->al_addr, 0);
- g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3,
- LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE);
+ IGMP_MEMBERSHIP_QUERY,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
+ g->al_addr, 0);
+ g->al_query = SetQueryTimer(g, vifi,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
g->al_timerid = SetTimer(vifi, g);
break;
}
@@ -704,33 +908,7 @@ accept_neighbor_request(src, dst)
u_char *p, *ncount;
struct listaddr *la;
int datalen;
- u_int32 temp_addr, us, them = src;
-
- /* Determine which of our addresses to use as the source of our response
- * to this query.
- */
- if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
- int udp; /* find best interface to reply on */
- struct sockaddr_in addr;
- int addrlen = sizeof(addr);
-
- addr.sin_family = AF_INET;
-#if (defined(BSD) && (BSD >= 199103))
- addr.sin_len = sizeof addr;
-#endif
- addr.sin_addr.s_addr = dst;
- 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) {
- log(LOG_WARNING, errno, "Determining local address");
- close(udp);
- return;
- }
- close(udp);
- us = addr.sin_addr.s_addr;
- } else /* query sent to us alone */
- us = dst;
+ u_int32 temp_addr, them = src;
#define PUT_ADDR(a) temp_addr = ntohl(a); \
*p++ = temp_addr >> 24; \
@@ -751,7 +929,7 @@ accept_neighbor_request(src, dst)
/* Make sure that there's room for this neighbor... */
if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
- send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
htonl(MROUTED_LEVEL), datalen);
p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
datalen = 0;
@@ -775,8 +953,8 @@ accept_neighbor_request(src, dst)
}
if (datalen != 0)
- send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS, htonl(MROUTED_LEVEL),
- datalen);
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ htonl(MROUTED_LEVEL), datalen);
}
/*
@@ -791,33 +969,7 @@ accept_neighbor_request2(src, dst)
u_char *p, *ncount;
struct listaddr *la;
int datalen;
- u_int32 us, them = src;
-
- /* Determine which of our addresses to use as the source of our response
- * to this query.
- */
- if (IN_MULTICAST(ntohl(dst))) { /* query sent to a multicast group */
- int udp; /* find best interface to reply on */
- struct sockaddr_in addr;
- int addrlen = sizeof(addr);
-
- addr.sin_family = AF_INET;
-#if (defined(BSD) && (BSD >= 199103))
- addr.sin_len = sizeof addr;
-#endif
- addr.sin_addr.s_addr = dst;
- 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) {
- log(LOG_WARNING, errno, "Determining local address");
- close(udp);
- return;
- }
- close(udp);
- us = addr.sin_addr.s_addr;
- } else /* query sent to us alone */
- us = dst;
+ u_int32 them = src;
p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
datalen = 0;
@@ -847,7 +999,7 @@ accept_neighbor_request2(src, dst)
if (rflags & DVMRP_NF_TUNNEL)
rflags |= DVMRP_NF_DOWN;
if (datalen > MAX_DVMRP_DATA_LEN - 12) {
- send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
htonl(MROUTED_LEVEL), datalen);
p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
datalen = 0;
@@ -865,7 +1017,7 @@ accept_neighbor_request2(src, dst)
for ( ; la; la = la->al_next) {
/* Make sure that there's room for this neighbor... */
if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
- send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
htonl(MROUTED_LEVEL), datalen);
p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
datalen = 0;
@@ -873,6 +1025,9 @@ accept_neighbor_request2(src, dst)
}
/* Put out the header for this neighbor list... */
if (ncount == 0) {
+ /* If it's a one-way tunnel, mark it down. */
+ if (rflags & DVMRP_NF_TUNNEL && la->al_flags & NBRF_ONEWAY)
+ rflags |= DVMRP_NF_DOWN;
*(u_int*)p = v->uv_lcl_addr;
p += 4;
*p++ = v->uv_metric;
@@ -882,16 +1037,25 @@ accept_neighbor_request2(src, dst)
*p++ = 0;
datalen += 4 + 4;
}
+ /* Don't report one-way peering on phyint at all */
+ if (!(rflags & DVMRP_NF_TUNNEL) && la->al_flags & NBRF_ONEWAY)
+ continue;
*(u_int*)p = la->al_addr;
p += 4;
datalen += 4;
(*ncount)++;
}
+ if (*ncount == 0) {
+ *(u_int*)p = v->uv_rmt_addr;
+ p += 4;
+ datalen += 4;
+ (*ncount)++;
+ }
}
}
if (datalen != 0)
- send_igmp(us, them, IGMP_DVMRP, DVMRP_NEIGHBORS2, htonl(MROUTED_LEVEL),
- datalen);
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
}
void
@@ -942,7 +1106,7 @@ accept_info_request(src, dst, p, datalen)
*/
static int
info_version(p)
- char *p;
+ u_char *p;
{
int len;
extern char versionstring[];
@@ -951,7 +1115,7 @@ info_version(p)
p++; /* skip over length */
*p++ = 0; /* zero out */
*p++ = 0; /* reserved fields */
- strcpy(p, versionstring); /* XXX strncpy!!! */
+ strcpy((char *)p, versionstring); /* XXX strncpy!!! */
len = strlen(versionstring);
return ((len + 3) / 4);
@@ -980,7 +1144,8 @@ accept_neighbors2(src, dst, p, datalen, level)
u_char *p;
int datalen;
{
- log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
@@ -993,7 +1158,8 @@ accept_info_reply(src, dst, p, datalen)
u_char *p;
int datalen;
{
- log(LOG_INFO, 0, "ignoring spurious DVMRP info reply from %s to %s",
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP info reply from %s to %s",
inet_fmt(src, s1), inet_fmt(dst, s2));
}
@@ -1001,9 +1167,9 @@ accept_info_reply(src, dst, p, datalen)
/*
* Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
* 'msgtype' is the type of DVMRP message received from the neighbor.
- * Return TRUE if 'addr' is a valid neighbor, FALSE otherwise.
+ * Return the neighbor entry if 'addr' is a valid neighbor, FALSE otherwise.
*/
-int
+struct listaddr *
update_neighbor(vifi, addr, msgtype, p, datalen, level)
vifi_t vifi;
u_int32 addr;
@@ -1014,14 +1180,17 @@ update_neighbor(vifi, addr, msgtype, p, datalen, level)
{
register struct uvif *v;
register struct listaddr *n;
- u_int32 genid = 0;
- u_int32 router;
+ int pv = level & 0xff;
+ int mv = (level >> 8) & 0xff;
+ int has_genid = 0;
+ int in_router_list = 0;
+ int dvmrpspec = 0;
+ u_int32 genid;
u_int32 send_tables = 0;
+ int i;
int do_reset = FALSE;
- int nflags;
v = &uvifs[vifi];
- nflags = (level >> 16) & 0xff;
/*
* Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
@@ -1038,11 +1207,66 @@ update_neighbor(vifi, addr, msgtype, p, datalen, level)
(addr == v->uv_lcl_addr ||
addr == v->uv_subnet )) {
log(LOG_WARNING, 0,
- "received DVMRP message from 'the unknown host' or self: %s",
+ "received DVMRP message from %s: %s",
+ (addr == v->uv_lcl_addr) ? "self (check device loopback)" :
+ "'the unknown host'",
inet_fmt(addr, s1));
- return (FALSE);
+ return NULL;
+ }
+
+ /*
+ * Ignore all neighbors on vifs forced into leaf mode
+ */
+ if (v->uv_flags & VIFF_FORCE_LEAF) {
+ return NULL;
+ }
+
+ /*
+ * mrouted's version 3.3 and later include the generation ID
+ * and the list of neighbors on the vif in their probe messages.
+ */
+ if (msgtype == DVMRP_PROBE && ((pv == 3 && mv > 2) ||
+ (pv > 3 && pv < 10))) {
+ u_int32 router;
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "checking probe from %s (%d.%d) on vif %d",
+ inet_fmt(addr, s1), pv, mv, vifi);
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+
+ has_genid = 1;
+
+ for (i = 0; i < 4; i++)
+ ((char *)&genid)[i] = *p++;
+ datalen -= 4;
+
+ while (datalen > 0) {
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+ for (i = 0; i < 4; i++)
+ ((char *)&router)[i] = *p++;
+ datalen -= 4;
+
+ if (router == v->uv_lcl_addr) {
+ in_router_list = 1;
+ break;
+ }
+ }
}
+ if ((pv == 3 && mv == 255) || (pv > 3 && pv < 10))
+ dvmrpspec = 1;
+
/*
* Look for addr in list of neighbors.
*/
@@ -1052,157 +1276,217 @@ update_neighbor(vifi, addr, msgtype, p, datalen, level)
}
}
- /*
- * Found it. Reset its timer, and check for a version change
- */
- if (n) {
- n->al_timer = 0;
-
+ if (n == NULL) {
/*
- * update the neighbors version and protocol number
- * if changed => router went down and came up,
- * so take action immediately.
+ * New neighbor.
+ *
+ * If this neighbor follows the DVMRP spec, start the probe
+ * handshake. If not, then it doesn't require the probe
+ * handshake, so establish the peering immediately.
*/
- if ((n->al_pv != (level & 0xff)) ||
- (n->al_mv != ((level >> 8) & 0xff))) {
+ if (dvmrpspec && (msgtype != DVMRP_PROBE))
+ return NULL;
- do_reset = TRUE;
- log(LOG_DEBUG, 0,
- "version change neighbor %s [old:%d.%d, new:%d.%d]",
- inet_fmt(addr, s1),
- n->al_pv, n->al_mv, level&0xff, (level >> 8) & 0xff);
-
- n->al_pv = level & 0xff;
- n->al_mv = (level >> 8) & 0xff;
+ for (i = 0; i < MAXNBRS; i++)
+ if (nbrs[i] == NULL)
+ break;
+
+ if (i == MAXNBRS) {
+ /* XXX This is a severe new restriction. */
+ /* XXX want extensible bitmaps! */
+ log(LOG_ERR, 0, "Can't handle %dth neighbor %s on vif %d!",
+ MAXNBRS, inet_fmt(addr, s1), vifi);
+ /*NOTREACHED*/
}
- } else {
+
/*
- * If not found, add it to the list. If the neighbor has a lower
- * IP address than me, yield querier duties to it.
+ * Add it to our list of neighbors.
*/
- log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x",
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x idx %d",
inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff,
- (level >> 16) & 0xff);
+ (level >> 16) & 0xff, i);
n = (struct listaddr *)malloc(sizeof(struct listaddr));
if (n == NULL)
log(LOG_ERR, 0, "ran out of memory"); /* fatal */
n->al_addr = addr;
- n->al_pv = level & 0xff;
- n->al_mv = (level >> 8) & 0xff;
- n->al_genid = 0;
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = has_genid ? genid : 0;
+ n->al_index = i;
+ nbrs[i] = n;
time(&n->al_ctime);
n->al_timer = 0;
+ n->al_flags = has_genid ? NBRF_GENID : 0;
n->al_next = v->uv_neighbors;
+ v->uv_neighbors = n;
+
+ /*
+ * If we are not configured to peer with non-pruning routers,
+ * check the deprecated "I-know-how-to-prune" bit. This bit
+ * was MBZ in early mrouted implementations (<3.5) and is required
+ * to be set by the DVMRPv3 specification.
+ */
+ if (!(v->uv_flags & VIFF_ALLOW_NONPRUNERS) &&
+ !((level & 0x020000) || (pv == 3 && mv < 5))) {
+ n->al_flags |= NBRF_TOOOLD;
+ }
+
+ /*
+ * If this router implements the DVMRPv3 spec, then don't peer
+ * with him if we haven't yet established a bidirectional connection.
+ */
+ if (dvmrpspec) {
+ if (!in_router_list) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "waiting for probe from %s with my addr",
+ inet_fmt(addr, s1));
+ n->al_flags |= NBRF_WAITING;
+ return NULL;
+ }
+ }
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
+ }
/*
* If we thought that we had no neighbors on this vif, send a route
* report to the vif. If this is just a new neighbor on the same
* vif, send the route report just to the new neighbor.
*/
- if (v->uv_neighbors == NULL) {
- send_tables = (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group;
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
vifs_with_neighbors++;
} else {
send_tables = addr;
}
- v->uv_neighbors = n;
- if (!(v->uv_flags & VIFF_TUNNEL) &&
- ntohl(addr) < ntohl(v->uv_lcl_addr))
- v->uv_flags &= ~VIFF_QUERIER;
- }
-
- /*
- * Check if the router gen-ids are the same.
- * Need to reset the prune state of the router if not.
- * Also check for one-way interfaces by seeing if we are in our
- * neighbor's list of known routers.
- */
- if (msgtype == DVMRP_PROBE) {
-
- /* Check genid neighbor flag. Also check version number; 3.3 and
- * 3.4 didn't set this flag. */
- if ((((level >> 16) & 0xff) & NF_GENID) ||
- (((level & 0xff) == 3) && (((level >> 8) & 0xff) > 2))) {
+ NBRM_SET(i, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, i);
+ } else {
+ /*
+ * Found it. Reset its timer.
+ */
+ n->al_timer = 0;
- int i;
+ if (n->al_flags & NBRF_WAITING && msgtype == DVMRP_PROBE) {
+ n->al_flags &= ~NBRF_WAITING;
+ if (!in_router_list) {
+ log(LOG_WARNING, 0, "possible one-way peering with %s on vif %d",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
+ return NULL;
+ } else {
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d exits WAITING",
+ inet_fmt(addr, s1), vifi);
+ }
+ }
- if (datalen < 4) {
- log(LOG_WARNING, 0,
- "received truncated probe message from %s (len %d)",
- inet_fmt(addr, s1), datalen);
- return (FALSE);
+ if (n->al_flags & NBRF_ONEWAY && msgtype == DVMRP_PROBE) {
+ if (in_router_list) {
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors++;
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ log(LOG_NOTICE, 0, "peering with %s on vif %d is no longer one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags &= ~NBRF_ONEWAY;
+ } else {
+ /* XXX rate-limited warning message? */
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d is still ONEWAY",
+ inet_fmt(addr, s1), vifi);
}
+ }
- for (i = 0; i < 4; i++)
- ((char *)&genid)[i] = *p++;
- datalen -= 4;
+ /*
+ * When peering with a genid-capable but pre-DVMRP spec peer,
+ * we might bring up the peering with a route report and not
+ * remember his genid. Assume that he doesn't send a route
+ * report and then reboot before sending a probe.
+ */
+ if (has_genid && !(n->al_flags & NBRF_GENID)) {
+ n->al_flags |= NBRF_GENID;
+ n->al_genid = genid;
+ }
- if (n->al_genid == 0)
- n->al_genid = genid;
- else if (n->al_genid != genid) {
- log(LOG_DEBUG, 0,
- "new genid neigbor %s on vif %d [old:%x, new:%x]",
- inet_fmt(addr, s1), vifi, n->al_genid, genid);
+ /*
+ * update the neighbors version and protocol number and genid
+ * if changed => router went down and came up,
+ * so take action immediately.
+ */
+ if ((n->al_pv != pv) ||
+ (n->al_mv != mv) ||
+ (has_genid && n->al_genid != genid)) {
- n->al_genid = genid;
- do_reset = TRUE;
- }
-
- /*
- * loop through router list and check for one-way ifs.
- */
-
- v->uv_flags |= VIFF_ONEWAY;
+ do_reset = TRUE;
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "version/genid change neighbor %s [old:%d.%d/%8x, new:%d.%d/%8x]",
+ inet_fmt(addr, s1),
+ n->al_pv, n->al_mv, n->al_genid, pv, mv, genid);
- while (datalen > 0) {
- if (datalen < 4) {
- log(LOG_WARNING, 0,
- "received truncated probe message from %s (len %d)",
- inet_fmt(addr, s1), datalen);
- return (FALSE);
- }
- for (i = 0; i < 4; i++)
- ((char *)&router)[i] = *p++;
- datalen -= 4;
- if (router == v->uv_lcl_addr) {
- v->uv_flags &= ~VIFF_ONEWAY;
- break;
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = genid;
+ time(&n->al_ctime);
+ }
+
+ if ((pv == 3 && mv > 2) || (pv > 3 && pv < 10)) {
+ if (!(n->al_flags & VIFF_ONEWAY) && has_genid && !in_router_list &&
+ (time(NULL) - n->al_ctime > 20)) {
+ if (NBRM_ISSET(n->al_index, v->uv_nbrmap)) {
+ NBRM_CLR(n->al_index, v->uv_nbrmap);
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
}
+ delete_neighbor_from_routes(addr, vifi, n->al_index);
+ reset_neighbor_state(vifi, addr);
+ log(LOG_WARNING, 0, "peering with %s on vif %d is one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
}
}
- }
- if (n->al_flags != nflags) {
- n->al_flags = nflags;
-
- if (n->al_flags & NF_LEAF) {
- /*XXX If we have non-leaf neighbors then we know we shouldn't
- * mark this vif as a leaf. For now we just count on other
- * probes and/or reports resetting the timer. */
- if (!v->uv_leaf_timer)
- v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
- } else {
- /* If we get a leaf to non-leaf transition, we *must* update
- * the routing table. */
- if (v->uv_flags & VIFF_LEAF && send_tables == 0)
- send_tables = addr;
- v->uv_flags &= ~VIFF_LEAF;
- v->uv_leaf_timer = 0;
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
}
+
+ /* check "leaf" flag */
}
if (do_reset) {
reset_neighbor_state(vifi, addr);
if (!send_tables)
send_tables = addr;
}
- if (send_tables)
+ if (send_tables) {
+ send_probe_on_vif(v);
report(ALL_ROUTES, vifi, send_tables);
+ }
+ v->uv_leaf_timer = 0;
+ v->uv_flags &= ~VIFF_LEAF;
- return (TRUE);
+ return n;
}
@@ -1215,7 +1499,7 @@ age_vifs()
{
register vifi_t vifi;
register struct uvif *v;
- register struct listaddr *a, *prev_a, *n;
+ register struct listaddr *a, *prev_a;
register u_int32 addr;
for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
@@ -1227,39 +1511,58 @@ age_vifs()
a = v->uv_neighbors;
a != NULL;
prev_a = a, a = a->al_next) {
+ int exp_time;
+ int idx;
+
+ if (((a->al_pv == 3) && (a->al_mv >= 3)) ||
+ ((a->al_pv > 3) && (a->al_pv < 10)))
+ exp_time = NEIGHBOR_EXPIRE_TIME;
+ else
+ exp_time = OLD_NEIGHBOR_EXPIRE_TIME;
- if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME)
+ if ((a->al_timer += TIMER_INTERVAL) < exp_time)
continue;
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "Neighbor %s (%d.%d) expired after %d seconds",
+ inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv, exp_time);
+
/*
* Neighbor has expired; delete it from the neighbor list,
* delete it from the 'dominants' and 'subordinates arrays of
- * any route entries and assume querier duties unless there is
- * another neighbor with a lower IP address than mine.
+ * any route entries.
*/
+ NBRM_CLR(a->al_index, v->uv_nbrmap);
+ nbrs[a->al_index] = NULL; /* XXX is it a good idea to reuse indxs? */
+ idx = a->al_index;
addr = a->al_addr;
prev_a->al_next = a->al_next;
free((char *)a);
- a = prev_a;
+ a = prev_a;/*XXX use ** */
- delete_neighbor_from_routes(addr, vifi);
+ delete_neighbor_from_routes(addr, vifi, idx);
+ reset_neighbor_state(vifi, addr);
- if (v->uv_neighbors == NULL)
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
vifs_with_neighbors--;
v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+ }
- if (!(v->uv_flags & VIFF_TUNNEL)) {
- v->uv_flags |= VIFF_QUERIER;
- for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
- if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) {
- v->uv_flags &= ~VIFF_QUERIER;
- }
- if (!(n->al_flags & NF_LEAF)) {
- v->uv_leaf_timer = 0;
- }
- }
- }
+ if (v->uv_querier &&
+ (v->uv_querier->al_timer += TIMER_INTERVAL) >
+ IGMP_OTHER_QUERIER_PRESENT_INTERVAL) {
+ /*
+ * The current querier has timed out. We must become the
+ * querier.
+ */
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "querier %s timed out",
+ inet_fmt(v->uv_querier->al_addr, s1));
+ free(v->uv_querier);
+ v->uv_querier = NULL;
+ v->uv_flags |= VIFF_QUERIER;
+ send_query(v);
}
}
}
@@ -1281,6 +1584,36 @@ neighbor_info(vifi, addr)
return NULL;
}
+static struct vnflags {
+ int vn_flag;
+ char *vn_name;
+} vifflags[] = {
+ { VIFF_DOWN, "down" },
+ { VIFF_DISABLED, "disabled" },
+ { VIFF_QUERIER, "querier" },
+ { VIFF_ONEWAY, "one-way" },
+ { VIFF_LEAF, "leaf" },
+ { VIFF_IGMPV1, "IGMPv1" },
+ { VIFF_REXMIT_PRUNES, "rexmit_prunes" },
+ { VIFF_PASSIVE, "passive" },
+ { VIFF_ALLOW_NONPRUNERS,"allow_nonpruners" },
+ { VIFF_NOFLOOD, "noflood" },
+ { VIFF_NOTRANSIT, "notransit" },
+ { VIFF_BLASTER, "blaster" },
+ { VIFF_FORCE_LEAF, "force_leaf" },
+ { VIFF_OTUNNEL, "old-tunnel" },
+};
+
+static struct vnflags nbrflags[] = {
+ { NBRF_LEAF, "leaf" },
+ { NBRF_GENID, "have-genid" },
+ { NBRF_WAITING, "waiting" },
+ { NBRF_ONEWAY, "one-way" },
+ { NBRF_TOOOLD, "too old" },
+ { NBRF_TOOMANYROUTES, "too many routes" },
+ { NBRF_NOTPRUNING, "not pruning?" },
+};
+
/*
* Print the contents of the uvifs array on file 'fp'.
*/
@@ -1292,8 +1625,13 @@ dump_vifs(fp)
register struct uvif *v;
register struct listaddr *a;
register struct phaddr *p;
+ register struct vif_acl *acl;
+ int i;
struct sioc_vif_req v_req;
+ time_t now;
+ char *label;
+ time(&now);
fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors);
if (vifs_with_neighbors == 1)
@@ -1321,65 +1659,106 @@ dump_vifs(fp)
v->uv_threshold,
v->uv_rate_limit);
- if (v->uv_flags & VIFF_ONEWAY) fprintf(fp, " one-way");
- if (v->uv_flags & VIFF_DOWN) fprintf(fp, " down");
- if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled");
- if (v->uv_flags & VIFF_QUERIER) fprintf(fp, " querier");
- if (v->uv_flags & VIFF_SRCRT) fprintf(fp, " src-rt");
- if (v->uv_flags & VIFF_LEAF) fprintf(fp, " leaf");
- if (v->uv_flags & VIFF_IGMPV1) fprintf(fp, " IGMPv1");
+ for (i = 0; i < sizeof(vifflags) / sizeof(struct vnflags); i++)
+ if (v->uv_flags & vifflags[i].vn_flag)
+ fprintf(fp, " %s", vifflags[i].vn_name);
+
fprintf(fp, "\n");
+ /*
+ fprintf(fp, " #routes: %d\n", v->uv_nroutes);
+ */
+ if (v->uv_admetric != 0)
+ fprintf(fp, " advert-metric %2u\n",
+ v->uv_admetric);
- if (v->uv_addrs != NULL) {
- fprintf(fp, " alternate subnets: %s\n",
- inet_fmts(v->uv_addrs->pa_subnet, v->uv_addrs->pa_subnetmask, s1));
- for (p = v->uv_addrs->pa_next; p; p = p->pa_next) {
- fprintf(fp, " %s\n",
+ label = "alternate subnets:";
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ fprintf(fp, " %18s %s\n", label,
inet_fmts(p->pa_subnet, p->pa_subnetmask, s1));
- }
+ label = "";
}
- if (v->uv_neighbors != NULL) {
- fprintf(fp, " peers: %s (%d.%d) (0x%x)\n",
- inet_fmt(v->uv_neighbors->al_addr, s1),
- v->uv_neighbors->al_pv, v->uv_neighbors->al_mv,
- v->uv_neighbors->al_flags);
- for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) {
- fprintf(fp, " %s (%d.%d) (0x%x)\n",
- inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
- a->al_flags);
- }
+ label = "peers:";
+ for (a = v->uv_neighbors; a != NULL; a = a->al_next) {
+ fprintf(fp, " %6s %s (%d.%d) [%d]",
+ label, inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
+ a->al_index);
+ for (i = 0; i < sizeof(nbrflags) / sizeof(struct vnflags); i++)
+ if (a->al_flags & nbrflags[i].vn_flag)
+ fprintf(fp, " %s", nbrflags[i].vn_name);
+ fprintf(fp, " up %s\n", scaletime(now - a->al_ctime));
+ /*fprintf(fp, " #routes %d\n", a->al_nroutes);*/
+ label = "";
}
- if (v->uv_groups != NULL) {
- fprintf(fp, " groups: %-15s\n",
- inet_fmt(v->uv_groups->al_addr, s1));
- for (a = v->uv_groups->al_next; a != NULL; a = a->al_next) {
- fprintf(fp, " %-15s\n",
- inet_fmt(a->al_addr, s1));
- }
+ label = "group host (time left):";
+ for (a = v->uv_groups; a != NULL; a = a->al_next) {
+ fprintf(fp, " %23s %-15s %-15s (%s)\n",
+ label,
+ inet_fmt(a->al_addr, s1),
+ inet_fmt(a->al_reporter, s2),
+ scaletime(timer_leftTimer(a->al_timerid)));
+ label = "";
}
- if (v->uv_acl != NULL) {
- struct vif_acl *acl;
-
- fprintf(fp, " boundaries: %-18s\n",
- inet_fmts(v->uv_acl->acl_addr, v->uv_acl->acl_mask, s1));
- for (acl = v->uv_acl->acl_next; acl != NULL; acl = acl->acl_next) {
- fprintf(fp, " : %-18s\n",
+ label = "boundaries:";
+ for (acl = v->uv_acl; acl != NULL; acl = acl->acl_next) {
+ fprintf(fp, " %11s %-18s\n", label,
inet_fmts(acl->acl_addr, acl->acl_mask, s1));
- }
+ label = "";
}
- v_req.vifi = vifi;
- if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
- log(LOG_WARNING, 0,
- "SIOCGETVIFCNT fails");
+ if (v->uv_filter) {
+ struct vf_element *vfe;
+ char lbuf[100];
+
+ sprintf(lbuf, "%5s %7s filter:",
+ v->uv_filter->vf_flags & VFF_BIDIR ? "bidir"
+ : " ",
+ v->uv_filter->vf_type == VFT_ACCEPT ? "accept"
+ : "deny");
+ label = lbuf;
+ for (vfe = v->uv_filter->vf_filter;
+ vfe != NULL; vfe = vfe->vfe_next) {
+ fprintf(fp, " %23s %-18s%s\n",
+ label,
+ inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s1),
+ vfe->vfe_flags & VFEF_EXACT ? " (exact)" : "");
+ label = "";
+ }
}
- else {
- fprintf(fp, " pkts in : %ld\n",
- v_req.icount);
- fprintf(fp, " pkts out: %ld\n",
- v_req.ocount);
+ if (!(v->uv_flags & (VIFF_TUNNEL|VIFF_DOWN|VIFF_DISABLED))) {
+ fprintf(fp, " IGMP querier: ");
+ if (v->uv_querier == NULL)
+ if (v->uv_flags & VIFF_QUERIER)
+ fprintf(fp, "%-18s (this system)\n",
+ inet_fmt(v->uv_lcl_addr, s1));
+ else
+ fprintf(fp, "NONE - querier election failure?\n");
+ else
+ fprintf(fp, "%-18s up %s last heard %s ago\n",
+ inet_fmt(v->uv_querier->al_addr, s1),
+ scaletime(now - v->uv_querier->al_ctime),
+ scaletime(v->uv_querier->al_timer));
}
+ if (v->uv_flags & VIFF_BLASTER)
+ fprintf(fp, " blasterbuf size: %dk\n",
+ v->uv_blasterlen / 1024);
+ fprintf(fp, " Nbr bitmaps: 0x%08lx%08lx\n",/*XXX*/
+ v->uv_nbrmap.hi, v->uv_nbrmap.lo);
+ if (v->uv_prune_lifetime != 0)
+ fprintf(fp, " Prune Lifetime: %d seconds\n",
+ v->uv_prune_lifetime);
+
+ v_req.vifi = vifi;
+ if (did_final_init)
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
+ log(LOG_WARNING, errno,
+ "SIOCGETVIFCNT fails on vif %d", vifi);
+ } else {
+ fprintf(fp, " pkts/bytes in : %lu/%lu\n",
+ v_req.icount, v_req.ibytes);
+ fprintf(fp, " pkts/bytes out: %lu/%lu\n",
+ v_req.ocount, v_req.obytes);
+ }
fprintf(fp, "\n");
}
fprintf(fp, "\n");
@@ -1432,7 +1811,7 @@ SetTimer(vifi, g)
cbk = (cbk_t *) malloc(sizeof(cbk_t));
cbk->g = g;
cbk->vifi = vifi;
- return timer_setTimer(g->al_timer, (cfunc_t)DelVif, (void *)cbk);
+ return timer_setTimer(g->al_timer, DelVif, cbk);
}
/*
@@ -1457,7 +1836,7 @@ SendQuery(arg)
register struct uvif *v = &uvifs[cbk->vifi];
send_igmp(v->uv_lcl_addr, cbk->g->al_addr,
- IGMP_HOST_MEMBERSHIP_QUERY,
+ IGMP_MEMBERSHIP_QUERY,
cbk->q_time, cbk->g->al_addr, 0);
cbk->g->al_query = 0;
free(cbk);
@@ -1478,5 +1857,5 @@ SetQueryTimer(g, vifi, to_expire, q_time)
cbk->g = g;
cbk->q_time = q_time;
cbk->vifi = vifi;
- return timer_setTimer(to_expire, (cfunc_t)SendQuery, (void *)cbk);
+ return timer_setTimer(to_expire, SendQuery, cbk);
}
diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h
index 94f5f7a9ee7b..ceb6d8c5189d 100644
--- a/usr.sbin/mrouted/vif.h
+++ b/usr.sbin/mrouted/vif.h
@@ -7,9 +7,91 @@
* Leland Stanford Junior University.
*
*
- * $Id: vif.h,v 3.8 1995/11/29 22:36:57 fenner Rel $
+ * vif.h,v 3.8.4.26 1998/01/14 21:21:19 fenner Exp
*/
+
+/*
+ * Bitmap handling functions.
+ * These should be fast but generic. bytes can be slow to zero and compare,
+ * words are hard to make generic. Thus two sets of macros (yuk).
+ */
+
+/*
+ * The VIFM_ functions should migrate out of <netinet/ip_mroute.h>, since
+ * the kernel no longer uses vifbitmaps.
+ */
+#ifndef VIFM_SET
+typedef u_long vifbitmap_t;
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+#endif
+/*
+ * And <netinet/ip_mroute.h> was missing some required functions anyway
+ */
+#ifndef VIFM_SETALL
+#define VIFM_SETALL(m) ((m) = ~0)
+#endif
+#define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n)))
+#define VIFM_ISEMPTY(m) ((m) == 0)
+#define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask))
+#define VIFM_SET_MASK(m, mask) ((m) |= (mask))
+
+/*
+ * Neighbor bitmaps are, for efficiency, implemented as a struct
+ * containing two variables of a native machine type. If you
+ * have a native type that's bigger than a long, define it below.
+ */
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) * 8
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+#define MAXNBRS 2 * NBRBITS
+#define NO_NBR MAXNBRS
+
+#define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \
+ ((m).hi |= (1 << (n - NBRBITS))))
+#define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \
+ ((m).hi &= ~(1 << (n - NBRBITS))))
+#define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \
+ ((m).hi & (1 << ((n) - NBRBITS))))
+#define NBRM_CLRALL(m) ((m).lo = (m).hi = 0)
+#define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi)
+#define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi))
+#define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0))
+#define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi))
+#define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi))
+#define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi))
+#define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi))
+#define NBRM_ISSETALLMASK(m, mask)\
+ ((((m).lo & (mask).lo) == (mask).lo) && \
+ (((m).hi & (mask).hi) == (mask).hi))
+/*
+ * This macro is TRUE if all the subordinates have been pruned, or if
+ * there are no subordinates on this vif.
+ * The arguments is the map of subordinates, the map of neighbors on the
+ * vif, and the map of received prunes.
+ */
+#define SUBS_ARE_PRUNED(sub, vifmask, prunes) \
+ (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \
+ ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi))
+
+struct blastinfo {
+ char * bi_buf; /* Pointer to malloced storage */
+ char * bi_cur; /* The update to process next */
+ char * bi_end; /* The place to put the next update */
+ int bi_len; /* Size of malloced storage */
+ int bi_timer; /* Timer to run process_blaster_report */
+};
+
/*
* User level Virtual Interface structure
*
@@ -18,30 +100,62 @@
* (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
*/
struct uvif {
- u_short uv_flags; /* VIFF_ flags defined below */
+ u_int 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_admetric; /* advertised cost of this vif */
u_char uv_threshold; /* min ttl required to forward on vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
u_int32 uv_lcl_addr; /* local address of this vif */
u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */
+ u_int32 uv_dst_addr; /* destination for DVMRP messages */
u_int32 uv_subnet; /* subnet number (phyints only) */
u_int32 uv_subnetmask; /* subnet mask (phyints only) */
u_int32 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 */
+ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */
+ struct listaddr *uv_querier; /* IGMP querier on vif */
+ int uv_igmpv1_warn;/* To rate-limit IGMPv1 warnings */
+ int uv_prune_lifetime; /* Prune lifetime or 0 for default */
struct vif_acl *uv_acl; /* access control list of groups */
int uv_leaf_timer; /* time until this vif is considrd leaf */
struct phaddr *uv_addrs; /* Additional subnets on this vif */
+ struct vif_filter *uv_filter; /* Route filters on this vif */
+ struct blastinfo uv_blaster; /* Info about route blasters */
+ int uv_nbrup; /* Counter for neighbor up events */
+ int uv_icmp_warn; /* To rate-limit ICMP warnings */
+ u_int uv_nroutes; /* # of routes with this vif as parent */
+ struct ip *uv_encap_hdr; /* Pre-formed header to encapsulate msgs*/
};
+#define uv_blasterbuf uv_blaster.bi_buf
+#define uv_blastercur uv_blaster.bi_cur
+#define uv_blasterend uv_blaster.bi_end
+#define uv_blasterlen uv_blaster.bi_len
+#define uv_blastertimer uv_blaster.bi_timer
+
#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 */
-#define VIFF_LEAF 0x1000 /* all neighbors are leaves */
-#define VIFF_IGMPV1 0x2000 /* Act as an IGMPv1 Router */
+#define VIFF_DOWN 0x000100 /* kernel state of interface */
+#define VIFF_DISABLED 0x000200 /* administratively disabled */
+#define VIFF_QUERIER 0x000400 /* I am the subnet's querier */
+#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */
+#define VIFF_LEAF 0x001000 /* all neighbors are leaves */
+#define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */
+#define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */
+#define VIFF_PASSIVE 0x008000 /* passive tunnel */
+#define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */
+#define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */
+#define VIFF_NOTRANSIT 0x040000 /* don't transit these vifs */
+#define VIFF_BLASTER 0x080000 /* nbr on vif blasts routes */
+#define VIFF_FORCE_LEAF 0x100000 /* ignore nbrs on this vif */
+#define VIFF_OTUNNEL 0x200000 /* DVMRP msgs "beside" tunnel*/
+
+#define AVOID_TRANSIT(v, r) \
+ (((r)->rt_parent != NO_VIF) && \
+ ((r)->rt_gateway != 0) && \
+ (uvifs[(v)].uv_flags & VIFF_NOTRANSIT) && \
+ (uvifs[(r)->rt_parent].uv_flags & VIFF_NOTRANSIT))
struct phaddr {
struct phaddr *pa_next;
@@ -56,23 +170,67 @@ struct vif_acl {
u_int32 acl_mask; /* Group addr. mask */
};
+struct vif_filter {
+ int vf_type;
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+ int vf_flags;
+#define VFF_BIDIR 1
+ struct vf_element *vf_filter;
+};
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ u_int32 vfe_addr;
+ u_int32 vfe_mask;
+ int vfe_flags;
+#define VFEF_EXACT 0x0001
+};
+
struct listaddr {
struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
u_int32 al_addr; /* local group or neighbor address */
u_long al_timer; /* for timing out group or neighbor */
- time_t al_ctime; /* neighbor creation time */
- u_int32 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; /* time since heard old report */
- u_char al_flags; /* flags related to this neighbor */
+ time_t al_ctime; /* entry creation time */
+ union {
+ struct {
+ u_int32 alur_genid; /* generation id for neighbor */
+ u_int alur_nroutes; /* # of routes w/ nbr as parent */
+ u_char alur_pv; /* router protocol version */
+ u_char alur_mv; /* router mrouted version */
+ u_char alur_index; /* neighbor index */
+ } alu_router;
+ struct {
+ u_int32 alug_reporter; /* a host which reported membership */
+ u_long alug_timerid; /* timer for group membership */
+ u_long alug_query; /* timer for repeated leave query */
+ u_char alug_old; /* time since heard old report */
+ } alu_group;
+ } al_alu;
+ u_short al_flags; /* flags related to this neighbor */
};
+#define al_genid al_alu.alu_router.alur_genid
+#define al_nroutes al_alu.alu_router.alur_nroutes
+#define al_pv al_alu.alu_router.alur_pv
+#define al_mv al_alu.alu_router.alur_mv
+#define al_index al_alu.alu_router.alur_index
+#define al_reporter al_alu.alu_group.alug_reporter
+#define al_old al_alu.alu_group.alug_old
+#define al_timerid al_alu.alu_group.alug_timerid
+#define al_query al_alu.alu_group.alug_query
-#define NF_LEAF 0x01 /* This neighbor is a leaf */
-#define NF_PRUNE 0x02 /* This neighbor understands prunes */
-#define NF_GENID 0x04 /* I supply genid & rtrlist in probe*/
-#define NF_MTRACE 0x08 /* I can understand mtrace requests */
+#define NBRF_LEAF 0x0001 /* This neighbor is a leaf */
+#define NBRF_GENID 0x0100 /* I know this neighbor's genid */
+#define NBRF_WAITING 0x0200 /* Waiting for peering to come up */
+#define NBRF_ONEWAY 0x0400 /* One-way peering */
+#define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */
+#define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */
+#define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */
+
+/*
+ * Don't peer with neighbors with any of these flags set
+ */
+#define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \
+ NBRF_TOOMANYROUTES|NBRF_NOTPRUNING)
#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */