aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/mrouted/prune.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mrouted/prune.c')
-rw-r--r--usr.sbin/mrouted/prune.c2718
1 files changed, 1806 insertions, 912 deletions
diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c
index 3269288cd80a..96d282e14495 100644
--- a/usr.sbin/mrouted/prune.c
+++ b/usr.sbin/mrouted/prune.c
@@ -7,21 +7,24 @@
* Leland Stanford Junior University.
*
*
- * $Id: prune.c,v 1.4 1995/05/16 00:28:48 jkh Exp $
+ * $Id: prune.c,v 3.5 1995/05/09 01:00:39 fenner Exp $
*/
#include "defs.h"
-#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */
-
extern int cache_lifetime;
extern int max_prune_lifetime;
+extern struct rtentry *routing_table;
/*
* dither cache lifetime to obtain a value between x and 2*x
*/
+#ifdef SYSV
+#define CACHE_LIFETIME(x) ((x) + (lrand48() % (x)))
+#else
#define CACHE_LIFETIME(x) ((x) + (random() % (x)))
+#endif
#define CHK_GS(x, y) { \
switch(x) { \
@@ -37,247 +40,835 @@ extern int max_prune_lifetime;
default: y = 0; \
} \
}
-
-static struct ktable *kernel_rtable; /* ptr to list of kernel rt entries */
+
+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 */
unsigned int kroutes; /* current number of cache entries */
+/****************************************************************************
+ Functions that are local to prune.c
+****************************************************************************/
-/*
- * Initialize the kernel table structure
+/*
+ * Updates the ttl values for each vif.
*/
-void init_ktable()
+static void
+prun_add_ttls(gt)
+ struct gtable *gt;
{
- kernel_rtable = NULL;
- kroutes = 0;
+ struct uvif *v;
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (VIFM_ISSET(vifi, gt->gt_grpmems))
+ gt->gt_ttls[vifi] = v->uv_threshold;
+ else
+ gt->gt_ttls[vifi] = 0;
+ }
}
/*
+ * checks for scoped multicast addresses
+ */
+#define GET_SCOPE(gt) { \
+ register int _i; \
+ if ((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); \
+ }
+
+int
+scoped_addr(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct vif_acl *acl;
+
+ for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next)
+ if ((addr & acl->acl_mask) == acl->acl_addr)
+ return 1;
+
+ return 0;
+}
+
+/*
* Determine if mcastgrp has a listener on vifi
*/
-int grplst_mem(vifi, mcastgrp)
+int
+grplst_mem(vifi, mcastgrp)
vifi_t vifi;
- u_long mcastgrp;
+ u_int32 mcastgrp;
{
register struct listaddr *g;
register struct uvif *v;
-
+
v = &uvifs[vifi];
-
+
for (g = v->uv_groups; g != NULL; g = g->al_next)
- if (mcastgrp == g->al_addr)
+ if (mcastgrp == g->al_addr)
return 1;
-
+
return 0;
}
/*
- * Updates the ttl values for each vif.
+ * Finds the group entry with the specified source and netmask.
+ * If netmask is 0, it uses the route's netmask.
+ *
+ * Returns TRUE if found a match, and the global variable gtp is left
+ * pointing to entry before the found entry.
+ * Returns FALSE if no exact match found, gtp is left pointing to before
+ * the entry in question belongs, or is NULL if the it belongs at the
+ * head of the list.
*/
-void prun_add_ttls(kt)
- struct ktable *kt;
+int
+find_src_grp(src, mask, grp)
+ u_int32 src;
+ u_int32 mask;
+ u_int32 grp;
{
- struct uvif *v;
- vifi_t vifi;
-
- for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
- if (VIFM_ISSET(vifi, kt->kt_grpmems))
- kt->kt_ttls[vifi] = v->uv_threshold;
- else
- kt->kt_ttls[vifi] = 0;
+ struct gtable *gt;
+
+ gtp = NULL;
+ gt = kernel_table;
+ while (gt != NULL) {
+ if (grp == gt->gt_mcastgrp &&
+ (mask ? (gt->gt_route->rt_origin == src &&
+ gt->gt_route->rt_originmask == mask) :
+ ((src & gt->gt_route->rt_originmask) ==
+ gt->gt_route->rt_origin)))
+ return TRUE;
+ if (ntohl(grp) > ntohl(gt->gt_mcastgrp) ||
+ (grp == gt->gt_mcastgrp &&
+ (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) ||
+ (mask == gt->gt_route->rt_originmask &&
+ (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) {
+ gtp = gt;
+ gt = gt->gt_gnext;
+ }
+ else break;
}
+ return FALSE;
}
/*
- * checks for scoped multicast addresses
+ * Check if the neighbor supports pruning
*/
-#define GET_SCOPE(kt) { \
- register int _i; \
- if (((kt)->kt_mcastgrp & 0xff000000) == 0xef000000) \
- for (_i = 0; _i < numvifs; _i++) \
- if (scoped_addr(_i, (kt)->kt_mcastgrp)) \
- VIFM_SET(_i, (kt)->kt_scope); \
- }
+static int
+pruning_neighbor(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
+
+ if (n == NULL)
+ return 0;
+
+ if (n->al_flags & NF_PRUNE)
+ return 1;
-int scoped_addr(vifi, addr)
+ /*
+ * 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);
+}
+
+/*
+ * Can the neighbor in question handle multicast traceroute?
+ */
+static int
+can_mtrace(vifi, addr)
vifi_t vifi;
- u_long addr;
+ u_int32 addr;
{
- struct vif_acl *acl;
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
- for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next)
- if ((addr & acl->acl_mask) == acl->acl_addr)
- return 1;
+ if (n == NULL)
+ return 0;
- return 0;
+ if (n->al_flags & NF_MTRACE)
+ return 1;
+
+ /*
+ * 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);
}
/*
- * Add a new table entry for (origin, mcastgrp)
+ * Returns the prune entry of the router, or NULL if none exists
*/
-void add_table_entry(origin, mcastgrp)
- u_long origin;
- u_long mcastgrp;
+static struct ptable *
+find_prune_entry(vr, pt)
+ u_int32 vr;
+ struct ptable *pt;
{
- struct rtentry *r;
- struct ktable *kt;
+ while (pt) {
+ if (pt->pt_router == vr)
+ return pt;
+ pt = pt->pt_next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Send a prune message to the dominant router for
+ * this source.
+ *
+ * Record an entry that a prune was sent for this group
+ */
+static void
+send_prune(gt)
+ struct gtable *gt;
+{
+ struct ptable *pt;
+ char *p;
int i;
+ int datalen;
+ u_int32 src;
+ u_int32 dst;
+ u_int32 tmp;
+
+ /* 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)
+ return;
- if ((kt = find_src_grp(origin, mcastgrp)) != NULL) {
- log(LOG_DEBUG, 0, "kernel entry exists for (%s %s)",
- inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ /* Don't send a prune to a non-pruning router */
+ if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway))
return;
+
+ /*
+ * sends a prune message to the router upstream.
+ */
+ src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+ dst = gt->gt_route->rt_gateway;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ /*
+ * determine prune lifetime
+ */
+ 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 we have a graft pending, cancel graft retransmission
+ */
+ gt->gt_grftsnt = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ tmp = htonl(gt->gt_prsent_timer);
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(tmp))[i];
+ datalen += 12;
+
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE,
+ htonl(MROUTED_LEVEL), 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),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+}
+
+/*
+ * a prune was sent upstream
+ * so, a graft has to be sent to annul the prune
+ * set up a graft timer so that if an ack is not
+ * heard within that time, another graft request
+ * is sent out.
+ */
+static void
+send_graft(gt)
+ struct gtable *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)
+ return;
+
+ src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+ dst = gt->gt_route->rt_gateway;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ datalen += 8;
+
+ if (datalen != 0) {
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
+ htonl(MROUTED_LEVEL), datalen);
}
+ 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),
+ inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent);
+}
- r = determine_route(origin);
+/*
+ * Send an ack that a graft was received
+ */
+void
+send_graft_ack(src, dst, origin, grp)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 origin;
+ u_int32 grp;
+{
+ register char *p;
+ register int i;
+ int datalen;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(grp))[i];
+ datalen += 8;
+
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
+ htonl(MROUTED_LEVEL), 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));
+}
+
+/*
+ * Update the kernel cache with all the routes hanging off the group entry
+ */
+static void
+update_kernel(g)
+ struct gtable *g;
+{
+ struct stable *st;
- /* allocate space for the new entry */
- kt = (struct ktable *)malloc(sizeof(struct ktable));
- if (kt == NULL)
- log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+ for (st = g->gt_srctbl; st; st = st->st_next)
+ k_add_rg(st->st_origin, g);
+}
- kroutes++;
+/****************************************************************************
+ Functions that are used externally
+****************************************************************************/
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Find a specific group entry in the group table
+ */
+struct gtable *
+find_grp(grp)
+ u_long grp;
+{
+ struct gtable *gt;
+
+ for (gt = kernel_table; gt; gt = gt->gt_gnext) {
+ if (ntohl(grp) < ntohl(gt->gt_mcastgrp))
+ break;
+ if (gt->gt_mcastgrp == grp)
+ return gt;
+ }
+ return NULL;
+}
+
+/*
+ * Given a group entry and source, find the corresponding source table
+ * entry
+ */
+struct stable *
+find_grp_src(gt, src)
+ struct gtable *gt;
+ u_long src;
+{
+ struct stable *st;
+ u_long grp = gt->gt_mcastgrp;
+ struct gtable *gtcurr;
+
+ for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) {
+ for (st = gtcurr->gt_srctbl; st; st = st->st_next)
+ if (st->st_origin == src)
+ return st;
+ }
+ return NULL;
+}
+
+/*
+ * Find next entry > specification
+ */
+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;
+{
+ struct gtable *gt, *gbest = NULL;
+ struct stable *st, *sbest = NULL;
+
+ /* Find first group entry >= grp spec */
+ (*gtpp) = kernel_table;
+ while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp))
+ (*gtpp)=(*gtpp)->gt_gnext;
+ if (!(*gtpp))
+ return 0; /* no more groups */
+
+ for (gt = kernel_table; gt; gt=gt->gt_gnext) {
+ /* Since grps are ordered, we can stop when group changes from gbest */
+ if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp)
+ break;
+ for (st = gt->gt_srctbl; st; st=st->st_next) {
+
+ /* Among those entries > spec, find "lowest" one */
+ if (((ntohl(gt->gt_mcastgrp)> ntohl(grp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)> ntohl(src))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask)))
+ && (!gbest
+ || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp)
+ && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) {
+ gbest = gt;
+ sbest = st;
+ }
+ }
+ }
+ (*gtpp) = gbest;
+ (*stpp) = sbest;
+ return (*gtpp)!=0;
+}
- /* add the new values in */
+/*
+ * Ensure that sg contains current information for the given group,source.
+ * This is fetched from the kernel as a unit so that counts for the entry
+ * are consistent, i.e. packet and byte counts for the same entry are
+ * read at the same time.
+ */
+void
+refresh_sg(sg, gt, st)
+ struct sioc_sg_req *sg;
+ struct gtable *gt;
+ struct stable *st;
+{
+ static int lastq = -1;
+
+ if (quantum != lastq || sg->src.s_addr!=st->st_origin
+ || sg->grp.s_addr!=gt->gt_mcastgrp) {
+ lastq = quantum;
+ sg->src.s_addr = st->st_origin;
+ sg->grp.s_addr = gt->gt_mcastgrp;
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)sg);
+ }
+}
+
+/*
+ * 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
+ */
+int
+next_child(gtpp, stpp, grp, src, mask, vifi)
+ struct gtable **gtpp;
+ struct stable **stpp;
+ u_long grp;
+ u_long src;
+ u_long 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))
+ || !((*stpp) = find_grp_src((*gtpp),src)))
+ if (!next_grp_src_mask(gtpp, stpp, grp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*gtpp)->gt_route->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*gtpp)->gt_route->rt_children))
+ return 1;
+ *vifi = 0;
+ } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp,
+ (*stpp)->st_origin, 0xFFFFFFFF) );
+
+ return 0;
+}
+#endif /* SNMP */
+
+/*
+ * Initialize the kernel table structure
+ */
+void
+init_ktable()
+{
+ kernel_table = NULL;
+ kernel_no_route = NULL;
+ kroutes = 0;
+}
+
+/*
+ * Add a new table entry for (origin, mcastgrp)
+ */
+void
+add_table_entry(origin, mcastgrp)
+ u_int32 origin;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *gt,**gtnp,*prev_gt;
+ struct stable *st,**stnp;
+ int i;
+
+ r = determine_route(origin);
+ prev_gt = NULL;
if (r == NULL) {
- kt->kt_origin = origin;
- kt->kt_mcastgrp = mcastgrp;
- kt->kt_originmask = 0xffffffff;
- kt->kt_parent = NO_VIF;
- kt->kt_gateway = 0;
- kt->kt_children = 0;
- kt->kt_leaves = 0;
- kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
- kt->kt_grpmems = 0;
- kt->kt_rlist = NULL;
- kt->kt_prsent_timer = 0;
- kt->kt_grftsnt = 0;
- kt->kt_prun_count = 0;
- kt->kt_scope = 0;
+ /*
+ * Look for it on the no_route table; if it is found then
+ * it will be detected as a duplicate below.
+ */
+ for (gt = kernel_no_route; gt; gt = gt->gt_next)
+ if (mcastgrp == gt->gt_mcastgrp &&
+ gt->gt_srctbl && gt->gt_srctbl->st_origin == origin)
+ break;
+ gtnp = &kernel_no_route;
+ } else {
+ gtnp = &r->rt_groups;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_mcastgrp >= mcastgrp)
+ break;
+ gtnp = &gt->gt_next;
+ prev_gt = gt;
+ }
}
- else {
- kt->kt_origin = r->rt_origin;
- kt->kt_mcastgrp = mcastgrp;
- kt->kt_originmask = r->rt_originmask;
- kt->kt_parent = r->rt_parent;
- kt->kt_gateway = r->rt_gateway;
- kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
- kt->kt_grpmems = 0;
- kt->kt_rlist = NULL;
- kt->kt_prsent_timer = 0;
- kt->kt_grftsnt = 0;
- kt->kt_prun_count = 0;
- kt->kt_scope = 0;
-
- VIFM_COPY(r->rt_children, kt->kt_children);
- VIFM_COPY(r->rt_leaves, kt->kt_leaves);
+ if (gt == NULL || gt->gt_mcastgrp != mcastgrp) {
+ gt = (struct gtable *)malloc(sizeof(struct gtable));
+ if (gt == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ 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_rsrr_cache = NULL;
+
+ if (r != NULL) {
/* obtain the multicast group membership list */
for (i = 0; i < numvifs; i++) {
- if (VIFM_ISSET(i, kt->kt_children) &&
- !(VIFM_ISSET(i, kt->kt_leaves)))
- VIFM_SET(i, kt->kt_grpmems);
+ if (VIFM_ISSET(i, r->rt_children) &&
+ !(VIFM_ISSET(i, r->rt_leaves)))
+ VIFM_SET(i, gt->gt_grpmems);
- if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, mcastgrp))
- VIFM_SET(i, kt->kt_grpmems);
+ if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
+ VIFM_SET(i, gt->gt_grpmems);
}
- GET_SCOPE(kt);
- if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) {
- kt->kt_grpmems = 0;
- kt->kt_scope = -1; /* make sure we don't forward */
+ 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;
+ }
+
+ /* update ttls */
+ prun_add_ttls(gt);
+
+ gt->gt_next = *gtnp;
+ *gtnp = gt;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt;
+ gt->gt_prev = prev_gt;
+
+ if (r) {
+ if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) {
+ struct gtable *g;
+
+ 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),
+ r, g->gt_route);
+ } else {
+ if (gtp) {
+ gt->gt_gnext = gtp->gt_gnext;
+ gt->gt_gprev = gtp;
+ gtp->gt_gnext = gt;
+ } else {
+ gt->gt_gnext = kernel_table;
+ gt->gt_gprev = NULL;
+ kernel_table = gt;
+ }
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt;
+ }
+ } else {
+ gt->gt_gnext = gt->gt_prev = NULL;
}
- else
- kt->kt_grpmems &= ~kt->kt_scope;
}
- /* update the kernel_rtable pointer */
- kt->kt_next = kernel_rtable;
- kernel_rtable = kt;
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ if (ntohl(st->st_origin) >= ntohl(origin))
+ break;
+ stnp = &st->st_next;
+ }
- /* update ttls and add entry into kernel */
- prun_add_ttls(kt);
- k_add_rg(kt);
+ if (st == NULL || st->st_origin != origin) {
+ st = (struct stable *)malloc(sizeof(struct stable));
+ if (st == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ st->st_origin = origin;
+ st->st_pktcnt = 0;
+ st->st_next = *stnp;
+ *stnp = st;
+ } else {
+ log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ return;
+ }
- log(LOG_DEBUG, 0, "add entry (%s %s) vif-list:%x",
- inet_fmt(kt->kt_origin, s1),
- inet_fmt(kt->kt_mcastgrp, s2),
- kt->kt_grpmems);
+ kroutes++;
+ k_add_rg(origin, gt);
+ 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.
- * One thing to do is to check if parent vif is the source
- * and not send a prune to that.
+ * mark this src-grp as a prune candidate.
*/
- if (!kt->kt_grpmems && kt->kt_gateway)
- send_prune(kt);
+ if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway)
+ send_prune(gt);
}
/*
* An mrouter has gone down and come up on an interface
* Forward on that interface immediately
*/
-void reset_neighbor_state(vifi, addr)
+void
+reset_neighbor_state(vifi, addr)
vifi_t vifi;
- u_long addr;
+ u_int32 addr;
{
- struct ktable *prev_kt, *kt;
- struct prunlst *prev_krl, *krl;
-
- /* Check each src-grp entry to see if it was pruned on that interface
- If so, forward on that interface */
- for (prev_kt = (struct ktable *)&kernel_rtable,
- kt = kernel_rtable; kt;
- prev_kt = kt, kt = kt->kt_next) {
- for (prev_krl = (struct prunlst *)&kt->kt_rlist,
- krl = prev_krl->rl_next;
- krl;
- prev_krl = krl, krl = krl->rl_next) {
- if (krl->rl_router == addr) {
- prev_krl->rl_next = krl->rl_next;
- free(krl);
- krl = prev_krl;
- kt->kt_prun_count--;
- }
- }
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt, *prev_pt;
+ struct stable *st, *prev_st;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ r = g->gt_route;
/*
* If neighbor was the parent, remove the prune sent state
* Don't send any grafts upstream.
*/
- if (vifi == kt->kt_parent) {
- k_del_rg(kt);
- prev_kt->kt_next = kt->kt_next;
- while (krl = kt->kt_rlist) {
- kt->kt_rlist = krl->rl_next;
- free((char *)krl);
+ if (vifi == r->rt_parent) {
+ if (addr == r->rt_gateway) {
+ log(LOG_DEBUG, 0, "reset_neighbor_state del prunes (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ /*
+ * Expire prune, send again on this vif.
+ */
+ VIFM_SET(pt->pt_vifi, g->gt_grpmems);
+ prev_pt = pt;
+ pt = prev_pt->pt_next;
+ free(prev_pt);
+ }
+ g->gt_pruntbl = NULL;
+
+ st = g->gt_srctbl;
+ while (st) {
+ log(LOG_DEBUG, 0, "reset_neighbor_state del sg (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "reset_neighbor_state trying to delete (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ prev_st = st;
+ st = prev_st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+ /*
+ * Keep the group entries themselves around since the
+ * state will likely just come right back, and if not,
+ * the group entries will time out with no kernel entries
+ * and no prune state.
+ */
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ }
+ } 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;
}
- free((char *)kt);
- kt = prev_kt;
- kroutes--;
- continue;
- }
-
- /*
- * Neighbor was not the parent, send grafts to join the groups
- */
- if (kt->kt_prsent_timer) {
- kt->kt_grftsnt = 1;
- send_graft(kt);
- kt->kt_prsent_timer = 0;
- }
-
- if (!VIFM_ISSET(vifi, kt->kt_grpmems)) {
- if (VIFM_ISSET(vifi, kt->kt_children) &&
- !(VIFM_ISSET(vifi, kt->kt_leaves)))
- VIFM_SET(vifi, kt->kt_grpmems);
- if (VIFM_ISSET(vifi, kt->kt_leaves) &&
- grplst_mem(vifi, kt->kt_mcastgrp))
- VIFM_SET(vifi, kt->kt_grpmems);
+ /*
+ * Remove any prunes that this router has sent us.
+ */
+ prev_pt = (struct ptable *)&g->gt_pruntbl;
+ for (pt = g->gt_pruntbl; pt; pt = pt->pt_next) {
+ if (pt->pt_vifi == vifi && pt->pt_router == addr) {
+ prev_pt->pt_next = pt->pt_next;
+ free(pt);
+ } else
+ prev_pt = pt;
+ }
- kt->kt_grpmems &= ~kt->kt_scope;
- prun_add_ttls(kt);
- k_add_rg(kt);
+ /*
+ * 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;
+ prun_add_ttls(g);
+
+ /* Update kernel state */
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ 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),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+ }
}
}
}
@@ -286,63 +877,119 @@ void reset_neighbor_state(vifi, addr)
* Delete table entry from the kernel
* del_flag determines how many entries to delete
*/
-void del_table_entry(r, mcastgrp, del_flag)
+void
+del_table_entry(r, mcastgrp, del_flag)
struct rtentry *r;
- u_long mcastgrp;
+ u_int32 mcastgrp;
u_int del_flag;
{
- struct ktable *kt, *prev_kt;
- struct prunlst *krl;
-
+ struct gtable *g, *prev_g;
+ struct stable *st, *prev_st;
+ struct ptable *pt, *prev_pt;
+
if (del_flag == DEL_ALL_ROUTES) {
- for (prev_kt = (struct ktable *)&kernel_rtable;
- kt = prev_kt->kt_next;
- prev_kt = kt) {
- if ((kt->kt_origin & r->rt_originmask) == r->rt_origin) {
- log(LOG_DEBUG, 0, "delete all rtes %s",
- inet_fmt(kt->kt_origin, s1));
-
- k_del_rg(kt);
-
- /* free prun list entries */
- while (kt->kt_rlist) {
- krl = kt->kt_rlist;
- kt->kt_rlist = krl->rl_next;
- free((char *)krl);
+ g = r->rt_groups;
+ while (g) {
+ 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));
+ 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));
}
-
- /* free the source mcastgrp entry */
- prev_kt->kt_next = kt->kt_next;
- free((char *)kt);
kroutes--;
- kt = prev_kt;
+ prev_st = st;
+ st = st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt->pt_next;
+ free(pt);
+ pt = prev_pt;
}
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+ prev_g = g->gt_next;
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ free(g);
+ g = prev_g;
}
+ r->rt_groups = NULL;
}
-
+
+ /*
+ * Dummy routine - someday this may be needed, so it is just there
+ */
if (del_flag == DEL_RTE_GROUP) {
- for (prev_kt = (struct ktable *)&kernel_rtable;
- (prev_kt) && (kt = prev_kt->kt_next);
- prev_kt = kt) {
- if ((kt->kt_origin & r->rt_originmask) == r->rt_origin &&
- kt->kt_mcastgrp == mcastgrp) {
- log(LOG_DEBUG, 0, "delete (%s, %s)",
- inet_fmt(kt->kt_origin, s1), inet_fmt(mcastgrp, s2));
-
- k_del_rg(kt);
-
- /* free prun list entries */
- while (kt->kt_rlist) {
- krl = kt->kt_rlist;
- kt->kt_rlist = krl->rl_next;
- free((char *)krl);
+ prev_g = (struct gtable *)&r->rt_groups;
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ if (g->gt_mcastgrp == mcastgrp) {
+ 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));
+ 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));
+ }
+ kroutes--;
+ prev_st = st->st_next;
+ free(st);
+ st = prev_st;
}
+ g->gt_srctbl = NULL;
- /* free the source mcastgrp entry */
- prev_kt->kt_next = kt->kt_next;
- free((char *)kt);
- kroutes--;
- break;
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt->pt_next;
+ free(pt);
+ pt = prev_pt;
+ }
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+ if (prev_g != (struct gtable *)&r->rt_groups)
+ g->gt_next->gt_prev = prev_g;
+ else
+ g->gt_next->gt_prev = NULL;
+ prev_g->gt_next = g->gt_next;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ free(g);
+ g = prev_g;
+ } else {
+ prev_g = g;
}
}
}
@@ -351,219 +998,164 @@ void del_table_entry(r, mcastgrp, del_flag)
/*
* update kernel table entry when a route entry changes
*/
-void update_table_entry(r)
+void
+update_table_entry(r)
struct rtentry *r;
{
- struct ktable *kt;
- struct prunlst *krl;
+ struct gtable *g;
+ struct ptable *pt, *prev_pt;
int i;
- int changed;
-
- for (kt = kernel_rtable; kt; kt = kt->kt_next)
- if ((kt->kt_origin & r->rt_originmask)== r->rt_origin) {
- changed = 0;
-
- if (kt->kt_leaves != r->rt_leaves)
- changed |= 0x1;
- if (kt->kt_children != r->rt_children)
- changed |= 0x2;
- if (kt->kt_parent != r->rt_parent)
- changed |= 0x4;
-
- if (!changed)
- continue;
-
- log(LOG_DEBUG, 0, "updating entry: (%s %s) code:%x",
- inet_fmt(kt->kt_origin, s1),
- inet_fmt(kt->kt_mcastgrp, s2), changed);
-
- /* free prun list entries */
- while (kt->kt_rlist) {
- krl = kt->kt_rlist;
- kt->kt_rlist = krl->rl_next;
- free((char *)krl);
- }
-
- kt->kt_parent = r->rt_parent;
- kt->kt_gateway = r->rt_gateway;
- kt->kt_grpmems = 0;
- kt->kt_prun_count = 0;
- VIFM_COPY(r->rt_children, kt->kt_children);
- VIFM_COPY(r->rt_leaves, kt->kt_leaves);
-
- /* obtain the multicast group membership list */
- for (i = 0; i < numvifs; i++) {
- if (VIFM_ISSET(i, kt->kt_children) &&
- !(VIFM_ISSET(i, kt->kt_leaves)))
- VIFM_SET(i, kt->kt_grpmems);
-
- if (VIFM_ISSET(i, kt->kt_leaves) && grplst_mem(i, kt->kt_mcastgrp))
- VIFM_SET(i, kt->kt_grpmems);
- }
- if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) {
- kt->kt_grpmems = 0;
- kt->kt_scope = -1;
- }
- else
- kt->kt_grpmems &= ~kt->kt_scope;
-
- if (kt->kt_grpmems && kt->kt_prsent_timer) {
- kt->kt_grftsnt = 1;
- send_graft(kt);
- kt->kt_prsent_timer = 0;
- }
-
- /* update ttls and add entry into kernel */
- prun_add_ttls(kt);
- k_add_rg(kt);
-
- if (!kt->kt_grpmems && kt->kt_gateway) {
- kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
- send_prune(kt);
- }
+
+ 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;
+
+ /* 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);
+ }
+ 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),
+ g->gt_grpmems);
+
+ if (g->gt_grpmems && g->gt_prsent_timer) {
+ g->gt_grftsnt = 1;
+ send_graft(g);
+ g->gt_prsent_timer = 0;
+ }
+ /* update ttls and add entry into kernel */
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* 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);
+ }
+ }
+}
/*
* set the forwarding flag for all mcastgrps on this vifi
*/
-void update_lclgrp(vifi, mcastgrp)
+void
+update_lclgrp(vifi, mcastgrp)
vifi_t vifi;
- u_long mcastgrp;
+ u_int32 mcastgrp;
{
- struct ktable *kt;
-
- log(LOG_DEBUG, 0, "group %s joined at vif %d",
+ struct rtentry *r;
+ struct gtable *g;
+
+ log(LOG_DEBUG, 0, "group %s joined on vif %d",
inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp &&
+ VIFM_ISSET(vifi, r->rt_children)) {
- for (kt = kernel_rtable; kt; kt = kt->kt_next)
- if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children)) {
- VIFM_SET(vifi, kt->kt_grpmems);
- kt->kt_grpmems &= ~kt->kt_scope;
- if (kt->kt_grpmems == 0)
+ VIFM_SET(vifi, g->gt_grpmems);
+ g->gt_grpmems &= ~g->gt_scope;
+ if (g->gt_grpmems == 0)
continue;
- prun_add_ttls(kt);
- k_add_rg(kt);
+
+ prun_add_ttls(g);
+ log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
}
+ }
}
/*
* reset forwarding flag for all mcastgrps on this vifi
*/
-void delete_lclgrp(vifi, mcastgrp)
+void
+delete_lclgrp(vifi, mcastgrp)
vifi_t vifi;
- u_long mcastgrp;
+ u_int32 mcastgrp;
{
- struct ktable *kt;
-
- log(LOG_DEBUG, 0, "group %s left at vif %d",
+ struct rtentry *r;
+ struct gtable *g;
+
+ log(LOG_DEBUG, 0, "group %s left on vif %d",
inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
- for (kt = kernel_rtable; kt; kt = kt->kt_next)
- if (kt->kt_mcastgrp == mcastgrp) {
- struct listaddr *vr;
+ if (g->gt_mcastgrp == mcastgrp) {
int stop_sending = 1;
- for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
- if (no_entry_exists(vr->al_addr, kt)) {
- stop_sending = 0;
- break;
- }
+ 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) {
- VIFM_CLR(vifi, kt->kt_grpmems);
- prun_add_ttls(kt);
- k_add_rg(kt);
+ VIFM_CLR(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
- /*
- * If there are no more members of this particular group,
- * send prune upstream
- */
- if (kt->kt_grpmems == NULL && kt->kt_gateway)
- send_prune(kt);
- }
+ /*
+ * 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)
+ send_prune(g);
}
+ }
}
-
-/*
- * Check if the neighbor supports pruning
- */
-int pruning_neighbor(vifi, addr)
- vifi_t vifi;
- u_long addr;
-{
- struct listaddr *u;
-
- for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next)
- if ((u->al_addr == addr) && (u->al_pv > 2))
- return 1;
-
- return 0;
-}
-
-/*
- * Send a prune message to the upstream router
- * given by the kt->kt_gateway argument. The origin and
- * multicast group can be determined from the kt
- * structure.
- *
- * Also, record an entry that a prune was sent for this group
- */
-void send_prune(kt)
- struct ktable *kt;
-{
- struct prunlst *krl;
- char *p;
- int i;
- int datalen;
- u_long src;
- u_long dst;
-
- /* Don't process any prunes if router is not pruning */
- if (pruning == 0)
- return;
-
- /* Don't send a prune to a non-pruning router */
- if (!pruning_neighbor(kt->kt_parent, kt->kt_gateway))
- return;
-
- /*
- * sends a prune message to the router upstream.
- */
- src = uvifs[kt->kt_parent].uv_lcl_addr;
- dst = kt->kt_gateway;
-
- p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
- datalen = 0;
-
- /*
- * determine prune lifetime
- */
- kt->kt_prsent_timer = kt->kt_timer;
- for (krl = kt->kt_rlist; krl; krl = krl->rl_next)
- if (krl->rl_timer < kt->kt_prsent_timer)
- kt->kt_prsent_timer = krl->rl_timer;
-
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_origin))[i];
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_mcastgrp))[i];
-#if BYTE_ORDER == BIG_ENDIAN
- for (i = 0; i < 4; i++)
-#endif
-#if BYTE_ORDER == LITTLE_ENDIAN
- for (i = 3; i >= 0; i--)
-#endif
- *p++ = ((char *)&(kt->kt_prsent_timer))[i];
- datalen += 12;
-
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE,
- htonl(MROUTED_LEVEL), datalen);
-
- /* log(LOG_DEBUG, 0, "send prune for src:%x, grp:%x up to %x",
- kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway);*/
}
/*
@@ -576,766 +1168,1031 @@ void send_prune(kt)
*
* Determines if a corresponding prune message has to be generated
*/
-void accept_prune(src, dst, p, datalen)
- u_long src;
- u_long dst;
+void
+accept_prune(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
char *p;
int datalen;
{
- u_long prun_src;
- u_long prun_dst;
- u_long prun_tmr;
+ u_int32 prun_src;
+ u_int32 prun_grp;
+ u_int32 prun_tmr;
vifi_t vifi;
int i;
- int stop_sending;
- struct ktable *kt;
- struct prunlst *pr_recv;
+ 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,
"ignoring prune report from non-neighbor %s",
inet_fmt(src, s1));
return;
}
-
+
/* Check if enough data is present */
- if (datalen < 12) {
- log(LOG_WARNING, 0,
- "non-decipherable prune from %s",
- inet_fmt(src, s1));
- return;
- }
-
+ if (datalen < 12)
+ {
+ log(LOG_WARNING, 0,
+ "non-decipherable prune from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
for (i = 0; i< 4; i++)
((char *)&prun_src)[i] = *p++;
for (i = 0; i< 4; i++)
- ((char *)&prun_dst)[i] = *p++;
-#if BYTE_ORDER == BIG_ENDIAN
+ ((char *)&prun_grp)[i] = *p++;
for (i = 0; i< 4; i++)
-#endif
-#if BYTE_ORDER == LITTLE_ENDIAN
- for (i = 3; i >= 0; i--)
-#endif
((char *)&prun_tmr)[i] = *p++;
+
+ 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);
- kt = find_src_grp(prun_src, prun_dst);
-
- if (kt == NULL)
- {
- log(LOG_WARNING, 0, "prune message received incorrectly");
+ /*
+ * Find the subnet for the prune
+ */
+ if (find_src_grp(prun_src, 0, prun_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ if (!VIFM_ISSET(vifi, r->rt_children)) {
+ log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3));
return;
}
-
- if (!VIFM_ISSET(vifi, kt->kt_children))
- {
- log(LOG_INFO, 0,
- "ignoring prune report from non-child %s", inet_fmt(src, s1));
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3));
return;
}
- if (VIFM_ISSET(vifi, kt->kt_scope)) {
- log(LOG_INFO, 0,
- "ignoring prune report from %s on scoped vif %d",
- inet_fmt(src, s1), vifi);
- return;
- }
- /* check if prune has been received from this source */
- if (!no_entry_exists(src, kt))
- {
- log(LOG_INFO, 0, "duplicate prune from %s", inet_fmt(src, s1));
- 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);
+ }
+ pt->pt_timer = prun_tmr;
+ } else {
+ /* allocate space for the prune structure */
+ pt = (struct ptable *)(malloc(sizeof(struct ptable)));
+ if (pt == NULL)
+ log(LOG_ERR, 0, "pt: ran out of memory");
+
+ pt->pt_vifi = vifi;
+ pt->pt_router = src;
+ pt->pt_timer = prun_tmr;
+
+ pt->pt_next = g->gt_pruntbl;
+ g->gt_pruntbl = pt;
}
- log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s) tmr %d",
- inet_fmt(src, s1), vifi,
- inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3), prun_tmr);
-
- /* allocate space for the prune structure */
- pr_recv = (struct prunlst *)(malloc(sizeof(struct prunlst)));
-
- if (pr_recv == NULL)
- log(LOG_ERR, 0, "pr_recv: ran out of memory");
-
- pr_recv->rl_vifi = vifi;
- pr_recv->rl_router = src;
- pr_recv->rl_timer = prun_tmr;
-
- /*
- * add this prune message to the list of prunes received
- * for this src group pair
- */
- pr_recv->rl_next = kt->kt_rlist;
- kt->kt_rlist = pr_recv;
-
- kt->kt_prun_count++;
- kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
- if (kt->kt_timer < prun_tmr)
- kt->kt_timer = prun_tmr;
-
- /*
- * check if any more packets need to be sent on the
- * vif which sent this message
- */
- for (vr = uvifs[vifi].uv_neighbors, stop_sending = 1;
- vr; vr = vr->al_next)
- if (no_entry_exists(vr->al_addr, kt)) {
- stop_sending = 0;
- break;
+ /* Refresh the group's lifetime */
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (g->gt_timer < prun_tmr)
+ g->gt_timer = prun_tmr;
+
+ /*
+ * 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)) {
+ VIFM_CLR(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
}
- if (stop_sending && !grplst_mem(vifi, prun_dst)) {
- VIFM_CLR(vifi, kt->kt_grpmems);
- prun_add_ttls(kt);
- k_add_rg(kt);
- }
-
- /*
- * check if all the child routers have expressed no interest
- * in this group and if this group does not exist in the
- * interface
- * Send a prune message then upstream
- */
- if(kt->kt_grpmems == NULL && kt->kt_gateway) {
- log(LOG_DEBUG, 0, "snt prun up %d %d", kt->kt_prun_count, rtr_cnt(kt));
- send_prune(kt);
+ /*
+ * check if all the child routers have expressed no interest
+ * in this group and if this group does not exist in the
+ * interface
+ * Send a prune message then upstream
+ */
+ if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
+ send_prune(g);
+ }
+ } else {
+ /*
+ * There is no kernel entry for this group. Therefore, we can
+ * simply ignore the prune, as we are not forwarding this traffic
+ * downstream.
+ */
+ 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),
+ prun_tmr, inet_fmt(src, s3));
+ return;
}
}
/*
- * Returns 1 if router vr is not present in the prunlist of kt
- */
-int no_entry_exists(vr, kt)
- u_long vr;
- struct ktable *kt;
-{
- struct prunlst *krl;
-
- for (krl = kt->kt_rlist; krl; krl = krl->rl_next)
- if (krl->rl_router == vr)
- return 0;
-
- return 1;
-}
-
-/*
- * Finds the entry for the source group pair in the table
- */
-struct ktable *find_src_grp(src, grp)
- u_long src;
- u_long grp;
-{
- struct ktable *kt;
-
- for (kt = kernel_rtable; kt; kt = kt->kt_next)
- if ((kt->kt_origin == (src & kt->kt_originmask)) &&
- (kt->kt_mcastgrp == grp))
- return kt;
-
- return NULL;
-}
-
-/*
- * scans through the neighbor list of this router and then
- * determines the total no. of child routers present
- */
-int rtr_cnt(kt)
- struct ktable *kt;
-{
- int ri;
- int rcount = 0;
- struct listaddr *u;
-
- for (ri = 0; ri < numvifs; ri++)
- if (VIFM_ISSET(ri, kt->kt_children))
- for(u = uvifs[ri].uv_neighbors; u; u = u->al_next)
- rcount++;
-
- return rcount;
-}
-
-/*
* Checks if this mcastgrp is present in the kernel table
* If so and if a prune was sent, it sends a graft upwards
*/
-void chkgrp_graft(vifi, mcastgrp)
+void
+chkgrp_graft(vifi, mcastgrp)
vifi_t vifi;
- u_long mcastgrp;
+ u_int32 mcastgrp;
{
- struct ktable *kt;
+ struct rtentry *r;
+ struct gtable *g;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children))
+ if (g->gt_prsent_timer) {
+ VIFM_SET(vifi, g->gt_grpmems);
- for (kt = kernel_rtable; kt; kt = kt->kt_next)
- if (kt->kt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, kt->kt_children))
- if (kt->kt_prsent_timer) {
- VIFM_SET(vifi, kt->kt_grpmems);
/*
* If the vif that was joined was a scoped vif,
* ignore it ; don't graft back
*/
- kt->kt_grpmems &= ~kt->kt_scope;
- if (kt->kt_grpmems == 0)
+ g->gt_grpmems &= ~g->gt_scope;
+ if (g->gt_grpmems == 0)
continue;
/* set the flag for graft retransmission */
- kt->kt_grftsnt = 1;
-
+ g->gt_grftsnt = 1;
+
/* send graft upwards */
- send_graft(kt);
-
+ send_graft(g);
+
/* reset the prune timer and update cache timer*/
- kt->kt_prsent_timer = 0;
- kt->kt_timer = max_prune_lifetime;
-
- prun_add_ttls(kt);
- k_add_rg(kt);
+ g->gt_prsent_timer = 0;
+ g->gt_timer = max_prune_lifetime;
+
+ log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
}
+ }
}
/* determine the multicast group and src
- *
- * if it does, then determine if a prune was sent
+ *
+ * if it does, then determine if a prune was sent
* upstream.
* if prune sent upstream, send graft upstream and send
* ack downstream.
- *
+ *
* if no prune sent upstream, change the forwarding bit
* for this interface and send ack downstream.
*
- * if no entry exists for this group just ignore the message
- * [this may not be the right thing to do. but lets see what
- * happens for the time being and then we might decide to do
- * a modification to the code depending on the type of behaviour
- * that we see in this]
+ * if no entry exists for this group send ack downstream.
*/
-void accept_graft(src, dst, p, datalen)
- u_long src;
- u_long dst;
+void
+accept_graft(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
char *p;
int datalen;
{
vifi_t vifi;
- u_long prun_src;
- u_long prun_dst;
- struct ktable *kt;
+ u_int32 graft_src;
+ u_int32 graft_grp;
int i;
- struct prunlst *krl;
- struct prunlst *prev_krl;
-
+ struct rtentry *r;
+ 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",
inet_fmt(src, s1));
return;
}
-
+
for (i = 0; i< 4; i++)
- ((char *)&prun_src)[i] = *p++;
+ ((char *)&graft_src)[i] = *p++;
for (i = 0; i< 4; i++)
- ((char *)&prun_dst)[i] = *p++;
-
+ ((char *)&graft_grp)[i] = *p++;
+
log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)",
inet_fmt(src, s1), vifi,
- inet_fmt(prun_src, s2), inet_fmt(prun_dst, s3));
-
- kt = find_src_grp(prun_src, prun_dst);
- if (kt == NULL) {
- log(LOG_DEBUG, 0, "incorrect graft received from %s", inet_fmt(src, s1));
- return;
- }
-
- if (VIFM_ISSET(vifi, kt->kt_scope)) {
- log(LOG_INFO, 0,
- "incorrect graft received from %s on scoped vif %d",
- inet_fmt(src, s1), vifi);
- return;
- }
- /* remove prune entry from the list
- * allow forwarding on that vif, make change in the kernel
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3));
+
+ /*
+ * Find the subnet for the graft
*/
- for (prev_krl = (struct prunlst *)&kt->kt_rlist;
- krl = prev_krl->rl_next;
- prev_krl = krl)
- if ((krl->rl_vifi) == vifi && (krl->rl_router == src)) {
- prev_krl->rl_next = krl->rl_next;
- free((char *)krl);
- krl = prev_krl;
-
- kt->kt_prun_count--;
- VIFM_SET(vifi, kt->kt_grpmems);
- prun_add_ttls(kt);
- k_add_rg(kt);
- break;
+ if (find_src_grp(graft_src, 0, graft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(graft_src, s2),
+ inet_fmt(graft_grp, s3));
+ return;
}
- /* send ack downstream */
- send_graft_ack(kt, src);
- kt->kt_timer = max_prune_lifetime;
-
- if (kt->kt_prsent_timer) {
- /* set the flag for graft retransmission */
- kt->kt_grftsnt = 1;
-
- /* send graft upwards */
- send_graft(kt);
-
- /* reset the prune sent timer */
- kt->kt_prsent_timer = 0;
- }
-}
-
-/*
- * Send an ack that a graft was received
- */
-void send_graft_ack(kt, to)
- struct ktable *kt;
- u_long to;
-{
- register char *p;
- register int i;
- int datalen;
- u_long src;
- u_long dst;
-
- src = uvifs[kt->kt_parent].uv_lcl_addr;
- dst = to;
-
- p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
- datalen = 0;
-
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_origin))[i];
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_mcastgrp))[i];
- datalen += 8;
-
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
- htonl(MROUTED_LEVEL), datalen);
-
- log(LOG_DEBUG, 0, "sent graft ack (%s, %s) to %s",
- inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2),
- inet_fmt(dst, s3));
-
-}
-
-/*
- * a prune was sent upstream
- * so, a graft has to be sent to annul the prune
- * set up a graft timer so that if an ack is not
- * heard within that time, another graft request
- * is sent out.
- */
-void send_graft(kt)
- struct ktable *kt;
-{
- register char *p;
- register int i;
- int datalen;
- u_long src;
- u_long dst;
-
- src = uvifs[kt->kt_parent].uv_lcl_addr;
- dst = kt->kt_gateway;
+ ptnp = &g->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) {
+ *ptnp = pt->pt_next;
+ free(pt);
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ break;
+ } else {
+ ptnp = &pt->pt_next;
+ }
+ }
- p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
- datalen = 0;
+ /* send ack downstream */
+ send_graft_ack(dst, src, graft_src, graft_grp);
+ g->gt_timer = max_prune_lifetime;
+
+ if (g->gt_prsent_timer) {
+ /* set the flag for graft retransmission */
+ g->gt_grftsnt = 1;
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_origin))[i];
- for (i = 0; i < 4; i++)
- *p++ = ((char *)&(kt->kt_mcastgrp))[i];
- datalen += 8;
+ /* send graft upwards */
+ send_graft(g);
- if (datalen != 0) {
- send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
- htonl(MROUTED_LEVEL), datalen);
+ /* 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.
+ */
+ send_graft_ack(dst, src, graft_src, graft_grp);
+ 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),
+ inet_fmt(src, s3));
+ return;
}
- log(LOG_DEBUG, 0, "sent graft (%s, %s) to %s",
- inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2),
- inet_fmt(kt->kt_gateway, s3));
}
/*
- * find out which group is involved first of all
+ * find out which group is involved first of all
* then determine if a graft was sent.
* if no graft sent, ignore the message
- * if graft was sent and the ack is from the right
- * source, remove the graft timer so that we don't
+ * if graft was sent and the ack is from the right
+ * source, remove the graft timer so that we don't
* have send a graft again
*/
-void accept_g_ack(src, dst, p, datalen)
- u_long src;
- u_long dst;
+void
+accept_g_ack(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
char *p;
int datalen;
{
+ struct gtable *g;
vifi_t vifi;
- u_long grft_src;
- u_long grft_dst;
- struct ktable *kt;
+ u_int32 grft_src;
+ u_int32 grft_grp;
int i;
-
+
if ((vifi = find_vif(src, dst)) == NO_VIF) {
log(LOG_INFO, 0,
- "ignoring graft ack report from non-neighbor %s",
+ "ignoring graft ack from non-neighbor %s",
inet_fmt(src, s1));
return;
}
-
- if (datalen < 8) {
+
+ if (datalen < 0 || datalen > 8) {
log(LOG_WARNING, 0,
- "received non-decipherable graft ack report from %s",
+ "received non-decipherable graft ack from %s",
inet_fmt(src, s1));
return;
}
-
+
for (i = 0; i< 4; i++)
((char *)&grft_src)[i] = *p++;
for (i = 0; i< 4; i++)
- ((char *)&grft_dst)[i] = *p++;
-
- log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s %s)",
+ ((char *)&grft_grp)[i] = *p++;
+
+ 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_dst, s3));
-
- kt = find_src_grp(grft_src, grft_dst);
-
- if (kt == NULL) {
- log(LOG_WARNING, 0, "received wrong graft ack from %s", inet_fmt(src, s1));
+ inet_fmt(grft_src, s2), inet_fmt(grft_grp, s3));
+
+ /*
+ * Find the subnet for the graft ack
+ */
+ if (find_src_grp(grft_src, 0, grft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ g->gt_grftsnt = 0;
+ } else {
+ log(LOG_WARNING, 0, "%s (%s, %s) from %s",
+ "rcvd graft ack with no kernel entry for",
+ inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2),
+ inet_fmt(src, s3));
return;
}
-
- if (kt->kt_grftsnt)
- kt->kt_grftsnt = 0;
}
/*
- * free all prune entries
+ * free all prune entries and kernel routes
+ * normally, this should inform the kernel that all of its routes
+ * are going away, but this is only called by restart(), which is
+ * about to call MRT_DONE which does that anyway.
*/
-void free_all_prunes()
+void
+free_all_prunes()
{
- register struct ktable *kt;
- register struct prunlst *krl;
+ register struct rtentry *r;
+ register struct gtable *g, *prev_g;
+ register struct stable *s, *prev_s;
+ register struct ptable *p, *prev_p;
+
+ for (r = routing_table; r; r = r->rt_next) {
+ g = r->rt_groups;
+ while (g) {
+ s = g->gt_srctbl;
+ while (s) {
+ prev_s = s->st_next;
+ free(s);
+ s = prev_s;
+ }
- while (kernel_rtable != NULL) {
- kt = kernel_rtable;
- kernel_rtable = kt->kt_next;
+ p = g->gt_pruntbl;
+ while (p) {
+ prev_p = p->pt_next;
+ free(p);
+ p = prev_p;
+ }
- while (kt->kt_rlist != NULL) {
- krl = kt->kt_rlist;
- kt->kt_rlist = krl->rl_next;
- free((char *)krl);
+ prev_g = g->gt_next;
+ free(g);
+ g = prev_g;
}
+ r->rt_groups = NULL;
+ }
+ kernel_table = NULL;
- free((char *)kt);
- kroutes--;
+ g = kernel_no_route;
+ while (g) {
+ if (g->gt_srctbl)
+ free(g->gt_srctbl);
+
+ prev_g = g->gt_next;
+ free(g);
+ g = prev_g;
}
+ kernel_no_route = NULL;
}
+/*
+ * When a new route is created, search
+ * a) The less-specific part of the routing table
+ * b) The route-less kernel table
+ * for sources that the new route might want to handle.
+ *
+ * "Inheriting" these sources might be cleanest, but simply deleting
+ * them is easier, and letting the kernel re-request them.
+ */
+void
+steal_sources(rt)
+ struct rtentry *rt;
+{
+ register struct rtentry *rp;
+ register struct gtable *gt, **gtnp;
+ register struct stable *st, **stnp;
+
+ for (rp = rt->rt_next; rp; rp = rp->rt_next) {
+ if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
+ 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));
+ 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) {
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, 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));
+ }
+ *stnp = st->st_next;
+ kroutes--;
+ free(st);
+ } else {
+ stnp = &st->st_next;
+ }
+ }
+ }
+ }
+}
+
+ gtnp = &kernel_no_route;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask)
+ == rt->rt_origin)) {
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, 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));
+ }
+ kroutes--;
+ free(gt->gt_srctbl);
+ *gtnp = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+ free(gt);
+ } else {
+ gtnp = &gt->gt_next;
+ }
+ }
+}
/*
* Advance the timers on all the cache entries.
* If there are any entries whose timers have expired,
* remove these entries from the kernel cache.
*/
-void age_table_entry()
+void
+age_table_entry()
{
- struct ktable *kt;
- struct ktable *prev_kt;
- struct prunlst *krl;
- struct prunlst *prev_krl;
-
- log(LOG_DEBUG, 0, "kr:%x pr:%x",
- kernel_rtable, (struct ktable *)&kernel_rtable);
+ struct rtentry *r;
+ struct gtable *gt, **gtnptr;
+ struct stable *st, **stnp;
+ struct ptable *pt, **ptnp;
+ struct sioc_sg_req sg_req;
+
+ log(LOG_DEBUG, 0, "ageing entries");
+
+ gtnptr = &kernel_table;
+ while ((gt = *gtnptr) != NULL) {
+ r = gt->gt_route;
- for (prev_kt = (struct ktable *)&kernel_rtable;
- kt = prev_kt->kt_next;
- prev_kt = kt) {
/* advance the timer for the kernel entry */
- kt->kt_timer -= ROUTE_MAX_REPORT_DELAY;
+ gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
+
+ /* decrement prune timer if need be */
+ if (gt->gt_prsent_timer > 0) {
+ gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY;
+ if (gt->gt_prsent_timer <= 0) {
+ log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_prsent_timer = -1;
+ }
+ }
/* retransmit graft if graft sent flag is still set */
- if (kt->kt_grftsnt) {
+ if (gt->gt_grftsnt) {
register int y;
- CHK_GS(kt->kt_grftsnt++, y);
+ CHK_GS(gt->gt_grftsnt++, y);
if (y)
- send_graft(kt);
+ send_graft(gt);
}
- /* delete the entry only if there are no subordinate
- routers
-
- Now, if there are subordinate routers, then, what we
- have to do is to decrement each and every router's prune
- time entry too and decide if we want to forward on
- that link basically
- */
- for (prev_krl = (struct prunlst *)&kt->kt_rlist,
- krl = prev_krl->rl_next; krl;
- prev_krl = krl, krl = krl->rl_next) {
-
- /* decrement prune timer received from downstream routers */
- if ((krl->rl_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) {
- log(LOG_DEBUG, 0, "forw again (%s, %s) on vif %d",
- inet_fmt(kt->kt_origin, s1),
- inet_fmt(kt->kt_mcastgrp, s2),
- krl->rl_vifi);
-
+ /*
+ * Age prunes
+ *
+ * If a prune expires, forward again on that vif.
+ */
+ ptnp = &gt->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) {
+ log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi);
+
/*
- * forwarding now, so entry is not pruned anymore
- * reset the cache timer to a largish value also
+ * No need to send a graft, any prunes that we sent
+ * will expire before any prunes that we have received.
*/
- kt->kt_prsent_timer = 0;
+ if (gt->gt_prsent_timer > 0) {
+ log(LOG_DEBUG, 0, "prune expired with %d left on %s",
+ gt->gt_prsent_timer, "prsent_timer");
+ gt->gt_prsent_timer = 0;
+ }
/* modify the kernel entry to forward packets */
- if (!VIFM_ISSET(krl->rl_vifi, kt->kt_grpmems)) {
- VIFM_SET(krl->rl_vifi, kt->kt_grpmems);
- prun_add_ttls(kt);
- k_add_rg(kt);
+ if (!VIFM_ISSET(pt->pt_vifi, gt->gt_grpmems)) {
+ VIFM_SET(pt->pt_vifi, gt->gt_grpmems);
+ log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems,
+ pt->pt_vifi);
+
+ prun_add_ttls(gt);
+ update_kernel(gt);
+#ifdef RSRR
+ /* Send route change notification to reservation
+ * protocol.
+ */
+ rsrr_cache_send(gt,1);
+#endif /* RSRR */
}
/* remove the router's prune entry and await new one */
- kt->kt_prun_count--;
- prev_krl->rl_next = krl->rl_next;
- free((char *)krl);
- krl = prev_krl;
-
- if (krl == NULL)
- break;
+ *ptnp = pt->pt_next;
+ free(pt);
+ } else {
+ ptnp = &pt->pt_next;
}
}
- if (kt->kt_timer <= 0) {
/*
- * If there are prune entries still outstanding,
- * update the cache timer otherwise expire entry.
+ * If the cache entry has expired, check for downstream prunes.
+ *
+ * If there are downstream prunes, refresh the cache entry's timer.
+ * Otherwise, check for traffic. If no traffic, delete this
+ * entry.
*/
- if (kt->kt_rlist) {
- kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (gt->gt_timer <= 0) {
+ if (gt->gt_pruntbl) {
+ if (gt->gt_prsent_timer == -1)
+ gt->gt_prsent_timer = 0;
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ gtnptr = &gt->gt_gnext;
+ continue;
}
- else {
- log(LOG_DEBUG, 0, "aging entry (%s, %s)",
- inet_fmt(kt->kt_origin, s1),
- inet_fmt(kt->kt_mcastgrp, s2));
-
- k_del_rg(kt);
- prev_kt->kt_next = kt->kt_next;
-
- /* free all the prune list entries */
- krl = kt->kt_rlist;
- while(krl) {
- prev_krl = krl;
- krl = krl->rl_next;
- free((char *)prev_krl);
+
+ /*
+ * If this entry was pruned, but all downstream prunes
+ * have expired, then it is safe to simply delete it.
+ * Otherwise, check for traffic before deleting.
+ */
+ if (gt->gt_prsent_timer == 0) {
+ 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 */
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ if (sg_req.pktcnt == st->st_pktcnt) {
+ *stnp = st->st_next;
+ 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));
+ }
+ kroutes--;
+ free(st);
+ } else {
+ stnp = &st->st_next;
+ }
}
- free((char *)kt);
+ if (gt->gt_srctbl) {
+ /* At least one source in the list still has traffic */
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ gtnptr = &gt->gt_gnext;
+ continue;
+ }
+ }
+
+ log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ /* free all the source entries */
+ while (st = gt->gt_srctbl) {
+ log(LOG_DEBUG, 0,
+ "age_table_entry (P) 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 (P) trying to delete (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
kroutes--;
- kt = prev_kt;
+ gt->gt_srctbl = st->st_next;
+ free(st);
+ }
+
+ /* free all the prune list entries */
+ while (gt->gt_pruntbl) {
+ gt->gt_pruntbl = pt->pt_next;
+ free(pt);
+ }
+
+ if (gt->gt_prev)
+ gt->gt_prev->gt_next = gt->gt_next;
+ else
+ gt->gt_route->rt_groups = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ if (gt->gt_gprev) {
+ gt->gt_gprev->gt_gnext = gt->gt_gnext;
+ gtnptr = &gt->gt_gprev->gt_gnext;
+ } else {
+ kernel_table = gt->gt_gnext;
+ gtnptr = &kernel_table;
}
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt->gt_gprev;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(gt,0);
+ rsrr_cache_clean(gt);
+#endif /* RSRR */
+ free((char *)gt);
+ } else {
+ if (gt->gt_prsent_timer == -1)
+ gt->gt_prsent_timer = 0;
+ gtnptr = &gt->gt_gnext;
+ }
+ }
+
+ /*
+ * When traversing the no_route table, the decision is much easier.
+ * Just delete it if it has timed out.
+ */
+ gtnptr = &kernel_no_route;
+ while ((gt = *gtnptr) != NULL) {
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
+
+ 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));
+ }
+ free(gt->gt_srctbl);
+ }
+ *gtnptr = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ free((char *)gt);
+ } else {
+ gtnptr = &gt->gt_next;
}
}
}
+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", t, s);
+
+ return p;
+}
+
/*
- * Print the contents of the routing table on file 'fp'.
+ * Print the contents of the cache table on file 'fp2'.
*/
-void dump_cache(fp2)
+void
+dump_cache(fp2)
FILE *fp2;
{
- register struct ktable *kt;
- register struct prunlst *krl;
+ register struct rtentry *r;
+ register struct gtable *gt;
+ register struct stable *st;
+ register struct ptable *pt;
register int i;
- register int count;
+ register time_t thyme = time(0);
fprintf(fp2,
"Multicast Routing Cache Table (%d entries)\n%s", kroutes,
- " Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs\n");
+ " Origin Mcast-group CTmr Age Ptmr IVif Forwvifs\n");
+
+ for (gt = kernel_no_route; gt; gt = gt->gt_next) {
+ if (gt->gt_srctbl) {
+ fprintf(fp2, " %-18s %-15s %-4s %-4s - -1\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));
+ fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin, s1));
+ }
+ }
+
+ 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),
+ inet_fmt(gt->gt_mcastgrp, s2));
- for (kt = kernel_rtable, count = 0; kt != NULL; kt = kt->kt_next) {
+ fprintf(fp2, " %-4s", scaletime(gt->gt_timer));
- fprintf(fp2, " %-15s %-15s",
- inet_fmts(kt->kt_origin, kt->kt_originmask, s1),
- inet_fmt(kt->kt_mcastgrp, s2));
+ fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime),
+ gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
+ " -");
- if (VIFM_ISSET(kt->kt_parent, kt->kt_scope)) {
- fprintf(fp2, " %5u %2ub %3u %c ",
- kt->kt_timer, kt->kt_parent, kt->kt_prun_count,
- kt->kt_prsent_timer ? 'P' : ' ');
- fprintf(fp2, "\n");
- continue;
- }
- else
- fprintf(fp2, " %5u %2u %3u %c ",
- kt->kt_timer, kt->kt_parent, kt->kt_prun_count,
- kt->kt_prsent_timer ? 'P' : ' ');
+ fprintf(fp2, "%2u%c%c ", r->rt_parent,
+ gt->gt_prsent_timer ? 'P' : ' ',
+ VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' ');
for (i = 0; i < numvifs; ++i) {
- if (VIFM_ISSET(i, kt->kt_grpmems))
+ if (VIFM_ISSET(i, gt->gt_grpmems))
fprintf(fp2, " %u ", i);
- else if (VIFM_ISSET(i, kt->kt_children) &&
- !VIFM_ISSET(i, kt->kt_leaves) &&
- VIFM_ISSET(i, kt->kt_scope))
- fprintf(fp2, " %u%c", i, 'b');
- else if (VIFM_ISSET(i, kt->kt_children) &&
- !VIFM_ISSET(i, kt->kt_leaves))
- fprintf(fp2, " %u%c", i, 'p');
+ else if (VIFM_ISSET(i, r->rt_children) &&
+ !VIFM_ISSET(i, r->rt_leaves))
+ fprintf(fp2, " %u%c", i,
+ VIFM_ISSET(i, gt->gt_scope) ? 'b' : 'p');
}
fprintf(fp2, "\n");
- count++;
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ fprintf(fp2, ">%s\n", inet_fmt(st->st_origin, s1));
+ }
+#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);
}
+#endif
}
-
-
-/*
- * Checks if there are any routers that can understand traceroute
- * downstream
- */
-int can_forward(vifi)
- vifi_t vifi;
-{
- struct listaddr *u;
-
- for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next)
- if (((u->al_pv > 2) && (u->al_mv > 2)) ||
- (u->al_pv > 3))
- return 1;
-
- return 0;
}
/*
* Traceroute function which returns traceroute replies to the requesting
* router. Also forwards the request to downstream routers.
*/
-void mtrace(src, dst, group, data, no, datalen)
- u_long src;
- u_long dst;
- u_long group;
+void
+accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 group;
char *data;
u_char no;
int datalen;
{
u_char type;
struct rtentry *rt;
+ struct gtable *gt;
struct tr_query *qry;
struct tr_resp *resp;
- struct uvif *v;
int vifi;
char *p;
- struct ktable *kt;
int rcount;
-
+ int errcode = TR_NO_ERR;
+ int resptype;
struct timeval tp;
- struct timezone tzp;
struct sioc_vif_req v_req;
struct sioc_sg_req sg_req;
+ /* Remember qid across invocations */
+ static u_int32 oqid = 0;
+
/* timestamp the request/response */
- gettimeofday(&tp, &tzp);
+ gettimeofday(&tp, 0);
/*
* Check if it is a query or a response
*/
if (datalen == QLEN) {
type = QUERY;
- printf("Traceroute query rcvd\n");
+ log(LOG_DEBUG, 0, "Traceroute query rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
}
- else if ((datalen - QLEN)%RLEN == 0) {
+ else if ((datalen - QLEN) % RLEN == 0) {
type = RESP;
- printf("Traceroute response rcvd\n");
+ log(LOG_DEBUG, 0, "Traceroute response rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ if IN_MULTICAST(ntohl(dst)) {
+ log(LOG_DEBUG, 0, "Dropping multicast response");
+ return;
+ }
}
else {
- printf("Non decipherable trace request %s", inet_fmt(src, s1));
+ log(LOG_WARNING, 0, "%s from %s to %s",
+ "Non decipherable tracer request recieved",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
return;
}
qry = (struct tr_query *)data;
+ if (oqid == qry->tr_qid) {
+ /*
+ * If the multicast router is a member of the group being
+ * queried, and the query is multicasted, then the router can
+ * recieve multiple copies of the same query. If we have already
+ * replied to this traceroute, just ignore it this time.
+ *
+ * This is not a total solution, but since if this fails you
+ * only get N copies, N <= the number of interfaces on the router,
+ * it is not fatal.
+ */
+ log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
+ return;
+ } else
+ oqid = qry->tr_qid;
+
/*
- * if it is a multicast packet with all reports filled, drop it
+ * if it is a packet with all reports filled, drop it
*/
if ((rcount = (datalen - QLEN)/RLEN) == no) {
- printf("multicast packet with reports filled in\n");
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
return;
}
- printf("s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1),
- inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3));
- printf("rttl: %d rd: %s\n", qry->tr_rttl, inet_fmt(qry->tr_raddr, s1));
- printf("rcount:%d\n", rcount);
+ 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", rcount);
/* determine the routing table entry for this traceroute */
rt = determine_route(qry->tr_src);
+ 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));
+ } else
+ log(LOG_DEBUG, 0, "...no route");
/*
- * Query type packet - check if rte exists
+ * Query type packet - check if rte exists
* Check if the query destination is a vif connected to me.
* and if so, whether I should start response back
*/
if (type == QUERY) {
if (rt == NULL) {
- printf("Mcast traceroute: no route entry %s\n",
+ log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s",
inet_fmt(qry->tr_src, s1));
if (IN_MULTICAST(ntohl(dst)))
return;
}
- for (v = uvifs, vifi = 0; vifi < numvifs; ++vifi, ++v)
- if (!(v->uv_flags & VIFF_TUNNEL) &&
- ((qry->tr_dst & v->uv_subnetmask) == v->uv_subnet))
- break;
-
- if (vifi == numvifs) {
- printf("Destination %s not an interface\n",
+ vifi = find_vif(qry->tr_dst, 0);
+
+ if (vifi == NO_VIF) {
+ /* The traceroute destination is not on one of my subnet vifs. */
+ log(LOG_DEBUG, 0, "Destination %s not an interface",
inet_fmt(qry->tr_dst, s1));
- return;
- }
- if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
- printf("Destination %s not on forwarding tree for src %s\n",
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
+ } else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
+ 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));
- return;
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
}
}
else {
/*
* determine which interface the packet came in on
+ * RESP packets travel hop-by-hop so this either traversed
+ * a tunnel or came from a directly attached mrouter.
*/
if ((vifi = find_vif(src, dst)) == NO_VIF) {
- printf("Wrong interface for packet\n");
- return;
+ log(LOG_DEBUG, 0, "Wrong interface for packet");
+ errcode = TR_WRONG_IF;
}
- }
-
- printf("Sending traceroute response\n");
-
+ }
+
+ log(LOG_DEBUG, 0, "Sending traceroute response");
+
/* copy the packet to the sending buffer */
p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
-
+
bcopy(data, p, datalen);
-
+
p += datalen;
+
+ /*
+ * If there is no room to insert our reply, coopt the previous hop
+ * error indication to relay this fact.
+ */
+ if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) {
+ resp = (struct tr_resp *)p - 1;
+ resp->tr_rflags = TR_NO_SPACE;
+ rt = NULL;
+ goto sendit;
+ }
/*
* fill in initial response fields
*/
resp = (struct tr_resp *)p;
- resp->tr_qarr = ((tp.tv_sec + JAN_1970) << 16) +
- ((tp.tv_usec << 10) / 15625);
+ bzero(resp, sizeof(struct tr_resp));
+ datalen += RLEN;
+
+ resp->tr_qarr = ((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec >> 4) & 0xffff);
- resp->tr_vifin = 0; /* default values */
- resp->tr_pktcnt = 0; /* default values */
resp->tr_rproto = PROTO_DVMRP;
- resp->tr_smask = 0;
+ 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;
@@ -1350,18 +2207,35 @@ void mtrace(src, dst, group, data, no, datalen)
/*
* fill in scoping & pruning information
*/
- kt = find_src_grp(qry->tr_src, group);
+ if (rt)
+ for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
+ if (gt->gt_mcastgrp >= group)
+ break;
+ }
+ else
+ gt = NULL;
- if (kt != NULL) {
+ if (gt && gt->gt_mcastgrp == group) {
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 = sg_req.count;
+ resp->tr_pktcnt = sg_req.pktcnt;
- if (VIFM_ISSET(vifi, kt->kt_scope))
+ if (VIFM_ISSET(vifi, gt->gt_scope))
resp->tr_rflags = TR_SCOPED;
- else if (kt->kt_prsent_timer)
+ 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))
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if (scoped_addr(vifi, group))
+ resp->tr_rflags = TR_SCOPED;
+ else if (!VIFM_ISSET(vifi, rt->rt_children))
+ resp->tr_rflags = TR_NO_FWD;
}
/*
@@ -1371,9 +2245,8 @@ void mtrace(src, dst, group, data, no, datalen)
src = dst; /* the dst address of resp. pkt */
resp->tr_inaddr = 0;
resp->tr_rflags = TR_NO_RTE;
- resp->tr_rmtaddr = 0;
- }
- else {
+ resp->tr_rmtaddr = 0;
+ } else {
/* get # of packets in on interface */
v_req.vifi = rt->rt_parent;
if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
@@ -1384,33 +2257,54 @@ void mtrace(src, dst, group, data, no, datalen)
resp->tr_inaddr = src;
resp->tr_rmtaddr = rt->rt_gateway;
if (!VIFM_ISSET(vifi, rt->rt_children)) {
- printf("Destination %s not on forwarding tree for src %s\n",
+ 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;
}
+ if (rt->rt_metric >= UNREACHABLE) {
+ resp->tr_rflags = TR_NO_RTE;
+ /* Hack to send reply directly */
+ rt = NULL;
+ }
}
+sendit:
/*
* if metric is 1 or no. of reports is 1, send response to requestor
- * else send to upstream router.
+ * else send to upstream router. If the upstream router can't handle
+ * mtrace, set an error code and send to requestor anyway.
*/
- printf("rcount:%d, no:%d\n", rcount, no);
+ log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
- if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1))
+ if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) {
+ resptype = IGMP_MTRACE_RESP;
dst = qry->tr_raddr;
- else
- dst = rt->rt_gateway;
+ } else
+ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
+ dst = qry->tr_raddr;
+ resp->tr_rflags = TR_OLD_ROUTER;
+ resptype = IGMP_MTRACE_RESP;
+ } else {
+ dst = rt->rt_gateway;
+ resptype = IGMP_MTRACE;
+ }
+
+ log(LOG_DEBUG, 0, "Sending %s to %s from %s",
+ resptype == IGMP_MTRACE_RESP ? "response" : "request on",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
if (IN_MULTICAST(ntohl(dst))) {
k_set_ttl(qry->tr_rttl);
+ /* Let the kernel pick the source address, since we might have picked
+ * a disabled phyint to multicast on.
+ */
send_igmp(INADDR_ANY, dst,
- IGMP_MTRACE_RESP, no, group,
- datalen + RLEN);
+ resptype, no, group,
+ datalen);
k_set_ttl(1);
- }
- else
+ } else
send_igmp(src, dst,
- IGMP_MTRACE, no, group,
- datalen + RLEN);
+ resptype, no, group,
+ datalen);
return;
}