/* * Print the contents of the routing table on file 'fp'. */ void dump_routes(FILE *fp) { register struct rtentry *r; register vifi_t i; fprintf(fp, "Multicast Routing Table (%u %s)\n%s\n", nroutes, (nroutes == 1) ? "entry" : "entries", " Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs"); for (r = routing_table; r != NULL; r = r->rt_next) { fprintf(fp, " %-18s %-15s ", inet_fmts(r->rt_origin, r->rt_originmask, s1), (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2)); fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ", r->rt_metric); fprintf(fp, " %3u %3u ", r->rt_timer, r->rt_parent); for (i = 0; i < numvifs; ++i) { if (VIFM_ISSET(i, r->rt_children)) { fprintf(fp, " %u%c", i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' '); } } fprintf(fp, "\n"); } fprintf(fp, "\n"); }
/* * A vif has gone down -- expire all routes that have that vif as parent, * and update the children bitmaps in all other route entries to take into * account the failed vif. */ void delete_vif_from_routes(vifi_t vifi) { register struct rtentry *r; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE) { if (vifi == r->rt_parent) { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_timer = ROUTE_EXPIRE_TIME; r->rt_metric = UNREACHABLE; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (VIFM_ISSET(vifi, r->rt_children)) { VIFM_CLR(vifi, r->rt_children); VIFM_CLR(vifi, r->rt_leaves); r->rt_subordinates[vifi] = 0; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } else { r->rt_dominants[vifi] = 0; } } } }
/* * A new vif has come up -- update the children and leaf bitmaps in all route * entries to take that into account. */ void add_vif_to_routes(vifi_t vifi) { register struct rtentry *r; register struct uvif *v; v = &uvifs[vifi]; for (r = routing_table; r != NULL; r = r->rt_next) { if (r->rt_metric != UNREACHABLE && !VIFM_ISSET(vifi, r->rt_children)) { VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; r->rt_subordinates[vifi] = 0; if (v->uv_neighbors == NULL) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; } else { VIFM_CLR(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } update_table_entry(r); } } }
/* * A multicast packet has been received on wrong iif by the kernel. * Check for a matching entry. If there is (S,G) with reset SPTbit and * the packet was received on the iif toward the source, this completes * the switch to the shortest path and triggers (S,G) prune toward the RP * (unless I am the RP). * Otherwise, if the packet's iif is in the oiflist of the routing entry, * trigger an Assert. */ static void process_wrong_iif(struct igmpmsg *igmpctl) { u_int32 source; u_int32 group; vifi_t iif; mrtentry_t *mrtentry_ptr; group = igmpctl->im_dst.s_addr; source = igmpctl->im_src.s_addr; iif = igmpctl->im_vif; /* Don't create routing entries for the LAN scoped addresses */ if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) return; /* Ignore if it comes on register vif. register vif is neither SPT iif, * neither is used to send asserts out. */ if (uvifs[iif].uv_flags & VIFF_REGISTER) return; mrtentry_ptr = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr == NULL) return; /* * TODO: check again! */ if (mrtentry_ptr->flags & MRTF_SG) { if (!(mrtentry_ptr->flags & MRTF_SPT)) { if (mrtentry_ptr->source->incoming == iif) { /* Switch to the Shortest Path */ mrtentry_ptr->flags |= MRTF_SPT; mrtentry_ptr->flags &= ~MRTF_RP; add_kernel_cache(mrtentry_ptr, source, group, MFC_MOVE_FORCE); k_chg_mfc(igmp_socket, source, group, iif, mrtentry_ptr->oifs, mrtentry_ptr->group->rpaddr); FIRE_TIMER(mrtentry_ptr->jp_timer); #ifdef RSRR rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK); #endif /* RSRR */ return; } } } /* Trigger an Assert */ if (VIFM_ISSET(iif, mrtentry_ptr->oifs)) send_pim_assert(source, group, iif, mrtentry_ptr); }
/* * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where * we have source-specific joins/prunes. */ void delete_leaf(vifi_t vifi, u_int32 source __attribute__((unused)), u_int32 group) { mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_srcs; vifbitmap_t new_oifs; vifbitmap_t old_oifs; vifbitmap_t new_leaves; mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (mrtentry_ptr == (mrtentry_t *)NULL) return; if (!VIFM_ISSET(vifi, mrtentry_ptr->leaves)) return; /* This interface wasn't leaf */ calc_oifs(mrtentry_ptr, &old_oifs); VIFM_COPY(mrtentry_ptr->leaves, new_leaves); VIFM_CLR(vifi, new_leaves); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, new_leaves, mrtentry_ptr->asserted_oifs, 0); calc_oifs(mrtentry_ptr, &new_oifs); if ((!VIFM_ISEMPTY(old_oifs)) && VIFM_ISEMPTY(new_oifs)) { /* The result oifs have changed from non-NULL to NULL */ FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call the function? send_pim_join_prune(mrtentry_ptr->upstream->vifi, mrtentry_ptr->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } /* Check all (S,G) entries and clear the inherited "leaf" flag. * TODO: XXX: This won't work for IGMPv3, because there we don't know * whether the (S,G) leaf oif was inherited from the (*,G) entry or * was created by source specific IGMP join. */ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) { VIFM_COPY(mrtentry_srcs->leaves, new_leaves); VIFM_CLR(vifi, new_leaves); change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming, mrtentry_srcs->joined_oifs, mrtentry_srcs->pruned_oifs, new_leaves, mrtentry_srcs->asserted_oifs, 0); } }
/* * Process a route report for a single origin, creating or updating the * corresponding routing table entry if necessary. 'src' is either the * address of a neighboring router from which the report arrived, or zero * to indicate a change of status of one of our own interfaces. */ void update_route(u_int32_t origin, u_int32_t mask, u_int metric, u_int32_t src, vifi_t vifi) { register struct rtentry *r; u_int adj_metric; /* * Compute an adjusted metric, taking into account the cost of the * subnet or tunnel over which the report arrived, and normalizing * all unreachable/poisoned metrics into a single value. */ if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) { logit(LOG_WARNING, 0, "%s reports out-of-range metric %u for origin %s", inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2)); return; } adj_metric = metric + uvifs[vifi].uv_metric; if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE; /* * Look up the reported origin in the routing table. */ if (!find_route(origin, mask)) { /* * Not found. * Don't create a new entry if the report says it's unreachable, * or if the reported origin and mask are invalid. */ if (adj_metric == UNREACHABLE) { return; } if (src != 0 && !inet_valid_subnet(origin, mask)) { logit(LOG_WARNING, 0, "%s reports an invalid origin (%s) and/or mask (%08x)", inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask)); return; } /* * OK, create the new routing entry. 'rtp' will be left pointing * to the new entry. */ create_route(origin, mask); /* * Now "steal away" any sources that belong under this route * by deleting any cache entries they might have created * and allowing the kernel to re-request them. */ steal_sources(rtp); rtp->rt_metric = UNREACHABLE; /* temporary; updated below */ } /* * We now have a routing entry for the reported origin. Update it? */ r = rtp; if (r->rt_metric == UNREACHABLE) { /* * The routing entry is for a formerly-unreachable or new origin. * If the report claims reachability, update the entry to use * the reported route. */ if (adj_metric == UNREACHABLE) return; r->rt_parent = vifi; init_children_and_leaves(r, vifi); r->rt_gateway = src; r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; update_table_entry(r); } else if (src == r->rt_gateway) { /* * The report has come either from the interface directly-connected * to the origin subnet (src and r->rt_gateway both equal zero) or * from the gateway we have chosen as the best first-hop gateway back * towards the origin (src and r->rt_gateway not equal zero). Reset * the route timer and, if the reported metric has changed, update * our entry accordingly. */ r->rt_timer = 0; if (adj_metric == r->rt_metric) return; if (adj_metric == UNREACHABLE) { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_timer = ROUTE_EXPIRE_TIME; } else if (adj_metric < r->rt_metric) { if (init_children_and_leaves(r, vifi)) { update_table_entry(r); } } r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (src == 0 || (r->rt_gateway != 0 && (adj_metric < r->rt_metric || (adj_metric == r->rt_metric && (ntohl(src) < ntohl(r->rt_gateway) || r->rt_timer >= ROUTE_SWITCH_TIME))))) { /* * The report is for an origin we consider reachable; the report * comes either from one of our own interfaces or from a gateway * other than the one we have chosen as the best first-hop gateway * back towards the origin. If the source of the update is one of * our own interfaces, or if the origin is not a directly-connected * subnet and the reported metric for that origin is better than * what our routing entry says, update the entry to use the new * gateway and metric. We also switch gateways if the reported * metric is the same as the one in the route entry and the gateway * associated with the route entry has not been heard from recently, * or if the metric is the same but the reporting gateway has a lower * IP address than the gateway associated with the route entry. * Did you get all that? */ if (r->rt_parent != vifi || adj_metric < r->rt_metric) { /* * XXX Why do we do this if we are just changing the metric? */ r->rt_parent = vifi; if (init_children_and_leaves(r, vifi)) { update_table_entry(r); } } r->rt_gateway = src; r->rt_timer = 0; r->rt_metric = adj_metric; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } else if (vifi != r->rt_parent) { /* * The report came from a vif other than the route's parent vif. * Update the children and leaf info, if necessary. */ if (VIFM_ISSET(vifi, r->rt_children)) { /* * Vif is a child vif for this route. */ if (metric < r->rt_metric || (metric == r->rt_metric && ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) { /* * Neighbor has lower metric to origin (or has same metric * and lower IP address) -- it becomes the dominant router, * and vif is no longer a child for me. */ VIFM_CLR(vifi, r->rt_children); VIFM_CLR(vifi, r->rt_leaves); r->rt_dominants [vifi] = src; r->rt_subordinates[vifi] = 0; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } else if (metric > UNREACHABLE) { /* "poisoned reverse" */ /* * Neighbor considers this vif to be on path to route's * origin; if no subordinate recorded, record this neighbor * as subordinate and clear the leaf flag. */ if (r->rt_subordinates[vifi] == 0) { VIFM_CLR(vifi, r->rt_leaves); r->rt_subordinates[vifi] = src; r->rt_leaf_timers [vifi] = 0; update_table_entry(r); } } else if (src == r->rt_subordinates[vifi]) { /* * Current subordinate no longer considers this vif to be on * path to route's origin; it is no longer a subordinate * router, and we set the leaf confirmation timer to give * us time to hear from other subordinates. */ r->rt_subordinates[vifi] = 0; if (uvifs[vifi].uv_neighbors == NULL || uvifs[vifi].uv_neighbors->al_next == NULL) { VIFM_SET(vifi, r->rt_leaves); update_table_entry(r); } else { r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } } } else if (src == r->rt_dominants[vifi] && (metric > r->rt_metric || (metric == r->rt_metric && ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) { /* * Current dominant no longer has a lower metric to origin * (or same metric and lower IP address); we adopt the vif * as our own child. */ VIFM_SET(vifi, r->rt_children); r->rt_dominants [vifi] = 0; if (metric > UNREACHABLE) { r->rt_subordinates[vifi] = src; } else if (uvifs[vifi].uv_neighbors == NULL || uvifs[vifi].uv_neighbors->al_next == NULL) { VIFM_SET(vifi, r->rt_leaves); } else { r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME; r->rt_flags |= RTF_LEAF_TIMING; } update_table_entry(r); } } }
/* * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where * we have source-specific Join/Prune. */ void add_leaf(vifi_t vifi, u_int32 source __attribute__((unused)), u_int32 group) { mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_srcs; vifbitmap_t old_oifs; vifbitmap_t new_oifs; vifbitmap_t new_leaves; if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) return; /* Don't create routing entries for the LAN scoped addresses */ /* * XXX: only if I am a DR, the IGMP Join should result in creating * a PIM MRT state. * XXX: Each router must know if it has local members, i.e., whether * it is a last-hop router as well. This info is needed so it will * know whether is allowed to initiate a SPT switch by sending * a PIM (S,G) Join to the high datarate source. * However, if a non-DR last-hop router has not received * a PIM Join, it should not create a PIM state, otherwise later * this state may incorrectly trigger PIM joins. * There is a design flow in pimd, so without making major changes * the best we can do is that the non-DR last-hop router will * record the local members only after it receives PIM Join from the DR * (i.e. after the second or third IGMP Join by the local member). * The downside is that a last-hop router may delay the initiation * of the SPT switch. Sigh... */ if (uvifs[vifi].uv_flags & VIFF_DR) mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); else mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (mrtentry_ptr == (mrtentry_t *)NULL) return; IF_DEBUG(DEBUG_MRT) logit(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi, inet_fmt(group, s1, sizeof(s1))); if (VIFM_ISSET(vifi, mrtentry_ptr->leaves)) return; /* Already a leaf */ calc_oifs(mrtentry_ptr, &old_oifs); VIFM_COPY(mrtentry_ptr->leaves, new_leaves); VIFM_SET(vifi, new_leaves); /* Add the leaf */ change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, new_leaves, mrtentry_ptr->asserted_oifs, 0); calc_oifs(mrtentry_ptr, &new_oifs); /* Only if I am the DR for that subnet, eventually initiate a Join */ if (!(uvifs[vifi].uv_flags & VIFF_DR)) return; if ((mrtentry_ptr->flags & MRTF_NEW) || (VIFM_ISEMPTY(old_oifs) && (!VIFM_ISEMPTY(new_oifs)))) { /* A new created entry or the oifs have changed * from NULL to non-NULL. */ mrtentry_ptr->flags &= ~MRTF_NEW; FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call the function below? send_pim_join_prune(mrtentry_ptr->upstream->vifi, mrtentry_ptr->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } /* Check all (S,G) entries and set the inherited "leaf" flag. * TODO: XXX: This won't work for IGMPv3, because there we don't know * whether the (S,G) leaf oif was inherited from the (*,G) entry or * was created by source specific IGMP join. */ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) { VIFM_COPY(mrtentry_srcs->leaves, new_leaves); VIFM_SET(vifi, new_leaves); change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming, mrtentry_srcs->joined_oifs, mrtentry_srcs->pruned_oifs, new_leaves, mrtentry_srcs->asserted_oifs, 0); } }
/* * Traceroute function which returns traceroute replies to the requesting * router. Also forwards the request to downstream routers. */ void accept_mtrace(u_int32 src, u_int32 dst, u_int32 group, char *data, u_int no, int datalen) { u_char type; mrtentry_t *mrt; struct tr_query *qry; struct tr_resp *resp; int vifi; char *p; u_int rcount; int errcode = TR_NO_ERR; int resptype; struct timeval tp; struct sioc_vif_req v_req; #if 0 /* TODO */ struct sioc_sg_req sg_req; #endif /* 0 */ u_int32 parent_address = INADDR_ANY; /* Remember qid across invocations */ static u_int32 oqid = 0; /* timestamp the request/response */ gettimeofday(&tp, 0); /* * Check if it is a query or a response */ if (datalen == QLEN) { type = QUERY; IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); } else if ((datalen - QLEN) % RLEN == 0) { type = RESP; IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); if (IN_MULTICAST(ntohl(dst))) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Dropping multicast response"); return; } } else { logit(LOG_WARNING, 0, "%s from %s to %s", "Non decipherable traceroute request recieved", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return; } qry = (struct tr_query *)data; /* * if it is a packet with all reports filled, drop it */ if ((rcount = (datalen - QLEN)/RLEN) == no) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "packet with all reports filled in"); return; } IF_DEBUG(DEBUG_TRACE) { logit(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), inet_fmt(qry->tr_dst, s3, sizeof(s3))); logit(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl, inet_fmt(qry->tr_raddr, s1, sizeof(s1))); logit(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid); } /* determine the routing table entry for this traceroute */ mrt = find_route(qry->tr_src, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); IF_DEBUG(DEBUG_TRACE) { if (mrt != (mrtentry_t *)NULL) { if (mrt->upstream != (pim_nbr_entry_t *)NULL) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; logit(LOG_DEBUG, 0, "mrt parent vif: %d rtr: %s metric: %d", mrt->incoming, inet_fmt(parent_address, s1, sizeof(s1)), mrt->metric); /* TODO logit(LOG_DEBUG, 0, "mrt origin %s", RT_FMT(rt, s1)); */ } else { logit(LOG_DEBUG, 0, "...no route"); } } /* * 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 (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. */ IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "ignoring duplicate traceroute packet"); return; } if (mrt == (mrtentry_t *)NULL) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s", inet_fmt(qry->tr_src, s1, sizeof(s1))); if (IN_MULTICAST(ntohl(dst))) return; } vifi = find_vif_direct(qry->tr_dst); if (vifi == NO_VIF) { /* The traceroute destination is not on one of my subnet vifs. */ IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Destination %s not an interface", inet_fmt(qry->tr_dst, s1, sizeof(s1))); if (IN_MULTICAST(ntohl(dst))) return; errcode = TR_WRONG_IF; } else if (mrt != (mrtentry_t *)NULL && !VIFM_ISSET(vifi, mrt->oifs)) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1, sizeof(s1)), inet_fmt(qry->tr_src, s2, sizeof(s2))); 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_direct(src)) == NO_VIF) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Wrong interface for packet"); errcode = TR_WRONG_IF; } } /* Now that we've decided to send a response, save the qid */ oqid = qry->tr_qid; IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Sending traceroute response"); /* copy the packet to the sending buffer */ p = igmp_send_buf + IP_IGMP_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) > igmp_send_buf + SEND_BUF_SIZE) { resp = (struct tr_resp *)p - 1; resp->tr_rflags = TR_NO_SPACE; mrt = NULL; goto sendit; } /* * fill in initial response fields */ resp = (struct tr_resp *)p; memset(resp, 0, sizeof(struct tr_resp)); datalen += RLEN; resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) + ((tp.tv_usec << 10) / 15625)); resp->tr_rproto = PROTO_PIM; resp->tr_outaddr = (vifi == NO_VIF) ? dst : uvifs[vifi].uv_lcl_addr; resp->tr_fttl = (vifi == NO_VIF) ? 0 : uvifs[vifi].uv_threshold; resp->tr_rflags = errcode; /* * obtain # of packets out on interface */ v_req.vifi = vifi; if (vifi != NO_VIF && ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifout = htonl(v_req.ocount); else resp->tr_vifout = 0xffffffff; /* * fill in scoping & pruning information */ /* TODO */ #if 0 if (mrt != (mrtentry_t *)NULL) for (gt = rt->rt_groups; gt; gt = gt->gt_next) { if (gt->gt_mcastgrp >= group) break; } else gt = NULL; if (gt && gt->gt_mcastgrp == group) { struct stable *st; for (st = gt->gt_srctbl; st; st = st->st_next) if (qry->tr_src == st->st_origin) break; sg_req.src.s_addr = qry->tr_src; sg_req.grp.s_addr = group; if (st && st->st_ctime != 0 && ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0) resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt); else resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff); if (VIFM_ISSET(vifi, gt->gt_scope)) resp->tr_rflags = TR_SCOPED; else if (gt->gt_prsent_timer) resp->tr_rflags = TR_PRUNED; else if (!VIFM_ISSET(vifi, gt->gt_grpmems)) if (VIFM_ISSET(vifi, rt->rt_children) && NBRM_ISSETMASK(uvifs[vifi].uv_nbrmap, rt->rt_subordinates)) /*XXX*/ 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 (rt && !VIFM_ISSET(vifi, rt->rt_children)) resp->tr_rflags = TR_NO_FWD; } #endif /* 0 */ /* * if no rte exists, set NO_RTE error */ if (mrt == (mrtentry_t *)NULL) { src = dst; /* the dst address of resp. pkt */ resp->tr_inaddr = 0; resp->tr_rflags = TR_NO_RTE; resp->tr_rmtaddr = 0; } else { /* get # of packets in on interface */ v_req.vifi = mrt->incoming; if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0) resp->tr_vifin = htonl(v_req.icount); else resp->tr_vifin = 0xffffffff; /* TODO MASK_TO_VAL(rt->rt_originmask, resp->tr_smask); */ src = uvifs[mrt->incoming].uv_lcl_addr; resp->tr_inaddr = src; if (mrt->upstream != (pim_nbr_entry_t *)NULL) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; resp->tr_rmtaddr = parent_address; if (!VIFM_ISSET(vifi, mrt->oifs)) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s", inet_fmt(qry->tr_dst, s1, sizeof(s1)), inet_fmt(qry->tr_src, s2, sizeof(s2))); resp->tr_rflags = TR_WRONG_IF; } #if 0 if (rt->rt_metric >= UNREACHABLE) { resp->tr_rflags = TR_NO_RTE; /* Hack to send reply directly */ rt = NULL; } #endif /* 0 */ } sendit: /* * if metric is 1 or no. of reports is 1, send response to requestor * else send to upstream router. If the upstream router can't handle * mtrace, set an error code and send to requestor anyway. */ IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no); if ((rcount + 1 == no) || (mrt == NULL) || (mrt->metric == 1)) { resptype = IGMP_MTRACE_RESP; dst = qry->tr_raddr; } else #if 0 /* TODO */ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) { dst = qry->tr_raddr; resp->tr_rflags = TR_OLD_ROUTER; resptype = IGMP_MTRACE_RESP; } else { #endif /* 0 */ if (mrt->upstream != (pim_nbr_entry_t *)NULL) parent_address = mrt->upstream->address; else parent_address = INADDR_ANY; dst = parent_address; resptype = IGMP_MTRACE; #if 0 /* TODO */ } #endif if (IN_MULTICAST(ntohl(dst))) { /* * Send the reply on a known multicast capable vif. * If we don't have one, we can't source any multicasts anyway. */ if (phys_vif != -1) { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Sending reply to %s from %s", inet_fmt(dst, s1, sizeof(s1)), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2, sizeof(s2))); k_set_ttl(igmp_socket, qry->tr_rttl); send_igmp(igmp_send_buf, uvifs[phys_vif].uv_lcl_addr, dst, resptype, no, group, datalen); k_set_ttl(igmp_socket, 1); } else logit(LOG_INFO, 0, "No enabled phyints -- %s", "dropping traceroute reply"); } else { IF_DEBUG(DEBUG_TRACE) logit(LOG_DEBUG, 0, "Sending %s to %s from %s", resptype == IGMP_MTRACE_RESP ? "reply" : "request on", inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2))); send_igmp(igmp_send_buf, src, dst, resptype, no, group, datalen); } }
static void dump_route(FILE *fp, mrtentry_t *r) { vifi_t vifi; char oifs[(sizeof(vifbitmap_t)<<3)+1]; char joined_oifs[(sizeof(vifbitmap_t)<<3)+1]; char pruned_oifs[(sizeof(vifbitmap_t)<<3)+1]; char leaves_oifs[(sizeof(vifbitmap_t)<<3)+1]; char asserted_oifs[(sizeof(vifbitmap_t)<<3)+1]; char incoming_iif[(sizeof(vifbitmap_t)<<3)+1]; for (vifi = 0; vifi < numvifs; vifi++) { oifs[vifi] = VIFM_ISSET(vifi, r->oifs) ? 'o' : '.'; joined_oifs[vifi] = VIFM_ISSET(vifi, r->joined_oifs) ? 'j' : '.'; pruned_oifs[vifi] = VIFM_ISSET(vifi, r->pruned_oifs) ? 'p' : '.'; leaves_oifs[vifi] = VIFM_ISSET(vifi, r->leaves) ? 'l' : '.'; asserted_oifs[vifi] = VIFM_ISSET(vifi, r->asserted_oifs) ? 'a' : '.'; incoming_iif[vifi] = '.'; } oifs[vifi] = 0x0; /* End of string */ joined_oifs[vifi] = 0x0; pruned_oifs[vifi] = 0x0; leaves_oifs[vifi] = 0x0; asserted_oifs[vifi] = 0x0; incoming_iif[vifi] = 0x0; incoming_iif[r->incoming] = 'I'; /* TODO: don't need some of the flags */ if (r->flags & MRTF_SPT) fprintf(fp, " SPT"); if (r->flags & MRTF_WC) fprintf(fp, " WC"); if (r->flags & MRTF_RP) fprintf(fp, " RP"); if (r->flags & MRTF_REGISTER) fprintf(fp, " REG"); if (r->flags & MRTF_IIF_REGISTER) fprintf(fp, " IIF_REG"); if (r->flags & MRTF_NULL_OIF) fprintf(fp, " NULL_OIF"); if (r->flags & MRTF_KERNEL_CACHE) fprintf(fp, " CACHE"); if (r->flags & MRTF_ASSERTED) fprintf(fp, " ASSERTED"); if (r->flags & MRTF_REG_SUPP) fprintf(fp, " REG_SUPP"); if (r->flags & MRTF_SG) fprintf(fp, " SG"); if (r->flags & MRTF_PMBR) fprintf(fp, " PMBR"); fprintf(fp, "\n"); fprintf(fp, "Joined oifs: %-20s\n", joined_oifs); fprintf(fp, "Pruned oifs: %-20s\n", pruned_oifs); fprintf(fp, "Leaves oifs: %-20s\n", leaves_oifs); fprintf(fp, "Asserted oifs: %-20s\n", asserted_oifs); fprintf(fp, "Outgoing oifs: %-20s\n", oifs); fprintf(fp, "Incoming : %-20s\n", incoming_iif); fprintf(fp, "\nTIMERS: Entry JP RS Assert VIFS:"); for (vifi = 0; vifi < numvifs; vifi++) fprintf(fp, " %d", vifi); fprintf(fp, "\n %5d %4d %4d %6d ", r->timer, r->jp_timer, r->rs_timer, r->assert_timer); for (vifi = 0; vifi < numvifs; vifi++) fprintf(fp, " %2d", r->vif_timers[vifi]); fprintf(fp, "\n"); }