Esempio n. 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");
}
Esempio n. 2
0
/*
 * Process an incoming route report message.
 */
void
accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
              u_int32_t level)
{
    vifi_t vifi;
    register int width, i, nrt = 0;
    int metric;
    u_int32_t mask;
    u_int32_t origin;
    struct newrt rt[4096];

    if ((vifi = find_vif(src, dst)) == NO_VIF) {
        logit(LOG_INFO, 0,
              "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
        return;
    }

    if (!update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level))
        return;

    if (datalen > 2*4096) {
        logit(LOG_INFO, 0,
              "ignoring oversize (%d bytes) route report from %s",
              datalen, inet_fmt(src, s1));
        return;
    }

    while (datalen > 0) {	/* Loop through per-mask lists. */

        if (datalen < 3) {
            logit(LOG_WARNING, 0,
                  "received truncated route report from %s",
                  inet_fmt(src, s1));
            return;
        }
        ((u_char *)&mask)[0] = 0xff;
        width = 1;
        if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
        if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
        if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
        if (!inet_valid_mask(ntohl(mask))) {
            logit(LOG_WARNING, 0,
                  "%s reports bogus netmask 0x%08x (%s)",
                  inet_fmt(src, s1), ntohl(mask), inet_fmt(mask, s2));
            return;
        }
        datalen -= 3;

        do {			/* Loop through (origin, metric) pairs */
            if (datalen < width + 1) {
                logit(LOG_WARNING, 0,
                      "received truncated route report from %s",
                      inet_fmt(src, s1));
                return;
            }
            origin = 0;
            for (i = 0; i < width; ++i)
                ((char *)&origin)[i] = *p++;
            metric = *p++;
            datalen -= width + 1;
            rt[nrt].mask   = mask;
            rt[nrt].origin = origin;
            rt[nrt].metric = (metric & 0x7f);
            ++nrt;
        } while (!(metric & 0x80));
    }

    qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
    start_route_updates();
    /*
     * If the last entry is default, change mask from 0xff000000 to 0
     */
    if (rt[nrt-1].origin == 0)
        rt[nrt-1].mask = 0;

    logit(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
          inet_fmt(src, s1), inet_fmt(dst, s2));
    for (i = 0; i < nrt; ++i) {
        if (i != 0 && rt[i].origin == rt[i-1].origin &&
                rt[i].mask == rt[i-1].mask) {
            logit(LOG_WARNING, 0, "%s reports duplicate route for %s",
                  inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
            continue;
        }
        update_route(rt[i].origin, rt[i].mask, rt[i].metric,
                     src, vifi);
    }

    if (routes_changed && !delay_change_reports)
        report_to_all_neighbors(CHANGED_ROUTES);
}
Esempio n. 3
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);
        }
    }
}
Esempio n. 4
0
/*
 * Query the kernel to find network interfaces that are multicast-capable
 * and install them in the uvifs array.
 */
void
config_vifs_from_kernel(void)
{
    struct ifaddrs *ifa, *ifap;
    struct uvif *v;
    vifi_t vifi;
    u_int32_t addr, mask, subnet;
    short flags;

    if (getifaddrs(&ifap) < 0)
	logit(LOG_ERR, errno, "getifaddrs");

    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
	/*
	 * Ignore any interface for an address family other than IP.
	 */
	if (ifa->ifa_addr->sa_family != AF_INET)
	    continue;

	addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;

	/*
	 * Ignore loopback interfaces and interfaces that do not support
	 * multicast.
	 */
	flags = ifa->ifa_flags;
	if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST)
	    continue;

	/*
	 * Ignore any interface whose address and mask do not define a
	 * valid subnet number, or whose address is of the form {subnet,0}
	 * or {subnet,-1}.
	 */
	mask = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr;
	subnet = addr & mask;
	if (!inet_valid_subnet(subnet, mask) ||
	    addr == subnet ||
	    addr == (subnet | ~mask)) {
	    logit(LOG_WARNING, 0,
		"ignoring %s, has invalid address (%s) and/or mask (%s)",
		ifa->ifa_name, inet_fmt(addr),
		inet_fmt(mask));
	    continue;
	}

	/*
	 * Ignore any interface that is connected to the same subnet as
	 * one already installed in the uvifs array.
	 */
	for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
	    if ((addr & v->uv_subnetmask) == v->uv_subnet ||
		(v->uv_subnet & mask) == subnet) {
		logit(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
		    ifa->ifa_name, v->uv_name);
		break;
	    }
	}
	if (vifi != numvifs)
	    continue;

	/*
	 * If there is room in the uvifs array, install this interface.
	 */
	if (numvifs == MAXVIFS) {
	    logit(LOG_WARNING, 0, "too many vifs, ignoring %s", ifa->ifa_name);
	    continue;
	}
	v  = &uvifs[numvifs];
	v->uv_flags       = 0;
	v->uv_metric      = DEFAULT_METRIC;
	v->uv_rate_limit  = DEFAULT_PHY_RATE_LIMIT;
	v->uv_threshold   = DEFAULT_THRESHOLD;
	v->uv_lcl_addr    = addr;
	v->uv_rmt_addr    = 0;
	v->uv_subnet      = subnet;
	v->uv_subnetmask  = mask;
	v->uv_subnetbcast = subnet | ~mask;
	strlcpy(v->uv_name, ifa->ifa_name, sizeof(v->uv_name));
	v->uv_groups      = NULL;
	v->uv_neighbors   = NULL;
	v->uv_acl         = NULL;
	v->uv_addrs	  = NULL;

	logit(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d",
	    v->uv_name, inet_fmt(addr),
	    inet_fmts(subnet, mask),
	    numvifs, v->uv_rate_limit);

	++numvifs;

	/*
	 * If the interface is not yet up, set the vifs_down flag to
	 * remind us to check again later.
	 */
	if (!(flags & IFF_UP)) {
	    v->uv_flags |= VIFF_DOWN;
	    vifs_down = TRUE;
	}
    }

    freeifaddrs(ifap);
}
Esempio n. 5
0
/*
 * Query the kernel to find network interfaces that are multicast-capable
 * and install them in the uvifs array.
 */
void
config_vifs_from_kernel(void)
{
    struct ifreq *ifrp, *ifend;
    struct uvif *v;
    vifi_t vifi;
    int n;
    u_int32 addr, mask, subnet;
    short flags;
    int num_ifreq = 32;

    ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
    ifc.ifc_buf = malloc(ifc.ifc_len);
    while (ifc.ifc_buf) {
	if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
	    dolog(LOG_ERR, errno, "ioctl SIOCGIFCONF");

	/*
	 * If the buffer was large enough to hold all the addresses
	 * then break out, otherwise increase the buffer size and
	 * try again.
	 *
	 * The only way to know that we definitely had enough space
	 * is to know that there was enough space for at least one
	 * more struct ifreq. ???
	 */
	if ((num_ifreq * sizeof(struct ifreq)) >=
	     ifc.ifc_len + sizeof(struct ifreq))
	     break;

	num_ifreq *= 2;
	ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
	ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);
    }
    if (ifc.ifc_buf == NULL)
	dolog(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory");

    ifrp = (struct ifreq *)ifc.ifc_buf;
    ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
    /*
     * Loop through all of the interfaces.
     */
    for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
	struct ifreq ifr;
#ifdef HAVE_SA_LEN
	n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
	if (n < sizeof(*ifrp))
	    n = sizeof(*ifrp);
#else
	n = sizeof(*ifrp);
#endif
	/*
	 * Ignore any interface for an address family other than IP.
	 */
	if (ifrp->ifr_addr.sa_family != AF_INET)
	    continue;

	addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;

	/*
	 * Need a template to preserve address info that is
	 * used below to locate the next entry.  (Otherwise,
	 * SIOCGIFFLAGS stomps over it because the requests
	 * are returned in a union.)
	 */
	bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));

	/*
	 * Ignore loopback interfaces and interfaces that do not support
	 * multicast.
	 */
	if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
	    dolog(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
	flags = ifr.ifr_flags;
	if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue;

	/*
	 * Ignore any interface whose address and mask do not define a
	 * valid subnet number, or whose address is of the form {subnet,0}
	 * or {subnet,-1}.
	 */
	if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0)
	    dolog(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name);
	mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
	subnet = addr & mask;
	if (!inet_valid_subnet(subnet, mask) ||
	    addr == subnet ||
	    addr == (subnet | ~mask)) {
	    dolog(LOG_WARNING, 0,
		"ignoring %s, has invalid address (%s) and/or mask (%s)",
		ifr.ifr_name, inet_fmt(addr, s1), inet_fmt(mask, s2));
	    continue;
	}

	/*
	 * Ignore any interface that is connected to the same subnet as
	 * one already installed in the uvifs array.
	 */
	for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
	    if (strcmp(v->uv_name, ifr.ifr_name) == 0) {
		dolog(LOG_DEBUG, 0, "skipping %s (%s on subnet %s) (alias for vif#%u?)",
			v->uv_name, inet_fmt(addr, s1),
			inet_fmts(subnet, mask, s2), vifi);
		break;
	    }
	    if ((addr & v->uv_subnetmask) == v->uv_subnet ||
		(v->uv_subnet & mask) == subnet) {
		dolog(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
					ifr.ifr_name, v->uv_name);
		break;
	    }
	}
	if (vifi != numvifs) continue;

	/*
	 * If there is room in the uvifs array, install this interface.
	 */
	if (numvifs == MAXVIFS) {
	    dolog(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name);
	    continue;
	}
	v  = &uvifs[numvifs];
	zero_vif(v, 0);
	v->uv_lcl_addr    = addr;
	v->uv_subnet      = subnet;
	v->uv_subnetmask  = mask;
	v->uv_subnetbcast = subnet | ~mask;
	strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
	v->uv_name[IFNAMSIZ-1] = '\0';

	if (flags & IFF_POINTOPOINT)
	    v->uv_flags |= VIFF_REXMIT_PRUNES;

	dolog(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d",
	    v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
	    numvifs, v->uv_rate_limit);

	++numvifs;

	/*
	 * If the interface is not yet up, set the vifs_down flag to
	 * remind us to check again later.
	 */
	if (!(flags & IFF_UP)) {
	    v->uv_flags |= VIFF_DOWN;
	    vifs_down = TRUE;
	}
    }
}