/* * 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 neighbor has failed or become unreachable. If that neighbor was * considered a dominant or subordinate router in any route entries, * take appropriate action. */ void delete_neighbor_from_routes(u_int32_t addr, 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) { if (r->rt_dominants[vifi] == addr) { 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); } else if (r->rt_subordinates[vifi] == addr) { r->rt_subordinates[vifi] = 0; if (v->uv_neighbors == 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 (v->uv_neighbors == NULL && r->rt_leaf_timers[vifi] != 0) { VIFM_SET(vifi, r->rt_leaves); r->rt_leaf_timers[vifi] = 0; update_table_entry(r); } } } }
/* * On every timer interrupt, advance the timer in each routing entry. */ void age_routes(void) { register struct rtentry *r; register struct rtentry *prev_r; register vifi_t vifi; for (prev_r = RT_ADDR, r = routing_table; r != NULL; prev_r = r, r = r->rt_next) { if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) { /* * Route is still good; see if any leaf timers need to be * advanced. */ if (r->rt_flags & RTF_LEAF_TIMING) { r->rt_flags &= ~RTF_LEAF_TIMING; for (vifi = 0; vifi < numvifs; ++vifi) { if (r->rt_leaf_timers[vifi] != 0) { /* * Unlike other timers, leaf timers decrement. */ if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0) { #ifdef NOTYET /* If the vif is a physical leaf but has neighbors, * it is not a tree leaf. If I am a leaf, then no * interface with neighbors is a tree leaf. */ if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) || (vifs_with_neighbors == 1)) && (uvifs[vifi].uv_neighbors != NULL))) { #endif VIFM_SET(vifi, r->rt_leaves); update_table_entry(r); #ifdef NOTYET } #endif } else { r->rt_flags |= RTF_LEAF_TIMING; } } } } } else if (r->rt_timer >= ROUTE_DISCARD_TIME) { /* * Time to garbage-collect the route entry. */ del_table_entry(r, 0, DEL_ALL_ROUTES); discard_route(prev_r); r = prev_r; } else if (r->rt_metric != UNREACHABLE) { /* * Time to expire the route entry. If the gateway is zero, * i.e., it is a route to a directly-connected subnet, just * set the timer back to zero; such routes expire only when * the interface to the subnet goes down. */ if (r->rt_gateway == 0) { r->rt_timer = 0; } else { del_table_entry(r, 0, DEL_ALL_ROUTES); r->rt_metric = UNREACHABLE; r->rt_flags |= RTF_CHANGED; routes_changed = TRUE; } } } }
/* * 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); } } }
static uint64_t find_start_address (uint64_t CR3_virtual_address, uint64_t start_pseudo_page, uint64_t end_pseudo_page, uint64_t total_pages) { // begin installing new tables in the reserved 512kb padding uint64_t table_pseudo_page = start_pseudo_page - 128; PML4 pml4 (CR3_virtual_address); // loop over pages, mapping PDP, PD & PT along the way for (uint64_t i = start_pseudo_page; i < total_pages; i++) { // address at this page number uint64_t virtual_address = pseudo_to_virtual (page_to_address (i)); PML4E pml4e (pml4.get_entry (virtual_address)); if (pml4e.empty ()) { trace ("currently not supporting table additions to PML4\n"); return 0UL; } // note: we have to recall the entry methods because the values may have changed above PDP pdp (pml4.get_entry (virtual_address)); PDPE pdpe (pdp.get_entry (virtual_address)); if (pdpe.empty ()) { uint64_t table_virtual_address = pseudo_to_virtual (page_to_address (table_pseudo_page)); uint64_t table_machine_address = page_to_address (pseudo_page_to_machine_page (table_pseudo_page++)); create_table_entry (CR3_virtual_address, table_virtual_address, table_machine_address); update_table_entry (pdpe.machine_address, table_machine_address); } // note: we have to recall the entry methods because the values may have changed above PD pd (pdp.get_entry (virtual_address)); PDE pde (pd.get_entry (virtual_address)); if (pde.empty ()) { uint64_t table_virtual_address = pseudo_to_virtual (page_to_address (table_pseudo_page)); uint64_t table_machine_address = page_to_address (pseudo_page_to_machine_page (table_pseudo_page++)); create_table_entry (CR3_virtual_address, table_virtual_address, table_machine_address); update_table_entry (pde.machine_address, table_machine_address); } // note: we have to recall the entry methods because the values may have changed above PT pt (pd.get_entry (virtual_address)); PTE pte (pt.get_entry (virtual_address)); // do not try to map the reserved pages if (i < end_pseudo_page) { update_table_entry (pte.machine_address, page_to_address (pseudo_page_to_machine_page (i))); } } if (table_pseudo_page > start_pseudo_page) { // there were more tables than would fit in 512kb area start_pseudo_page = table_pseudo_page; } return pseudo_to_virtual (page_to_address (start_pseudo_page)); }