Beispiel #1
0
/*
 * 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");
}
Beispiel #2
0
/*
 * 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;
            }
        }
    }
}
Beispiel #3
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);
        }
    }
}
Beispiel #4
0
/*
 * 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);
}
Beispiel #5
0
/*
 * 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);
    }

}
Beispiel #6
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);
        }
    }
}
Beispiel #7
0
/*
 * 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);
    }
}
Beispiel #8
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);
    }
}
Beispiel #9
0
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");
}