summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mrouted/prune.c
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1994-09-08 00:26:13 +0000
committerwollman <wollman@FreeBSD.org>1994-09-08 00:26:13 +0000
commit21876867d877dec1bf967dcd74dcfdd83a3ab69e (patch)
treeac5b66ecd661537969bffb9c43349152a182dd2c /usr.sbin/mrouted/prune.c
downloadFreeBSD-src-21876867d877dec1bf967dcd74dcfdd83a3ab69e.zip
FreeBSD-src-21876867d877dec1bf967dcd74dcfdd83a3ab69e.tar.gz
mrouted from multicast 3.3 distribution
Diffstat (limited to 'usr.sbin/mrouted/prune.c')
-rw-r--r--usr.sbin/mrouted/prune.c1370
1 files changed, 1370 insertions, 0 deletions
diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c
new file mode 100644
index 0000000..04387a1
--- /dev/null
+++ b/usr.sbin/mrouted/prune.c
@@ -0,0 +1,1370 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: prune.c,v 1.4 1994/08/24 23:54:33 thyagara Exp $
+ */
+
+
+#include "defs.h"
+
+extern int cache_lifetime;
+extern int max_prune_lifetime;
+
+/*
+ * dither cache lifetime to obtain a value between x and 2*x
+ */
+#define CACHE_LIFETIME(x) ((x) + (random() % (x)))
+
+#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; \
+ } \
+ }
+
+static struct ktable *kernel_rtable; /* ptr to list of kernel rt entries */
+unsigned int kroutes; /* current number of cache entries */
+
+
+/*
+ * Initialize the kernel table structure
+ */
+void init_ktable()
+{
+ kernel_rtable = NULL;
+ kroutes = 0;
+}
+
+/*
+ * Determine if mcastgrp has a listener on vifi
+ */
+int grplst_mem(vifi, mcastgrp)
+ vifi_t vifi;
+ u_long 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)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Updates the ttl values for each vif.
+ */
+void prun_add_ttls(kt)
+ struct ktable *kt;
+{
+ 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] = NULL;
+ }
+}
+
+/*
+ * checks for scoped multicast addresses
+ */
+#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); \
+ }
+
+int scoped_addr(vifi, addr)
+ vifi_t vifi;
+ u_long 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;
+}
+
+/*
+ * Add a new table entry for (origin, mcastgrp)
+ */
+void add_table_entry(origin, mcastgrp)
+ u_long origin;
+ u_long mcastgrp;
+{
+ struct rtentry *r;
+ struct ktable *kt;
+ int i;
+
+ 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));
+ return;
+ }
+
+ r = determine_route(origin);
+
+ /* 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 */
+
+ kroutes++;
+
+ /* add the new values in */
+ 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;
+ }
+ 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);
+
+ /* 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, mcastgrp))
+ VIFM_SET(i, kt->kt_grpmems);
+ }
+ GET_SCOPE(kt);
+ if (VIFM_ISSET(kt->kt_parent, kt->kt_scope))
+ kt->kt_grpmems = NULL;
+ else
+ kt->kt_grpmems &= ~kt->kt_scope;
+ }
+
+ /* update the kernel_rtable pointer */
+ kt->kt_next = kernel_rtable;
+ kernel_rtable = kt;
+
+ /* update ttls and add entry into kernel */
+ prun_add_ttls(kt);
+ k_add_rg(kt);
+
+ log(LOG_DEBUG, 0, "add entry s:%x g:%x gm:%x",
+ kt->kt_origin, kt->kt_mcastgrp, kt->kt_grpmems);
+
+ /* 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.
+ */
+ if (!kt->kt_grpmems && kt->kt_gateway)
+ send_prune(kt);
+}
+
+/*
+ * An mrouter has gone down and come up on an interface
+ * Forward on that interface immediately
+ */
+void reset_neighbor_state(vifi, addr)
+ vifi_t vifi;
+ u_long 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--;
+ }
+ }
+
+ /*
+ * 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);
+ }
+ 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);
+
+ kt->kt_grpmems &= ~kt->kt_scope;
+ prun_add_ttls(kt);
+ k_add_rg(kt);
+ }
+ }
+}
+
+/*
+ * Delete table entry from the kernel
+ * del_flag determines how many entries to delete
+ */
+void del_table_entry(r, mcastgrp, del_flag)
+ struct rtentry *r;
+ u_long mcastgrp;
+ u_int del_flag;
+{
+ struct mfcctl mc;
+ struct ktable *kt, *prev_kt;
+ struct prunlst *krl;
+
+ 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 %x grp %x",
+ kt->kt_origin, mcastgrp);
+
+ 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);
+ }
+
+ /* free the source mcastgrp entry */
+ prev_kt->kt_next = kt->kt_next;
+ free((char *)kt);
+ kroutes--;
+ kt = prev_kt;
+ }
+ }
+ }
+
+ 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 src %x grp %x",
+ kt->kt_origin, mcastgrp);
+
+ 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);
+ }
+
+ /* free the source mcastgrp entry */
+ prev_kt->kt_next = kt->kt_next;
+ free((char *)kt);
+ kroutes--;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * update kernel table entry when a route entry changes
+ */
+void update_table_entry(r)
+ struct rtentry *r;
+{
+ struct ktable *kt;
+ struct prunlst *krl;
+ 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++;
+ if (kt->kt_children != r->rt_children)
+ changed++;
+ if (kt->kt_parent != r->rt_parent)
+ changed++;
+
+ if (!changed)
+ continue;
+
+ log(LOG_DEBUG, 0, "update entry: s %-15s g %-15s",
+ inet_fmt(kt->kt_origin, s1), inet_fmt(kt->kt_mcastgrp, s2));
+
+ /* 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 = NULL;
+ 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);
+ }
+ }
+}
+
+
+
+/*
+ * set the forwarding flag for all mcastgrps on this vifi
+ */
+void update_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_long mcastgrp;
+{
+ struct ktable *kt;
+
+ log(LOG_DEBUG, 0, "group %x joined at vif %d", mcastgrp, vifi);
+
+ 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 == NULL)
+ continue;
+ prun_add_ttls(kt);
+ k_add_rg(kt);
+ }
+}
+
+/*
+ * reset forwarding flag for all mcastgrps on this vifi
+ */
+void delete_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_long mcastgrp;
+{
+
+ struct ktable *kt;
+
+ log(LOG_DEBUG, 0, "group %x left at vif %d", mcastgrp, vifi);
+
+ for (kt = kernel_rtable; kt; kt = kt->kt_next)
+ if (kt->kt_mcastgrp == mcastgrp) {
+ VIFM_CLR(vifi, kt->kt_grpmems);
+ prun_add_ttls(kt);
+ k_add_rg(kt);
+
+ /*
+ * If there are no more members of this particular group,
+ * send prune upstream
+ */
+ if (kt->kt_grpmems == NULL && kt->kt_gateway)
+ send_prune(kt);
+ }
+}
+
+/*
+ * 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];
+ for (i = 0; i < 4; i++)
+ *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);*/
+}
+
+/*
+ * Takes the prune message received and then strips it to
+ * determine the (src, grp) pair to be pruned.
+ *
+ * Adds the router to the (src, grp) entry then.
+ *
+ * Determines if further packets have to be sent down that vif
+ *
+ * Determines if a corresponding prune message has to be generated
+ */
+void accept_prune(src, dst, p, datalen)
+ u_long src;
+ u_long dst;
+ char *p;
+ int datalen;
+{
+ u_long prun_src;
+ u_long prun_dst;
+ u_long prun_tmr;
+ vifi_t vifi;
+ int i;
+ int stop_sending;
+ struct ktable *kt;
+ struct prunlst *pr_recv;
+ struct prunlst *krl;
+ 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;
+ }
+
+ if (datalen < 0 || datalen > 12)
+ {
+ log(LOG_WARNING, 0,
+ "received non-decipherable prune report 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++;
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_tmr)[i] = *p++;
+
+ kt = find_src_grp(prun_src, prun_dst);
+
+ if (kt == NULL)
+ {
+ log(LOG_WARNING, 0, "prune message received incorrectly");
+ return;
+ }
+
+ if (!VIFM_ISSET(vifi, kt->kt_children))
+ {
+ log(LOG_INFO, 0,
+ "ignoring prune report from non-child %s", inet_fmt(src, s1));
+ 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;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+}
+
+/*
+ * 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)
+ vifi_t vifi;
+ u_long mcastgrp;
+{
+ struct ktable *kt;
+
+ 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 == NULL)
+ continue;
+
+ /* set the flag for graft retransmission */
+ kt->kt_grftsnt = 1;
+
+ /* send graft upwards */
+ send_graft(kt);
+
+ /* 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);
+ }
+}
+
+/* determine the multicast group and src
+ *
+ * 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]
+ */
+void accept_graft(src, dst, p, datalen)
+ u_long src;
+ u_long dst;
+ char *p;
+ int datalen;
+{
+ vifi_t vifi;
+ u_long prun_src;
+ u_long prun_dst;
+ struct ktable *kt;
+ int i;
+ struct prunlst *krl;
+ struct prunlst *prev_krl;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft report from non-neighbor %s", inet_fmt(src, s1));
+ return;
+ }
+
+ if (datalen < 0 || datalen > 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft report 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++;
+
+ 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
+ */
+ 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;
+ }
+
+ /* 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, "send graft ack for src:%x, grp:%x to %x",
+ kt->kt_origin, kt->kt_mcastgrp, dst);
+}
+
+/*
+ * 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;
+
+ 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;
+
+ if (datalen != 0) {
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
+ htonl(MROUTED_LEVEL), datalen);
+ }
+ log(LOG_DEBUG, 0, "send graft for src:%x, grp:%x up to %x",
+ kt->kt_origin, kt->kt_mcastgrp, kt->kt_gateway);
+}
+
+/*
+ * 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
+ * have send a graft again
+ */
+void accept_g_ack(src, dst, p, datalen)
+ u_long src;
+ u_long dst;
+ char *p;
+ int datalen;
+{
+ vifi_t vifi;
+ u_long grft_src;
+ u_long grft_dst;
+ struct ktable *kt;
+ int i;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft ack report from non-neighbor %s", inet_fmt(src, s1));
+ return;
+ }
+
+ if (datalen < 0 || datalen > 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft ack report 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)",
+ 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));
+ return;
+ }
+
+ if (kt->kt_grftsnt)
+ kt->kt_grftsnt = 0;
+}
+
+
+/*
+ * free all prune entries
+ */
+void free_all_prunes()
+{
+ register struct ktable *kt;
+ register struct prunlst *krl;
+
+ while (kernel_rtable != NULL) {
+ kt = kernel_rtable;
+ kernel_rtable = kt->kt_next;
+
+ while (kt->kt_rlist != NULL) {
+ krl = kt->kt_rlist;
+ kt->kt_rlist = krl->rl_next;
+ free((char *)krl);
+ }
+
+ free((char *)kt);
+ kroutes--;
+ }
+}
+
+
+/*
+ * 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()
+{
+ 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);
+
+ 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;
+
+ /* decrement prune timer if need be */
+ if (kt->kt_prsent_timer)
+ kt->kt_prsent_timer -= ROUTE_MAX_REPORT_DELAY;
+
+ /* retransmit graft if graft sent flag is still set */
+ if (kt->kt_grftsnt) {
+ register int y;
+ CHK_GS(kt->kt_grftsnt++, y);
+ if (y)
+ send_graft(kt);
+ }
+
+ /* 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
+ 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) {
+ if ((krl->rl_timer -= ROUTE_MAX_REPORT_DELAY) <= 0) {
+ log(LOG_DEBUG, 0, "forw again s %x g%x on vif %d",
+ kt->kt_origin, kt->kt_mcastgrp, krl->rl_vifi);
+
+ 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);
+ }
+
+ kt->kt_prun_count--;
+ prev_krl->rl_next = krl->rl_next;
+ free((char *)krl);
+ krl = prev_krl;
+
+ if (krl == NULL)
+ break;
+ }
+ }
+
+ if (kt->kt_timer <= 0) {
+ /*
+ * If there are prune entries still outstanding,
+ * update the cache timer otherwise expire entry.
+ */
+ if (kt->kt_rlist) {
+ kt->kt_timer = CACHE_LIFETIME(cache_lifetime);
+ }
+ else {
+ log(LOG_DEBUG, 0, "age route s %x g %x",
+ kt->kt_origin, kt->kt_mcastgrp);
+
+ 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);
+ }
+
+ free((char *)kt);
+ kroutes--;
+ kt = prev_kt;
+ }
+ }
+ }
+}
+
+/*
+ * Print the contents of the routing table on file 'fp'.
+ */
+void dump_cache(fp2)
+ FILE *fp2;
+{
+ register struct ktable *kt;
+ register struct prunlst *krl;
+ register int i;
+ register int count;
+
+ fprintf(fp2,
+ "Multicast Routing Cache Table (%d entries)\n%s", kroutes,
+ " Origin-Subnet Mcast-group CTmr IVif Prcv# Psnt Forwvifs\n");
+
+ for (kt = kernel_rtable, count = 0; kt != NULL; kt = kt->kt_next) {
+
+ fprintf(fp2, " %-15s %-15s",
+ inet_fmts(kt->kt_origin, kt->kt_originmask, s1),
+ inet_fmt(kt->kt_mcastgrp, s2));
+
+ 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' : ' ');
+
+ for (i = 0; i < numvifs; ++i) {
+ if (VIFM_ISSET(i, kt->kt_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');
+ }
+ fprintf(fp2, "\n");
+ count++;
+ }
+}
+
+
+/*
+ * 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;
+ char *data;
+ u_char no;
+ int datalen;
+{
+ u_char type;
+ struct rtentry *rt;
+ struct tr_query *qry;
+ struct tr_resp *resp;
+ struct uvif *v;
+ int vifi;
+ char *p;
+ struct ktable *kt;
+ int rcount;
+
+ struct timeval tp;
+ struct timezone tzp;
+ struct sioc_vif_req v_req;
+ struct sioc_sg_req sg_req;
+
+ /* timestamp the request/response */
+ gettimeofday(&tp, &tzp);
+
+ /*
+ * Check if it is a query or a response
+ */
+ if (datalen == QLEN) {
+ type = QUERY;
+ printf("Traceroute query rcvd\n");
+ }
+ else if ((datalen - QLEN)%RLEN == 0) {
+ type = RESP;
+ printf("Traceroute response rcvd\n");
+ }
+ else {
+ printf("Non decipherable trace request %s", inet_fmt(src, s1));
+ return;
+ }
+
+ qry = (struct tr_query *)data;
+
+ /*
+ * if it is a multicast packet with all reports filled, drop it
+ */
+ if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ printf("multicast packet with reports filled in\n");
+ 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);
+
+ /* determine the routing table entry for this traceroute */
+ rt = determine_route(qry->tr_src);
+
+ /*
+ * 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",
+ 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",
+ 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",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ return;
+ }
+ }
+ else {
+ /*
+ * determine which interface the packet came in on
+ */
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ printf("Wrong interface for packet\n");
+ return;
+ }
+ }
+
+ printf("Sending traceroute response\n");
+
+ /* copy the packet to the sending buffer */
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ bcopy(data, p, datalen);
+
+ p += datalen;
+
+ /*
+ * fill in initial response fields
+ */
+ resp = (struct tr_resp *)p;
+ resp->tr_qarr = ((tp.tv_sec & 0xffff) << 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;
+ resp->tr_outaddr = uvifs[vifi].uv_lcl_addr;
+ resp->tr_fttl = uvifs[vifi].uv_threshold;
+ resp->tr_rflags = TR_NO_ERR;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ v_req.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifout = v_req.ocount;
+
+ /*
+ * fill in scoping & pruning information
+ */
+ kt = find_src_grp(qry->tr_src, group);
+
+ if (kt != NULL) {
+ 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;
+
+ if (VIFM_ISSET(vifi, kt->kt_scope))
+ resp->tr_rflags = TR_SCOPED;
+ else if (kt->kt_prsent_timer)
+ resp->tr_rflags = TR_PRUNED;
+ }
+
+ /*
+ * if no rte exists, set NO_RTE error
+ */
+ if (rt == NULL) {
+ src = dst; /* the dst address of resp. pkt */
+ resp->tr_inaddr = NULL;
+ resp->tr_rflags = TR_NO_RTE;
+ resp->tr_rmtaddr = NULL;
+ }
+ else {
+ /* get # of packets in on interface */
+ v_req.vifi = rt->rt_parent;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifin = v_req.icount;
+
+ 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)) {
+ printf("Destination %s not on forwarding tree for src %s\n",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ resp->tr_rflags = TR_WRONG_IF;
+ }
+ }
+
+ /*
+ * if metric is 1 or no. of reports is 1, send response to requestor
+ * else send to upstream router.
+ */
+ printf("rcount:%d, no:%d\n", rcount, no);
+
+ if ((rcount + 1 == no) || (rt->rt_metric == 1))
+ dst = qry->tr_raddr;
+ else
+ dst = rt->rt_gateway;
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ k_set_ttl(qry->tr_rttl);
+ send_igmp(src, dst,
+ IGMP_MTRACE_RESP, no, group,
+ datalen + RLEN);
+ k_set_ttl(1);
+ }
+ else
+ send_igmp(src, dst,
+ IGMP_MTRACE, no, group,
+ datalen + RLEN);
+ return;
+}
OpenPOWER on IntegriCloud