Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ipv6_routing_table.c
00001 /* 00002 * Copyright (c) 2012-2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 /* 00018 * ipv6_routing_table.c 00019 * 00020 * Implements IPv6 Neighbour Cache (RFC 4861), Destination Cache (RFC 4861), 00021 * and Routing Table (RFC 4191, incorporating the RFC 4861 Prefix List) 00022 * 00023 * Note that RFC 4861 dictates that the Prefix List is checked first, 00024 * followed by the Default Router List. In simple host scenarios, the 00025 * longest-match routing table look-up achieves that, because on-link entries 00026 * from the Prefix List are longer than the ::/0 default routes. 00027 * 00028 * In more complex scenarios, we can have more-specific routes preferred over 00029 * more general on-link prefixes, eg the border router preferring a /128 RPL 00030 * DAO-SR route instead of the /64 on-link prefix for the Ethernet backbone. 00031 * 00032 */ 00033 #include "nsconfig.h" 00034 #include "ns_types.h" 00035 #include "common_functions.h" 00036 #include "ip6string.h" 00037 #include "randLIB.h" 00038 #include "ns_trace.h" 00039 #include "string.h" 00040 #include "Core/include/address.h" 00041 #include "ipv6_stack/ipv6_routing_table.h" 00042 #include "Common_Protocols/ipv6_constants.h" 00043 #include "Common_Protocols/icmpv6.h" 00044 #include "nsdynmemLIB.h" 00045 #include "Service_Libs/etx/etx.h" 00046 #include "Common_Protocols/ipv6_resolution.h" 00047 #include <stdarg.h> 00048 #include <stdio.h> 00049 00050 #define TRACE_GROUP "rout" 00051 00052 #define NCACHE_GC_PERIOD 20 /* seconds */ 00053 #define DCACHE_GC_PERIOD 20 /* seconds */ 00054 00055 static uint16_t current_max_cache = 64; 00056 00057 /* We track "lifetime" of garbage-collectible entries, resetting 00058 * when used. Entries with lifetime 0 are favoured 00059 * for garbage-collection. */ 00060 #define NCACHE_GC_AGE 600 /* 10 minutes (1s units - decremented every slow timer call) */ 00061 #define DCACHE_GC_AGE 30 /* 10 minutes (20s units - decremented once per periodic GC) */ 00062 #define DCACHE_GC_AGE_LL 6 /* 2 minutes for link-local destinations */ 00063 00064 /* For probable routers, consider them unreachable if ETX is greater than this */ 00065 #define ETX_REACHABILITY_THRESHOLD 0x200 /* 8.8 fixed-point, so 2 */ 00066 00067 static NS_LIST_DEFINE(ipv6_destination_cache, ipv6_destination_t, link); 00068 static NS_LIST_DEFINE(ipv6_routing_table, ipv6_route_t, link); 00069 00070 static ipv6_destination_t *ipv6_destination_lookup(const uint8_t *address, int8_t interface_id); 00071 static void ipv6_destination_cache_forget_router(ipv6_neighbour_cache_t *cache, const uint8_t neighbour_addr[16]); 00072 static void ipv6_destination_cache_forget_neighbour(const ipv6_neighbour_t *neighbour); 00073 static void ipv6_destination_release(ipv6_destination_t *dest); 00074 static void ipv6_route_table_remove_router(int8_t interface_id, const uint8_t *addr, ipv6_route_src_t source); 00075 static uint16_t total_metric(const ipv6_route_t *route); 00076 static void trace_debug_print(const char *fmt, ...); 00077 static uint8_t ipv6_route_table_count_source(int8_t interface_id, ipv6_route_src_t source); 00078 static void ipv6_route_table_remove_last_one_from_source(int8_t interface_id, ipv6_route_src_t source); 00079 static uint8_t ipv6_route_table_get_max_entries(int8_t interface_id, ipv6_route_src_t source); 00080 00081 static uint16_t dcache_gc_timer; 00082 00083 static uint16_t cache_long_term(bool is_destination) 00084 { 00085 uint16_t value = current_max_cache/8; 00086 if (is_destination) { 00087 value*=2; 00088 } 00089 if (value < 4) { 00090 value = 4; 00091 } 00092 return value; 00093 } 00094 00095 static uint16_t cache_short_term(bool is_destination) 00096 { 00097 uint16_t value = current_max_cache/2; 00098 if (value < cache_long_term(is_destination)) { 00099 return cache_long_term(is_destination); 00100 } 00101 return value; 00102 } 00103 00104 static uint32_t next_probe_time(ipv6_neighbour_cache_t *cache, uint_fast8_t retrans_num) 00105 { 00106 uint32_t t = cache->retrans_timer; 00107 00108 while (retrans_num--) { 00109 t *= BACKOFF_MULTIPLE; 00110 if (t > MAX_RETRANS_TIMER) { 00111 t = MAX_RETRANS_TIMER; 00112 break; 00113 } 00114 } 00115 00116 return randLIB_randomise_base(t, 0x4000, 0xBFFF); 00117 } 00118 00119 int8_t ipv6_neighbour_set_current_max_cache(uint16_t max_cache) 00120 { 00121 if (max_cache < 4) { 00122 return -1; 00123 } 00124 current_max_cache = max_cache; 00125 return 0; 00126 } 00127 00128 /* Called when we determine a neighbour is no longer a router */ 00129 void ipv6_router_gone(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry) 00130 { 00131 tr_debug("Router gone: %s", trace_ipv6(entry->ip_address)); 00132 entry->is_router = false; 00133 /* Only delete RA routes to satisfy RFC 4861. We should have a callback to 00134 * other route providers here - eg RPL might want to know, and delete from 00135 * the Candidate Neighbour set. But unfortunately our 6LoWPAN-ND routers do 00136 * currently send RS packets while running, which means this would break 00137 * stuff. We get spurious switches of IsRouter to false :( 00138 */ 00139 ipv6_route_table_remove_router(cache->interface_id, entry->ip_address, ROUTE_RADV); 00140 /* The above will re-evaluate all destinations affected by the routing 00141 * change; the below is needed to also forget redirections to the router. 00142 */ 00143 ipv6_destination_cache_forget_router(cache, entry->ip_address); 00144 } 00145 00146 /* Called when a neighbour has apparently become reachable */ 00147 static void ipv6_neighbour_appeared(ipv6_neighbour_cache_t *cache, uint8_t address[static 16]) 00148 { 00149 (void)cache; 00150 (void)address; 00151 } 00152 00153 /* Called when a neighbour has apparently become unreachable */ 00154 static void ipv6_neighbour_gone(ipv6_neighbour_cache_t *cache, uint8_t address[static 16]) 00155 { 00156 (void) cache; 00157 tr_debug("Lost contact with neighbour: %s", trace_ipv6(address)); 00158 // We can keep trying to talk directly to that neighbour, but should 00159 // avoid using it any more as a router, if there's an alternative. 00160 ipv6_destination_cache_forget_router(cache, address); 00161 } 00162 00163 void ipv6_neighbour_cache_init(ipv6_neighbour_cache_t *cache, int8_t interface_id) 00164 { 00165 /* Init Double linked Routing Table */ 00166 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00167 ipv6_neighbour_entry_remove(cache, cur); 00168 } 00169 cache->gc_timer = NCACHE_GC_PERIOD; 00170 cache->retrans_timer = 1000; 00171 cache->max_ll_len = 0; 00172 cache->interface_id = interface_id; 00173 cache->recv_addr_reg = false; 00174 cache->send_addr_reg = false; 00175 cache->send_nud_probes = true; 00176 cache->recv_na_aro = false; 00177 cache->recv_ns_aro = false; 00178 cache->route_if_info.metric = 0; 00179 memset(cache->route_if_info.sources, 0, sizeof(cache->route_if_info.sources)); 00180 } 00181 00182 void ipv6_neighbour_cache_flush(ipv6_neighbour_cache_t *cache) 00183 { 00184 /* Flush non-registered entries only */ 00185 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00186 if (cur->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00187 ipv6_neighbour_entry_remove(cache, cur); 00188 } 00189 } 00190 } 00191 00192 00193 ipv6_neighbour_t *ipv6_neighbour_lookup(ipv6_neighbour_cache_t *cache, const uint8_t *address) 00194 { 00195 ns_list_foreach(ipv6_neighbour_t, cur, &cache->list) { 00196 if (addr_ipv6_equal(cur->ip_address, address)) { 00197 return cur; 00198 } 00199 } 00200 00201 return NULL; 00202 } 00203 00204 ipv6_neighbour_t *ipv6_neighbour_lookup_by_interface_id(int8_t interface_id, const uint8_t *address) 00205 { 00206 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(interface_id); 00207 if (!ncache) { 00208 return NULL; 00209 } 00210 00211 return ipv6_neighbour_lookup(ncache, address); 00212 } 00213 00214 00215 void ipv6_neighbour_entry_remove(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry) 00216 { 00217 /* Remove entry from cache first - avoids weird garbage collection issues, like 00218 * it being pushed out while generating ICMP errors, or ICMP errors actually using 00219 * the entry. 00220 */ 00221 ns_list_remove(&cache->list, entry); 00222 switch (entry->state) { 00223 case IP_NEIGHBOUR_NEW: 00224 break; 00225 case IP_NEIGHBOUR_INCOMPLETE: 00226 /* If the NCE is discarded, the queued packet must also be discarded */ 00227 /* Handle this here to avoid leakage in the event that the NCE is */ 00228 /* dropped by garbage collection rather than the expected timeout */ 00229 ipv6_interface_resolution_failed(cache, entry); 00230 break; 00231 case IP_NEIGHBOUR_STALE: 00232 case IP_NEIGHBOUR_REACHABLE: 00233 case IP_NEIGHBOUR_DELAY: 00234 case IP_NEIGHBOUR_PROBE: 00235 case IP_NEIGHBOUR_UNREACHABLE: 00236 /* Destination cache no longer has direct pointers to neighbour cache, 00237 * so a NCE being deleted no longer necessarily needs any special 00238 * action. Neighbour GC needn't affect the Dest Cache. 00239 */ 00240 // ipv6_neighbour_gone(cache, entry); 00241 break; 00242 } 00243 ipv6_destination_cache_forget_neighbour(entry); 00244 ns_dyn_mem_free(entry); 00245 } 00246 00247 ipv6_neighbour_t *ipv6_neighbour_lookup_or_create(ipv6_neighbour_cache_t *cache, const uint8_t *address/*, bool tentative*/) 00248 { 00249 uint_fast16_t count = 0; 00250 ipv6_neighbour_t *entry = NULL; 00251 00252 ns_list_foreach(ipv6_neighbour_t, cur, &cache->list) { 00253 count++; 00254 if (addr_ipv6_equal(cur->ip_address, address)) { 00255 if (cur != ns_list_get_first(&cache->list)) { 00256 ns_list_remove(&cache->list, cur); 00257 ns_list_add_to_start(&cache->list, cur); 00258 } 00259 return cur; 00260 } 00261 } 00262 00263 if (count >= current_max_cache) { 00264 entry = ns_list_get_last(&cache->list); 00265 ipv6_neighbour_entry_remove(cache, entry); 00266 } 00267 00268 // Allocate new - note we have a basic size, plus enough for the LL address, 00269 // plus another 8 for the EUI-64 of registration (RFC 6775). Note that in 00270 // the protocols, the link-layer address and EUI-64 are distinct. The 00271 // neighbour may be using a short link-layer address, not its EUI-64. 00272 entry = ns_dyn_mem_alloc(sizeof(ipv6_neighbour_t) + cache->max_ll_len + (cache->recv_addr_reg ? 8 : 0)); 00273 if (!entry) { 00274 tr_warn("No mem!"); 00275 return NULL; 00276 } 00277 00278 memcpy(entry->ip_address, address, 16); 00279 entry->is_router = false; 00280 entry->from_redirect = false; 00281 entry->state = IP_NEIGHBOUR_NEW; 00282 /*if (tentative && cache->reg_required) 00283 entry->type = IP_NEIGHBOUR_TENTATIVE; 00284 else*/ 00285 entry->type = IP_NEIGHBOUR_GARBAGE_COLLECTIBLE; 00286 ns_list_init(&entry->queue); 00287 entry->timer = 0; 00288 entry->lifetime = 0; 00289 entry->retrans_count = 0; 00290 entry->ll_type = ADDR_NONE ; 00291 if (cache->recv_addr_reg) { 00292 memset(ipv6_neighbour_eui64(cache, entry), 0, 8); 00293 } 00294 00295 ns_list_add_to_start(&cache->list, entry); 00296 00297 return entry; 00298 } 00299 00300 ipv6_neighbour_t *ipv6_neighbour_lookup_or_create_by_interface_id(int8_t interface_id, const uint8_t *address/*, bool tentative*/) 00301 { 00302 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(interface_id); 00303 if (!ncache) { 00304 return NULL; 00305 } 00306 00307 return ipv6_neighbour_lookup_or_create(ncache, address/*, tentative*/); 00308 } 00309 00310 ipv6_neighbour_t *ipv6_neighbour_used(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry) 00311 { 00312 /* Reset the GC life, if it's a GC entry */ 00313 if (entry->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00314 entry->lifetime = NCACHE_GC_AGE; 00315 } 00316 00317 /* Move it to the front of the list */ 00318 if (entry != ns_list_get_first(&cache->list)) { 00319 ns_list_remove(&cache->list, entry); 00320 ns_list_add_to_start(&cache->list, entry); 00321 } 00322 00323 /* If the entry is stale, prepare delay timer for active NUD probe */ 00324 if (entry->state == IP_NEIGHBOUR_STALE && cache->send_nud_probes) { 00325 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_DELAY); 00326 } 00327 00328 /* Special case for Registered Unreachable entries - restart the probe timer if stopped */ 00329 else if (entry->state == IP_NEIGHBOUR_UNREACHABLE && entry->timer == 0) { 00330 entry->timer = next_probe_time(cache, entry->retrans_count); 00331 } 00332 00333 return entry; 00334 } 00335 00336 static bool ipv6_neighbour_state_is_probably_reachable(ip_neighbour_cache_state_t state) 00337 { 00338 switch (state) { 00339 case IP_NEIGHBOUR_NEW: 00340 case IP_NEIGHBOUR_INCOMPLETE: 00341 case IP_NEIGHBOUR_UNREACHABLE: 00342 return false; 00343 case IP_NEIGHBOUR_REACHABLE: 00344 case IP_NEIGHBOUR_STALE: 00345 case IP_NEIGHBOUR_DELAY: 00346 case IP_NEIGHBOUR_PROBE: 00347 return true; 00348 } 00349 return false; 00350 } 00351 00352 bool ipv6_neighbour_is_probably_reachable(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *n) 00353 { 00354 if (!n) { 00355 return false; 00356 } 00357 if (!ipv6_neighbour_state_is_probably_reachable(n->state)) { 00358 return false; 00359 } 00360 uint16_t etx = ipv6_map_ip_to_ll_and_call_ll_addr_handler(NULL, cache->interface_id, n, n->ip_address, etx_read); 00361 if (etx > ETX_REACHABILITY_THRESHOLD) { 00362 /* "Unknown" is signalled as low values, so will be return "true" */ 00363 return false; 00364 } 00365 return true; 00366 } 00367 00368 bool ipv6_neighbour_addr_is_probably_reachable(ipv6_neighbour_cache_t *cache, const uint8_t *address) 00369 { 00370 return ipv6_neighbour_is_probably_reachable(cache, ipv6_neighbour_lookup(cache, address)); 00371 } 00372 00373 bool ipv6_neighbour_ll_addr_match(const ipv6_neighbour_t *entry, addrtype_t ll_type, const uint8_t *ll_address) 00374 { 00375 return ll_type == entry->ll_type && memcmp(entry->ll_address, ll_address, addr_len_from_type(ll_type)) == 0; 00376 } 00377 00378 static bool ipv6_neighbour_update_ll(ipv6_neighbour_t *entry, addrtype_t ll_type, const uint8_t *ll_address) 00379 { 00380 uint8_t ll_len = addr_len_from_type(ll_type); 00381 00382 /* Any new address info clears the "redirected" flag - redirect itself 00383 * sets it again after this is called. 00384 */ 00385 entry->from_redirect = false; 00386 00387 if (ll_type != entry->ll_type || memcmp(entry->ll_address, ll_address, ll_len)) { 00388 entry->ll_type = ll_type; 00389 memcpy(entry->ll_address, ll_address, ll_len); 00390 return true; 00391 } 00392 return false; 00393 } 00394 00395 void ipv6_neighbour_invalidate_ll_addr(ipv6_neighbour_cache_t *cache, addrtype_t ll_type, const uint8_t *ll_address) 00396 { 00397 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00398 if (cur->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE && ipv6_neighbour_ll_addr_match(cur, ll_type, ll_address)) { 00399 ipv6_neighbour_entry_remove(cache, cur); 00400 } 00401 } 00402 } 00403 00404 void ipv6_neighbour_delete_registered_by_eui64(ipv6_neighbour_cache_t *cache, const uint8_t *eui64) 00405 { 00406 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00407 if (cur->type != IP_NEIGHBOUR_GARBAGE_COLLECTIBLE && memcmp(ipv6_neighbour_eui64(cache, cur), eui64, 8) == 0) { 00408 ipv6_neighbour_entry_remove(cache, cur); 00409 } 00410 } 00411 } 00412 00413 void ipv6_neighbour_set_state(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, ip_neighbour_cache_state_t state) 00414 { 00415 if (!ipv6_neighbour_state_is_probably_reachable(entry->state) && 00416 ipv6_neighbour_state_is_probably_reachable(state)) { 00417 /* A neighbour is becoming reachable - may affect destination cache */ 00418 ipv6_neighbour_appeared(cache, entry->ip_address); 00419 } 00420 switch (state) { 00421 case IP_NEIGHBOUR_INCOMPLETE: 00422 entry->retrans_count = 0; 00423 entry->timer = cache->retrans_timer; 00424 break; 00425 case IP_NEIGHBOUR_STALE: 00426 entry->timer = 0; 00427 break; 00428 case IP_NEIGHBOUR_DELAY: 00429 entry->timer = DELAY_FIRST_PROBE_TIME; 00430 break; 00431 case IP_NEIGHBOUR_PROBE: 00432 entry->retrans_count = 0; 00433 entry->timer = next_probe_time(cache, 0); 00434 break; 00435 case IP_NEIGHBOUR_REACHABLE: 00436 entry->timer = cache->reachable_time; 00437 break; 00438 case IP_NEIGHBOUR_UNREACHABLE: 00439 /* Progress to this from PROBE - timers continue */ 00440 ipv6_neighbour_gone(cache, entry->ip_address); 00441 break; 00442 default: 00443 entry->timer = 0; 00444 break; 00445 } 00446 entry->state = state; 00447 } 00448 00449 /* Called when LL address information is received other than in an NA (NS source, RS source, RA source, Redirect target) */ 00450 void ipv6_neighbour_entry_update_unsolicited(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, addrtype_t type, const uint8_t *ll_address/*, bool tentative*/) 00451 { 00452 bool modified_ll = ipv6_neighbour_update_ll(entry, type, ll_address); 00453 00454 switch (entry->state) { 00455 case IP_NEIGHBOUR_NEW: 00456 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00457 break; 00458 case IP_NEIGHBOUR_INCOMPLETE: 00459 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00460 ipv6_send_queued(entry); 00461 break; 00462 default: 00463 if (modified_ll) { 00464 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00465 } 00466 break; 00467 } 00468 } 00469 00470 ipv6_neighbour_t *ipv6_neighbour_update_unsolicited(ipv6_neighbour_cache_t *cache, const uint8_t *ip_address, addrtype_t type, const uint8_t *ll_address/*, bool tentative*/) 00471 { 00472 ipv6_neighbour_t *entry = ipv6_neighbour_lookup_or_create(cache, ip_address/*, tentative*/); 00473 if (!entry) { 00474 return NULL; 00475 } 00476 00477 ipv6_neighbour_entry_update_unsolicited(cache, entry, type, ll_address/*, tentative*/); 00478 00479 return entry; 00480 } 00481 00482 void ipv6_neighbour_update_from_na(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, uint8_t flags, addrtype_t ll_type, const uint8_t *ll_address) 00483 { 00484 if (entry->state == IP_NEIGHBOUR_NEW || entry->state == IP_NEIGHBOUR_INCOMPLETE) { 00485 entry->is_router = flags & NA_R; 00486 if (ll_type == ADDR_NONE ) { 00487 return; 00488 } 00489 00490 ipv6_neighbour_update_ll(entry, ll_type, ll_address); 00491 if (flags & NA_S) { 00492 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_REACHABLE); 00493 } else { 00494 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00495 } 00496 ipv6_send_queued(entry); 00497 return; 00498 } 00499 00500 /* Already have a complete entry with known LL address */ 00501 bool ll_addr_differs = ll_type != ADDR_NONE && !ipv6_neighbour_ll_addr_match(entry, ll_type, ll_address); 00502 00503 if (ll_addr_differs) { 00504 if (flags & NA_O) { 00505 entry->ll_type = ll_type; 00506 memcpy(entry->ll_address, ll_address, addr_len_from_type(ll_type)); 00507 } else { 00508 if (entry->state == IP_NEIGHBOUR_REACHABLE) { 00509 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00510 } 00511 return; 00512 } 00513 } 00514 00515 if (flags & NA_S) { 00516 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_REACHABLE); 00517 } else if (ll_addr_differs) { 00518 ipv6_neighbour_set_state(cache, entry, IP_NEIGHBOUR_STALE); 00519 } 00520 00521 if (entry->is_router && !(flags & NA_R)) { 00522 ipv6_router_gone(cache, entry); 00523 } 00524 00525 entry->is_router = flags & NA_R; 00526 } 00527 00528 void ipv6_neighbour_reachability_confirmation(const uint8_t ip_address[static 16], int8_t interface_id) 00529 { 00530 /* No point creating an entry if doesn't exist */ 00531 ipv6_destination_t *dest = ipv6_destination_lookup(ip_address, interface_id); 00532 if (!dest) { 00533 return; 00534 } 00535 00536 /* We can't be absolutely certain which next hop is working, but last_neighbour is our best guess */ 00537 ipv6_neighbour_t *next_hop = dest->last_neighbour; 00538 #if 0 00539 if (next_hop) { 00540 tr_debug("%s rconf: mark %s reachable", trace_ipv6(ip_address), trace_ipv6(next_hop->ip_address)); 00541 } else { 00542 tr_debug("%s rconf: next hop unknown", trace_ipv6(ip_address)); 00543 } 00544 #endif 00545 if (!next_hop) { 00546 return; 00547 } 00548 00549 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(dest->interface_id); 00550 if (!ncache) { 00551 return; 00552 } 00553 00554 if (next_hop->state != IP_NEIGHBOUR_NEW && next_hop->state != IP_NEIGHBOUR_INCOMPLETE) { 00555 ipv6_neighbour_set_state(ncache, next_hop, IP_NEIGHBOUR_REACHABLE); 00556 } 00557 } 00558 00559 /* RFC 4861 doesn't have this, but would seem sensible to at least nudge it out of REACHABLE state. 00560 * This doesn't add a new state machine transition, we just cut short the timer. 00561 * This should normally be called /before/ initiating a retransmit, so the 00562 * retransmit then triggers an immediate transition into DELAY state. 00563 */ 00564 void ipv6_neighbour_reachability_problem(const uint8_t ip_address[static 16], int8_t interface_id) 00565 { 00566 /* No point creating an entry if doesn't exist */ 00567 ipv6_destination_t *dest = ipv6_destination_lookup(ip_address, interface_id); 00568 if (!dest) { 00569 return; 00570 } 00571 00572 /* We can't be absolutely certain which next hop has problems, but last_neighbour is our best guess */ 00573 ipv6_neighbour_t *next_hop = dest->last_neighbour; 00574 if (!next_hop) { 00575 return; 00576 } 00577 00578 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(dest->interface_id); 00579 if (!ncache) { 00580 return; 00581 } 00582 00583 if (next_hop->state == IP_NEIGHBOUR_REACHABLE) { 00584 ipv6_neighbour_set_state(ncache, next_hop, IP_NEIGHBOUR_STALE); 00585 } 00586 } 00587 00588 static const char *state_names[] = { 00589 [IP_NEIGHBOUR_NEW] = "NEW", 00590 [IP_NEIGHBOUR_INCOMPLETE] = "INCOMPLETE", 00591 [IP_NEIGHBOUR_STALE] = "STALE", 00592 [IP_NEIGHBOUR_REACHABLE] = "REACHABLE", 00593 [IP_NEIGHBOUR_DELAY] = "DELAY", 00594 [IP_NEIGHBOUR_PROBE] = "PROBE", 00595 [IP_NEIGHBOUR_UNREACHABLE] = "UNREACHABLE", 00596 }; 00597 00598 static const char *type_names[] = { 00599 [IP_NEIGHBOUR_GARBAGE_COLLECTIBLE] = "GC", 00600 [IP_NEIGHBOUR_REGISTERED] = "REGISTERED", 00601 [IP_NEIGHBOUR_TENTATIVE] = "TENTATIVE", 00602 }; 00603 00604 static void sprint_array(char *s, const uint8_t *ptr, uint_fast8_t len) 00605 { 00606 if (len == 0) { 00607 *s = '\0'; 00608 return; 00609 } 00610 00611 for (uint_fast8_t i = 0; i < len; i++) { 00612 s += sprintf(s, "%02x:", *ptr++); 00613 } 00614 // Replace last ':' with '\0' 00615 *(s - 1) = '\0'; 00616 } 00617 00618 void ipv6_neighbour_cache_print(const ipv6_neighbour_cache_t *cache, route_print_fn_t *print_fn) 00619 { 00620 print_fn("Neighbour Cache %d", cache->interface_id); 00621 print_fn("Reachable Time: %"PRIu32" Retrans Timer: %"PRIu32" MTU: %"PRIu16"", cache->reachable_time, cache->retrans_timer, cache->link_mtu); 00622 ns_list_foreach(const ipv6_neighbour_t, cur, &cache->list) { 00623 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str); 00624 print_fn("%sIP Addr: %s", cur->is_router ? "Router " : "", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, cur->ip_address)); 00625 // Reusing addr_str for the array prints as it's no longer needed and 41 bytes is more than enough. 00626 sprint_array(addr_str, cur->ll_address, addr_len_from_type(cur->ll_type)); 00627 print_fn("LL Addr: (%s %"PRIu32") %s", state_names[cur->state], cur->timer, addr_str); 00628 if (cache->recv_addr_reg && memcmp(ipv6_neighbour_eui64(cache, cur), ADDR_EUI64_ZERO, 8)) { 00629 sprint_array( addr_str, ipv6_neighbour_eui64(cache, cur), 8); 00630 print_fn("EUI-64: (%s %"PRIu32") %s", type_names[cur->type], cur->lifetime, addr_str); 00631 } else if (cur->type != IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00632 print_fn(" (%s %"PRIu32") [no EUI-64]", type_names[cur->type], cur->lifetime); 00633 } 00634 } 00635 } 00636 00637 static void ipv6_neighbour_cache_gc_periodic(ipv6_neighbour_cache_t *cache) 00638 { 00639 uint_fast16_t gc_count = 0; 00640 ns_list_foreach_safe(ipv6_neighbour_t, entry, &cache->list) { 00641 if (entry->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00642 gc_count++; 00643 } 00644 } 00645 00646 if (gc_count <= cache_long_term(false)) { 00647 return; 00648 } 00649 00650 /* Removal strategy - to stay below MAX_SHORT_TERM, we will chuck any STALE entries */ 00651 /* To stay below MAX_LONG_TERM, we will chuck old STALE entries */ 00652 ns_list_foreach_reverse_safe(ipv6_neighbour_t, entry, &cache->list) { 00653 /* Expiration of non-GC entries handled in slow timer routine */ 00654 if (entry->type != IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00655 continue; 00656 } 00657 00658 if (entry->state != IP_NEIGHBOUR_STALE && entry->state != IP_NEIGHBOUR_UNREACHABLE) { 00659 continue; 00660 } 00661 00662 if (entry->lifetime == 0 || gc_count > cache_short_term(false)) { 00663 ipv6_neighbour_entry_remove(cache, entry); 00664 if (--gc_count <= cache_long_term(false)) { 00665 break; 00666 } 00667 } 00668 } 00669 } 00670 00671 void ipv6_neighbour_cache_slow_timer(ipv6_neighbour_cache_t *cache, uint8_t seconds) 00672 { 00673 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00674 if (cur->lifetime == 0 || cur->lifetime == 0xffffffff) { 00675 continue; 00676 } 00677 00678 if (cur->lifetime > seconds) { 00679 cur->lifetime -= seconds; 00680 continue; 00681 } 00682 00683 cur->lifetime = 0; 00684 00685 /* Lifetime expired */ 00686 switch (cur->type) { 00687 case IP_NEIGHBOUR_GARBAGE_COLLECTIBLE: 00688 /* No immediate action, but 0 lifetime is an input to the GC */ 00689 break; 00690 00691 case IP_NEIGHBOUR_TENTATIVE: 00692 case IP_NEIGHBOUR_REGISTERED: 00693 /* These are deleted as soon as lifetime expires */ 00694 ipv6_neighbour_gone(cache, cur->ip_address); 00695 ipv6_neighbour_entry_remove(cache, cur); 00696 break; 00697 } 00698 } 00699 00700 if (cache->gc_timer > seconds) { 00701 cache->gc_timer -= seconds; 00702 return; 00703 } 00704 00705 cache->gc_timer = NCACHE_GC_PERIOD; 00706 //ipv6_neighbour_cache_print(cache); 00707 ipv6_neighbour_cache_gc_periodic(cache); 00708 } 00709 00710 void ipv6_neighbour_cache_fast_timer(ipv6_neighbour_cache_t *cache, uint16_t ticks) 00711 { 00712 uint32_t ms = (uint32_t) ticks * 100; 00713 00714 ns_list_foreach_safe(ipv6_neighbour_t, cur, &cache->list) { 00715 if (cur->timer == 0) { 00716 continue; 00717 } 00718 00719 if (cur->timer > ms) { 00720 cur->timer -= ms; 00721 continue; 00722 } 00723 00724 cur->timer = 0; 00725 00726 /* Timer expired */ 00727 switch (cur->state) { 00728 case IP_NEIGHBOUR_NEW: 00729 /* Shouldn't happen */ 00730 break; 00731 case IP_NEIGHBOUR_INCOMPLETE: 00732 if (++cur->retrans_count >= MAX_MULTICAST_SOLICIT) { 00733 /* Should be safe for registration - Tentative/Registered entries can't be INCOMPLETE */ 00734 ipv6_neighbour_gone(cache, cur->ip_address); 00735 ipv6_neighbour_entry_remove(cache, cur); 00736 } else { 00737 ipv6_interface_resolve_send_ns(cache, cur, false, cur->retrans_count); 00738 cur->timer = cache->retrans_timer; 00739 } 00740 break; 00741 case IP_NEIGHBOUR_STALE: 00742 /* Shouldn't happen */ 00743 break; 00744 case IP_NEIGHBOUR_REACHABLE: 00745 ipv6_neighbour_set_state(cache, cur, IP_NEIGHBOUR_STALE); 00746 break; 00747 case IP_NEIGHBOUR_DELAY: 00748 ipv6_neighbour_set_state(cache, cur, IP_NEIGHBOUR_PROBE); 00749 ipv6_interface_resolve_send_ns(cache, cur, true, 0); 00750 break; 00751 case IP_NEIGHBOUR_PROBE: 00752 if (cur->retrans_count >= MARK_UNREACHABLE - 1) { 00753 if (cur->from_redirect) { 00754 ipv6_neighbour_gone(cache, cur->ip_address); 00755 ipv6_neighbour_entry_remove(cache, cur); 00756 break; 00757 } else { 00758 ipv6_neighbour_set_state(cache, cur, IP_NEIGHBOUR_UNREACHABLE); 00759 } 00760 } 00761 /* no break */ 00762 case IP_NEIGHBOUR_UNREACHABLE: 00763 if (cur->retrans_count < 0xFF) { 00764 cur->retrans_count++; 00765 } 00766 00767 if (cur->retrans_count >= MAX_UNICAST_SOLICIT && cur->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00768 ipv6_neighbour_entry_remove(cache, cur); 00769 } else { 00770 ipv6_interface_resolve_send_ns(cache, cur, true, cur->retrans_count); 00771 if (cur->retrans_count >= MAX_UNICAST_SOLICIT - 1) { 00772 /* "Final" unicast probe */ 00773 if (cur->type == IP_NEIGHBOUR_GARBAGE_COLLECTIBLE) { 00774 /* Only wait 1 initial retrans time for response to final probe - don't want backoff in this case */ 00775 cur->timer = cache->retrans_timer; 00776 } else { 00777 /* We're not going to remove this. Let's stop the timer. We'll restart to probe once more if it's used */ 00778 cur->timer = 0; 00779 } 00780 } else { 00781 /* Backoff for the next probe */ 00782 cur->timer = next_probe_time(cache, cur->retrans_count); 00783 } 00784 } 00785 break; 00786 } 00787 } 00788 } 00789 00790 void ipv6_destination_cache_print(route_print_fn_t *print_fn) 00791 { 00792 print_fn("Destination Cache:"); 00793 ns_list_foreach(ipv6_destination_t, entry, &ipv6_destination_cache) { 00794 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str); 00795 print_fn(" %s (life %u)", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, entry->destination), entry->lifetime); 00796 #ifdef HAVE_IPV6_ND 00797 if (entry->redirected) { 00798 print_fn(" Redirect %s%%%u", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, entry->redirect_addr), entry->interface_id); 00799 } 00800 #endif 00801 #ifndef NO_IPV6_PMTUD 00802 print_fn(" PMTU %u (life %u)", entry->pmtu, entry->pmtu_lifetime); 00803 #endif 00804 } 00805 } 00806 00807 static ipv6_destination_t *ipv6_destination_lookup(const uint8_t *address, int8_t interface_id) 00808 { 00809 bool is_ll = addr_is_ipv6_link_local(address); 00810 00811 if (is_ll && interface_id == -1) { 00812 return NULL; 00813 } 00814 00815 ns_list_foreach(ipv6_destination_t, cur, &ipv6_destination_cache) { 00816 if (!addr_ipv6_equal(cur->destination, address)) { 00817 continue; 00818 } 00819 /* For LL addresses, interface ID must also be compared */ 00820 if (is_ll && cur->interface_id != interface_id) { 00821 continue; 00822 } 00823 00824 return cur; 00825 } 00826 00827 return NULL; 00828 } 00829 00830 /* Unlike original version, this does NOT perform routing check - it's pure destination cache look-up 00831 * 00832 * We no longer attempt to cache route lookups in the destination cache, as 00833 * assumption that routing look-ups are keyed purely by destination is no longer 00834 * true. If necessary, a caching layer could be placed into 00835 * ipv6_route_choose_next_hop. 00836 * 00837 * Interface IDs are a little tricky here. Current situation is that we 00838 * require an interface ID for <=realm-local addresses, and it's ignored for 00839 * other addresses. That prevents us having multiple Destination Cache entries 00840 * for one global address. 00841 */ 00842 ipv6_destination_t *ipv6_destination_lookup_or_create(const uint8_t *address, int8_t interface_id) 00843 { 00844 uint_fast16_t count = 0; 00845 ipv6_destination_t *entry = NULL; 00846 bool interface_specific = addr_ipv6_scope(address, NULL) <= IPV6_SCOPE_REALM_LOCAL; 00847 00848 if (interface_specific && interface_id == -1) { 00849 return NULL; 00850 } 00851 00852 /* Find any existing entry */ 00853 ns_list_foreach(ipv6_destination_t, cur, &ipv6_destination_cache) { 00854 count++; 00855 if (!addr_ipv6_equal(cur->destination, address)) { 00856 continue; 00857 } 00858 /* For LL addresses, interface ID must also be compared */ 00859 if (interface_specific && cur->interface_id != interface_id) { 00860 continue; 00861 } 00862 00863 entry = cur; 00864 break; 00865 } 00866 00867 00868 if (!entry) { 00869 if (count > current_max_cache) { 00870 entry = ns_list_get_last(&ipv6_destination_cache); 00871 ns_list_remove(&ipv6_destination_cache, entry); 00872 ipv6_destination_release(entry); 00873 } 00874 00875 /* If no entry, make one */ 00876 entry = ns_dyn_mem_alloc(sizeof(ipv6_destination_t)); 00877 if (!entry) { 00878 return NULL; 00879 } 00880 memcpy(entry->destination, address, 16); 00881 entry->refcount = 1; 00882 #ifdef HAVE_IPV6_ND 00883 entry->redirected = false; 00884 #endif 00885 entry->last_neighbour = NULL; 00886 #ifndef NO_IPV6_PMTUD 00887 entry->pmtu = 0xffff; 00888 entry->pmtu_lifetime = 0; 00889 #endif 00890 #ifndef NO_IP_FRAGMENT_TX 00891 entry->fragment_id = randLIB_get_32bit(); 00892 #endif 00893 if (interface_specific) { 00894 entry->interface_id = interface_id; 00895 } else { 00896 entry->interface_id = -1; 00897 } 00898 ns_list_add_to_start(&ipv6_destination_cache, entry); 00899 } else if (entry != ns_list_get_first(&ipv6_destination_cache)) { 00900 /* If there was an entry, and it wasn't at the start, move it */ 00901 ns_list_remove(&ipv6_destination_cache, entry); 00902 ns_list_add_to_start(&ipv6_destination_cache, entry); 00903 } 00904 00905 if (addr_ipv6_scope(address, NULL) <= IPV6_SCOPE_LINK_LOCAL) { 00906 entry->lifetime = DCACHE_GC_AGE_LL; 00907 } else { 00908 entry->lifetime = DCACHE_GC_AGE; 00909 } 00910 00911 return entry; 00912 } 00913 00914 00915 /* Force re-evaluation of next hop for all entries using the specified next hop as 00916 * a router. Will keep using it for direct comms. 00917 */ 00918 static void ipv6_destination_cache_forget_router(ipv6_neighbour_cache_t *ncache, const uint8_t neighbour_addr[static 16]) 00919 { 00920 ipv6_neighbour_t *neighbour = ipv6_neighbour_lookup(ncache, neighbour_addr); 00921 00922 ns_list_foreach(ipv6_destination_t, entry, &ipv6_destination_cache) { 00923 if (entry->last_neighbour && entry->interface_id == ncache->interface_id && entry->last_neighbour == neighbour) { 00924 entry->last_neighbour = NULL; 00925 } 00926 #ifdef HAVE_IPV6_ND 00927 if (entry->redirected && entry->interface_id == ncache->interface_id && addr_ipv6_equal(entry->redirect_addr, neighbour_addr)) { 00928 entry->redirected = false; 00929 } 00930 #endif 00931 } 00932 } 00933 00934 static void ipv6_destination_cache_forget_neighbour(const ipv6_neighbour_t *neighbour) 00935 { 00936 ns_list_foreach(ipv6_destination_t, entry, &ipv6_destination_cache) { 00937 if (entry->last_neighbour == neighbour) { 00938 entry->last_neighbour = NULL; 00939 } 00940 } 00941 } 00942 00943 #ifdef HAVE_IPV6_ND 00944 void ipv6_destination_redirect(const uint8_t *dest_addr, const uint8_t *sender_addr, const uint8_t *redirect_addr, int8_t interface_id, addrtype_t ll_type, const uint8_t *ll_address) 00945 { 00946 ipv6_destination_t *dest_entry = ipv6_destination_lookup_or_create(dest_addr, interface_id); 00947 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(interface_id); 00948 bool to_router; 00949 00950 if (!dest_entry || !ncache) { 00951 tr_warn("Redirect failure - no dest entry/ncache"); 00952 return; 00953 } 00954 00955 if (!dest_entry->last_neighbour || dest_entry->interface_id != interface_id || !addr_ipv6_equal(dest_entry->last_neighbour->ip_address, sender_addr)) { 00956 tr_warn("Redirect not sent from current next hop"); 00957 return; 00958 } 00959 00960 if (addr_ipv6_equal(redirect_addr, dest_addr)) { 00961 /* We're being told it is on-link */ 00962 to_router = false; 00963 } else if (addr_is_ipv6_link_local(redirect_addr)) { 00964 /* We're being sent to a different router */ 00965 to_router = true; 00966 } else { 00967 tr_debug("Invalid redirection: %s", trace_ipv6(redirect_addr)); 00968 return; 00969 } 00970 00971 // XXX need to consider invalidating/preserving other information? 00972 // Possibly not as we should only be handling this if not a router, so no 00973 // possibility of screwing up RPL. Although the "am I a router" check isn't 00974 // in place... 00975 dest_entry->redirected = true; 00976 memcpy(dest_entry->redirect_addr, redirect_addr, 16); 00977 00978 ipv6_neighbour_t *ncache_entry = NULL; 00979 00980 if (ll_type != ADDR_NONE ) { 00981 ncache_entry = ipv6_neighbour_update_unsolicited(ncache, redirect_addr, ll_type, ll_address); 00982 if (ncache_entry) { 00983 ncache_entry->from_redirect = true; 00984 } 00985 } 00986 00987 if (to_router) { 00988 if (!ncache_entry) { 00989 ncache_entry = ipv6_neighbour_lookup(ncache, redirect_addr); 00990 } 00991 00992 if (ncache_entry) { 00993 ncache_entry->is_router = true; 00994 } 00995 } 00996 00997 tr_debug("Redirection added"); 00998 tr_debug("Iface %d destination: %s", interface_id, trace_ipv6(dest_addr)); 00999 tr_debug("Old next hop: %s", trace_ipv6(sender_addr)); 01000 tr_debug("New next hop: %s", trace_ipv6(redirect_addr)); 01001 } 01002 #endif 01003 01004 static void ipv6_destination_release(ipv6_destination_t *dest) 01005 { 01006 if (--dest->refcount == 0) { 01007 ns_dyn_mem_free(dest); 01008 } 01009 } 01010 01011 static void ipv6_destination_cache_gc_periodic(void) 01012 { 01013 uint_fast16_t gc_count = 0; 01014 ns_list_foreach_safe(ipv6_destination_t, entry, &ipv6_destination_cache) { 01015 if (entry->lifetime) { 01016 entry->lifetime--; 01017 } 01018 gc_count++; 01019 #ifndef NO_IPV6_PMTUD 01020 /* Purge old PMTU values */ 01021 if (entry->pmtu_lifetime) { 01022 if (entry->pmtu_lifetime <= DCACHE_GC_PERIOD) { 01023 tr_info("Resetting PMTU for: %s", trace_ipv6(entry->destination)); 01024 entry->pmtu_lifetime = 0; 01025 uint16_t old_mtu = entry->pmtu; 01026 if (entry->interface_id >= 0) { 01027 entry->pmtu = ipv6_neighbour_cache_by_interface_id(entry->interface_id)->link_mtu; 01028 } else { 01029 entry->pmtu = 0xffff; 01030 } 01031 if (entry->pmtu != old_mtu) { 01032 // socket_pmtu_changed(entry->destination, entry->interface_id, old_mtu, entry->pmtu); 01033 } 01034 } else { 01035 entry->pmtu_lifetime -= DCACHE_GC_PERIOD; 01036 } 01037 } 01038 #endif 01039 } 01040 01041 if (gc_count <= cache_long_term(true)) { 01042 return; 01043 } 01044 01045 /* Cache is in most-recently-used-first order. GC strategy is to start from 01046 * the back, and reduce the size to "MAX_SHORT_TERM" every GC period, 01047 * deleting any entry. Timed-out entries will be deleted to keep it to 01048 * MAX_LONG_TERM. 01049 */ 01050 ns_list_foreach_reverse_safe(ipv6_destination_t, entry, &ipv6_destination_cache) { 01051 if (entry->lifetime == 0 || gc_count > cache_short_term(true)) { 01052 ns_list_remove(&ipv6_destination_cache, entry); 01053 ipv6_destination_release(entry); 01054 if (--gc_count <= cache_long_term(true)) { 01055 break; 01056 } 01057 } 01058 } 01059 01060 } 01061 01062 void ipv6_destination_cache_timer(uint8_t seconds) 01063 { 01064 dcache_gc_timer += seconds; 01065 01066 if (dcache_gc_timer >= DCACHE_GC_PERIOD) { 01067 dcache_gc_timer -= DCACHE_GC_PERIOD; 01068 ipv6_destination_cache_gc_periodic(); 01069 //ipv6_destination_cache_print(trace_debug_print); 01070 //ipv6_route_table_print(trace_debug_print); 01071 } 01072 } 01073 01074 static const char *route_src_names[] = { 01075 [ROUTE_ANY] = "?", 01076 [ROUTE_STATIC] = "Static", 01077 [ROUTE_USER] = "User", 01078 [ROUTE_LOOPBACK] = "Loopback", 01079 [ROUTE_RADV] = "RAdv", 01080 [ROUTE_ARO] = "ARO", 01081 [ROUTE_RPL_DAO] = "RPL DAO", 01082 [ROUTE_RPL_DAO_SR] = "RPL DAO SR", 01083 [ROUTE_RPL_SRH] = "RPL SRH", 01084 [ROUTE_RPL_DIO] = "RPL DIO", 01085 [ROUTE_RPL_ROOT] = "RPL Root", 01086 [ROUTE_RPL_INSTANCE] = "RPL Instance", 01087 [ROUTE_RPL_FWD_ERROR] = "RPL Fwd-Error", 01088 [ROUTE_MULTICAST] = "Multicast", 01089 [ROUTE_MPL] = "MPL", 01090 [ROUTE_RIP] = "RIP", 01091 [ROUTE_THREAD] = "Thread", 01092 [ROUTE_THREAD_BORDER_ROUTER] = "Thread BR", 01093 [ROUTE_THREAD_PROXIED_HOST] = "Thread Proxy", 01094 [ROUTE_REDIRECT] = "Redirect", 01095 }; 01096 01097 /* Which types of routes get probed as per RFC 4191 */ 01098 /* (Others are assumed to be always reachable) */ 01099 static const bool ipv6_route_probing[ROUTE_MAX] = { 01100 [ROUTE_RADV] = true, 01101 [ROUTE_RPL_DAO] = true, 01102 [ROUTE_RPL_DIO] = true, 01103 [ROUTE_RPL_ROOT] = true, 01104 [ROUTE_RPL_INSTANCE] = true, 01105 }; 01106 01107 /* Which route types get minimum link MTU by default */ 01108 /* Makes life easier for tunnel-based systems like RPL */ 01109 static const bool ipv6_route_min_mtu[ROUTE_MAX] = { 01110 [ROUTE_RPL_DAO] = true, 01111 [ROUTE_RPL_DAO_SR] = true, 01112 [ROUTE_RPL_DIO] = true, 01113 [ROUTE_RPL_ROOT] = true, 01114 [ROUTE_RPL_INSTANCE] = true, 01115 [ROUTE_MPL] = true, 01116 }; 01117 01118 // Remember when a route source has deleted an entry - allows buffers still in 01119 // event queue to have their route info invalidated. 01120 static bool ipv6_route_source_invalidated[ROUTE_MAX]; 01121 01122 static ipv6_route_predicate_fn_t *ipv6_route_predicate[ROUTE_MAX]; 01123 static ipv6_route_next_hop_fn_t *ipv6_route_next_hop_computation[ROUTE_MAX]; 01124 01125 void ipv6_route_table_set_predicate_fn(ipv6_route_src_t src, ipv6_route_predicate_fn_t fn) 01126 { 01127 ipv6_route_predicate[src] = fn; 01128 } 01129 01130 void ipv6_route_table_set_next_hop_fn(ipv6_route_src_t src, ipv6_route_next_hop_fn_t fn) 01131 { 01132 ipv6_route_next_hop_computation[src] = fn; 01133 } 01134 01135 static void ipv6_route_print(const ipv6_route_t *route, route_print_fn_t *print_fn) 01136 { 01137 // Route prefix is variable-length, so need to zero pad for ip6tos 01138 uint8_t addr[16] = { 0 }; 01139 bitcopy(addr, route->prefix, route->prefix_len); 01140 ROUTE_PRINT_ADDR_STR_BUFFER_INIT(addr_str); 01141 if (route->lifetime != 0xFFFFFFFF) { 01142 print_fn(" %24s/%-3u if:%u src:'%s' id:%d lifetime:%"PRIu32, 01143 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, route->info.interface_id, 01144 route_src_names[route->info.source], route->info.source_id, route->lifetime 01145 ); 01146 } else { 01147 print_fn(" %24s/%-3u if:%u src:'%s' id:%d lifetime:infinite", 01148 ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, addr), route->prefix_len, route->info.interface_id, 01149 route_src_names[route->info.source], route->info.source_id 01150 ); 01151 } 01152 if (route->on_link) { 01153 print_fn(" On-link (met %d)", total_metric(route)); 01154 } else { 01155 print_fn(" next-hop %s (met %d)", ROUTE_PRINT_ADDR_STR_FORMAT(addr_str, route->info.next_hop_addr), total_metric(route)); 01156 } 01157 } 01158 01159 void ipv6_route_table_print(route_print_fn_t *print_fn) 01160 { 01161 print_fn("Routing table:"); 01162 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01163 ipv6_route_print(r, print_fn); 01164 } 01165 } 01166 01167 /* 01168 * This function returns total effective metric, which is a combination 01169 * of 1) route metric, and 2) interface metric. Can be extended to include 01170 * protocol metric as well in the future. 01171 */ 01172 static uint16_t total_metric(const ipv6_route_t *route) 01173 { 01174 ipv6_neighbour_cache_t *cache; 01175 uint16_t metric; 01176 01177 metric = route->metric; 01178 cache = ipv6_neighbour_cache_by_interface_id(route->info.interface_id); 01179 01180 if (cache) { 01181 metric += cache->route_if_info.metric; 01182 } 01183 01184 return metric; 01185 } 01186 01187 #ifdef FEA_TRACE_SUPPORT 01188 void trace_debug_print(const char *fmt, ...) 01189 { 01190 va_list ap; 01191 va_start(ap, fmt); 01192 vtracef(TRACE_LEVEL_DEBUG, TRACE_GROUP, fmt, ap); 01193 va_end(ap); 01194 } 01195 #endif 01196 01197 static void ipv6_route_entry_remove(ipv6_route_t *route) 01198 { 01199 tr_debug("Deleted route:"); 01200 #ifdef FEA_TRACE_SUPPORT 01201 ipv6_route_print(route, trace_debug_print); 01202 #endif 01203 if (protocol_core_buffers_in_event_queue > 0) { 01204 // Alert any buffers in the queue already routed by this source 01205 ipv6_route_source_invalidated[route->info.source] = true; 01206 } 01207 ns_list_remove(&ipv6_routing_table, route); 01208 ns_dyn_mem_free(route); 01209 } 01210 01211 static bool ipv6_route_same_router(const ipv6_route_t *a, const ipv6_route_t *b) 01212 { 01213 if (a == b) { 01214 return true; 01215 } 01216 return !a->on_link && !b->on_link && 01217 a->info.interface_id == b->info.interface_id && 01218 addr_ipv6_equal(a->info.next_hop_addr, b->info.next_hop_addr); 01219 } 01220 01221 static void ipv6_route_probe(ipv6_route_t *route) 01222 { 01223 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(route->info.interface_id); 01224 if (!ncache || !ncache->send_nud_probes || route->probe_timer) { 01225 return; 01226 } 01227 01228 ipv6_neighbour_t *n = ipv6_neighbour_lookup_or_create(ncache, route->info.next_hop_addr); 01229 if (!n) { 01230 return; 01231 } 01232 ipv6_interface_resolve_send_ns(ncache, n, true, 0); 01233 01234 /* We need to limit to once per minute *per router* - so set the hold-off 01235 * timer for *all* routing entries to this router 01236 */ 01237 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01238 if (ipv6_route_same_router(r, route)) { 01239 r->probe_timer = 60; 01240 r->probe = false; 01241 } 01242 } 01243 } 01244 01245 /* Return true is a is better than b */ 01246 static bool ipv6_route_is_better(const ipv6_route_t *a, const ipv6_route_t *b) 01247 { 01248 /* Prefer longer prefix */ 01249 if (a->prefix_len < b->prefix_len) { 01250 return false; 01251 } 01252 01253 if (a->prefix_len > b->prefix_len) { 01254 return true; 01255 } 01256 01257 /* Prefer on-link */ 01258 if (b->on_link && !a->on_link) { 01259 return false; 01260 } 01261 01262 if (a->on_link && !b->on_link) { 01263 return true; 01264 } 01265 01266 /* If prefixes exactly equal, tiebreak by metric */ 01267 return total_metric(a) < total_metric(b); 01268 } 01269 01270 /* Find the "best" route regardless of reachability, but respecting the skip flag and predicates */ 01271 static ipv6_route_t *ipv6_route_find_best(const uint8_t *addr, int8_t interface_id, ipv6_route_predicate_fn_t *predicate) 01272 { 01273 ipv6_route_t *best = NULL; 01274 ns_list_foreach(ipv6_route_t, route, &ipv6_routing_table) { 01275 /* We mustn't be skipping this route */ 01276 if (route->search_skip) { 01277 continue; 01278 } 01279 01280 /* Interface must match, if caller specified */ 01281 if (interface_id != -1 && interface_id != route->info.interface_id) { 01282 continue; 01283 } 01284 01285 /* Prefix must match */ 01286 if (!bitsequal(addr, route->prefix, route->prefix_len)) { 01287 continue; 01288 } 01289 01290 /* Check the predicate for the route itself. This allows, 01291 * RPL "root" routes (the instance defaults) to be ignored in normal 01292 * lookup. Note that for caching to work properly, we require 01293 * the route predicate to produce "constant" results. 01294 */ 01295 bool valid = true; 01296 if (ipv6_route_predicate[route->info.source]) { 01297 valid = ipv6_route_predicate[route->info.source](&route->info, valid); 01298 } 01299 01300 /* Then the supplied search-specific predicate can override */ 01301 if (predicate) { 01302 valid = predicate(&route->info, valid); 01303 } 01304 01305 /* If blocked by either predicate, skip */ 01306 if (!valid) { 01307 continue; 01308 } 01309 01310 if (!best || ipv6_route_is_better(route, best)) { 01311 best = route; 01312 } 01313 } 01314 return best; 01315 } 01316 01317 ipv6_route_t *ipv6_route_choose_next_hop(const uint8_t *dest, int8_t interface_id, ipv6_route_predicate_fn_t *predicate) 01318 { 01319 ipv6_route_t *best = NULL; 01320 bool reachable = false; 01321 bool need_to_probe = false; 01322 01323 ns_list_foreach(ipv6_route_t, route, &ipv6_routing_table) { 01324 route->search_skip = false; 01325 } 01326 01327 /* Search algorithm from RFC 4191, S3.2: 01328 * 01329 * When a type C host does next-hop determination and consults its 01330 * Routing Table for an off-link destination, it searches its routing 01331 * table to find the route with the longest prefix that matches the 01332 * destination, using route preference values as a tie-breaker if 01333 * multiple matching routes have the same prefix length. If the best 01334 * route points to a non-reachable router, this router is remembered for 01335 * the algorithm described in Section 3.5 below, and the next best route 01336 * is consulted. This check is repeated until a matching route is found 01337 * that points to a reachable router, or no matching routes remain. 01338 * 01339 * Note that rather than having a separate on-link Prefix List, we have 01340 * on-link entries. These take precedence over default routes (by their 01341 * non-0 length), but not necessarily over more-specific routes. Therefore 01342 * it is possible that we consider a few non-reachable routers first, then 01343 * fall back to on-link. This behaviour may or may not be desired, depending 01344 * on the scenario. If not desired, the router entries should have their 01345 * "probing" flag set to false, so they always take precedence over 01346 * the on-link entry. 01347 * 01348 * There is currently no mechanism for an on-link entry to always take 01349 * precedence over a more-specific route, which is what would happen if 01350 * we really did have a separate Prefix List and Routing Table. One 01351 * possibility would be a special precedence flag. 01352 */ 01353 for (;;) { 01354 ipv6_route_t *route = ipv6_route_find_best(dest, interface_id, predicate); 01355 if (!route) { 01356 break; 01357 } 01358 01359 if (route->on_link) { 01360 reachable = true; 01361 } else { 01362 /* Some routes (eg RPL SR) compute next hop on demand */ 01363 if (ipv6_route_next_hop_computation[route->info.source]) { 01364 if (!ipv6_route_next_hop_computation[route->info.source](dest, &route->info)) { 01365 route->search_skip = true; 01366 continue; 01367 } 01368 } 01369 01370 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(route->info.interface_id); 01371 if (!ncache) { 01372 tr_warn("Invalid interface ID in routing table!"); 01373 route->search_skip = true; 01374 continue; 01375 } 01376 01377 if (ncache->send_nud_probes && ipv6_route_probing[route->info.source]) { 01378 /* Going via a router - check reachability, as per RFC 4191. 01379 * This only applies for certain routes (currently those from RAs) */ 01380 reachable = ipv6_neighbour_addr_is_probably_reachable(ncache, route->info.next_hop_addr); 01381 } else { 01382 /* Can't probe, so have to assume router is reachable */ 01383 reachable = true; 01384 } 01385 } 01386 01387 if (reachable) { 01388 /* If router is reachable, we'll take it now */ 01389 best = route; 01390 break; 01391 } else { 01392 /* Otherwise, note it, and look for other less-good reachable ones */ 01393 route->search_skip = true; 01394 01395 /* As we would have used it, probe to check for reachability */ 01396 route->probe = need_to_probe = true; 01397 01398 if (!best) { 01399 best = route; 01400 } 01401 continue; 01402 } 01403 } 01404 01405 /* This is a bit icky - data structures are routes, but we need to probe 01406 * routers - a many->1 mapping. Probe flag is set on all routes we skipped; 01407 * but we don't want to probe the router we actually chose. 01408 */ 01409 if (need_to_probe) { 01410 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01411 if (!r->probe) { 01412 continue; 01413 } 01414 r->probe = false; 01415 01416 /* Note that best must be set if need_to_probe is */ 01417 if (!ipv6_route_same_router(r, best) && ipv6_route_is_better(r, best)) { 01418 ipv6_route_probe(r); 01419 } 01420 } 01421 } 01422 01423 if (best && !reachable) { 01424 /* We've chosen a non-reachable router; this means no routers were 01425 * reachable. Move it to the bottom of the list, so that next time 01426 * we do this, we try (and hence probe) another non-reachable router, 01427 * otherwise we'll never make progress. This satisfies the 01428 * round-robin requirement in RFC 4861 6.3.6.2, enhanced for RFC 4191. 01429 */ 01430 ns_list_remove(&ipv6_routing_table, best); 01431 ns_list_add_to_end(&ipv6_routing_table, best); 01432 } 01433 01434 return best; 01435 } 01436 01437 ipv6_route_t *ipv6_route_lookup_with_info(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source, void *info, int_fast16_t src_id) 01438 { 01439 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01440 if (interface_id == r->info.interface_id && prefix_len == r->prefix_len && bitsequal(prefix, r->prefix, prefix_len)) { 01441 if (source != ROUTE_ANY) { 01442 if (source != r->info.source) { 01443 continue; 01444 } 01445 if (info && info != r->info.info) { 01446 continue; 01447 } 01448 if (src_id != -1 && src_id != r->info.source_id) { 01449 continue; 01450 } 01451 if (info && ipv6_route_next_hop_computation[source]) { 01452 /* No need to match the actual next hop - we assume info distinguishes */ 01453 return r; 01454 } 01455 } 01456 01457 /* "next_hop" being NULL means on-link; this is a flag in the route entry, and r->next_hop can't be NULL */ 01458 if ((next_hop && r->on_link) || (!next_hop && !r->on_link)) { 01459 continue; 01460 } 01461 01462 if (next_hop && !r->on_link && !addr_ipv6_equal(next_hop, r->info.next_hop_addr)) { 01463 continue; 01464 } 01465 01466 return r; 01467 } 01468 } 01469 01470 return NULL; 01471 } 01472 01473 #define PREF_TO_METRIC(pref) (128 - 64 * (pref)) 01474 01475 uint8_t ipv6_route_pref_to_metric(int_fast8_t pref) 01476 { 01477 if (pref <-1 || pref > +1) { 01478 pref = 0; 01479 } 01480 return PREF_TO_METRIC(pref); 01481 } 01482 01483 ipv6_route_t *ipv6_route_add(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source, uint32_t lifetime, int_fast8_t pref) 01484 { 01485 return ipv6_route_add_with_info(prefix, prefix_len, interface_id, next_hop, source, NULL, 0, lifetime, pref); 01486 } 01487 01488 ipv6_route_t *ipv6_route_add_with_info(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source, void *info, uint8_t source_id, uint32_t lifetime, int_fast8_t pref) 01489 { 01490 /* Only support -1, 0 and +1 prefs, as per RFC 4191 */ 01491 if (pref < -1 || pref > +1) { 01492 return NULL; 01493 } 01494 01495 return ipv6_route_add_metric(prefix, prefix_len, interface_id, next_hop, source, info, source_id, lifetime, PREF_TO_METRIC(pref)); 01496 } 01497 01498 ipv6_route_t *ipv6_route_add_metric(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source, void *info, uint8_t source_id, uint32_t lifetime, uint8_t metric) 01499 { 01500 ipv6_route_t *route = NULL; 01501 enum { UNCHANGED, UPDATED, NEW } changed_info = UNCHANGED; 01502 01503 // Maybe don't need this after all? We'll just assume that the next_hop is on-link 01504 // Thread certainly wants to use ULAs... 01505 #if 0 01506 if (next_hop) { 01507 /* Currently we require that all routes must be to link-local addresses. */ 01508 /* This simplifies all sorts of things - particularly that we can assume link-local addresses to be on-link. */ 01509 /* It is needed to make Redirects and probes work too. */ 01510 if (!addr_is_ipv6_link_local(next_hop)) { 01511 return NULL; 01512 } 01513 } 01514 #endif 01515 01516 01517 /* Check for matching info, in which case it's an update */ 01518 route = ipv6_route_lookup_with_info(prefix, prefix_len, interface_id, next_hop, source, info, source_id); 01519 01520 /* 0 lifetime is a deletion request (common to all protocols) */ 01521 if (lifetime == 0) { 01522 if (route) { 01523 tr_debug("Zero route lifetime"); 01524 ipv6_route_entry_remove(route); 01525 } 01526 return NULL; 01527 } 01528 01529 uint8_t max_entries = ipv6_route_table_get_max_entries(interface_id, source); 01530 if (max_entries > 0) { 01531 uint8_t entries = ipv6_route_table_count_source(interface_id, source); 01532 if (entries > max_entries) { 01533 ipv6_route_table_remove_last_one_from_source(interface_id, source); 01534 } 01535 } 01536 01537 if (!route) { /* new route */ 01538 uint_fast8_t prefix_bytes = (prefix_len + 7u) / 8u; 01539 route = ns_dyn_mem_alloc(sizeof(ipv6_route_t) + prefix_bytes); 01540 if (!route) { 01541 return NULL; 01542 } 01543 memset(route->prefix, 0, prefix_bytes); 01544 bitcopy(route->prefix, prefix, prefix_len); 01545 route->prefix_len = prefix_len; 01546 route->search_skip = false; 01547 route->probe = false; 01548 route->probe_timer = 0; 01549 route->lifetime = lifetime; 01550 route->metric = metric; 01551 route->info.source = source; 01552 route->info.info = info; 01553 route->info.source_id = source_id; 01554 route->info.interface_id = interface_id; 01555 route->info.pmtu = ipv6_route_min_mtu[source] ? IPV6_MIN_LINK_MTU : 0xFFFF; 01556 if (next_hop) { 01557 route->on_link = false; 01558 memcpy(route->info.next_hop_addr, next_hop, 16); 01559 } else { 01560 route->on_link = true; 01561 memset(route->info.next_hop_addr, 0, 16); 01562 } 01563 01564 /* See ipv6_route_probe - all routing entries to the same router 01565 * want to share the same hold-off time, so search and copy. 01566 */ 01567 if (next_hop) { 01568 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01569 if (ipv6_route_same_router(r, route)) { 01570 route->probe_timer = r->probe_timer; 01571 break; 01572 } 01573 } 01574 } 01575 01576 /* Routing table will be resorted during use, thanks to probing. */ 01577 /* Doesn't matter much where they start off, but put them at the */ 01578 /* beginning so new routes tend to get tried first. */ 01579 ns_list_add_to_start(&ipv6_routing_table, route); 01580 changed_info = NEW; 01581 } else { /* updating a route - only lifetime and metric can be changing */ 01582 route->lifetime = lifetime; 01583 if (metric != route->metric) { 01584 route->metric = metric; 01585 changed_info = UPDATED; 01586 } 01587 } 01588 01589 if (changed_info != UNCHANGED) { 01590 tr_debug("%s route:", changed_info == NEW ? "Added" : "Updated"); 01591 #ifdef FEA_TRACE_SUPPORT 01592 ipv6_route_print(route, trace_debug_print); 01593 #endif 01594 } 01595 01596 return route; 01597 } 01598 01599 int_fast8_t ipv6_route_delete(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source) 01600 { 01601 return ipv6_route_delete_with_info(prefix, prefix_len, interface_id, next_hop, source, NULL, 0); 01602 } 01603 01604 int_fast8_t ipv6_route_delete_with_info(const uint8_t *prefix, uint8_t prefix_len, int8_t interface_id, const uint8_t *next_hop, ipv6_route_src_t source, void *info, int_fast16_t source_id) 01605 { 01606 ipv6_route_t *route = ipv6_route_lookup_with_info(prefix, prefix_len, interface_id, next_hop, source, info, source_id); 01607 if (!route) { 01608 return -1; 01609 } 01610 01611 ipv6_route_entry_remove(route); 01612 return 0; 01613 } 01614 01615 void ipv6_route_table_remove_interface(int8_t interface_id) 01616 { 01617 ns_list_foreach_safe(ipv6_route_t, r, &ipv6_routing_table) { 01618 if (interface_id == r->info.interface_id) { 01619 ipv6_route_entry_remove(r); 01620 } 01621 } 01622 } 01623 01624 static void ipv6_route_table_remove_router(int8_t interface_id, const uint8_t *addr, ipv6_route_src_t source) 01625 { 01626 ns_list_foreach_safe(ipv6_route_t, r, &ipv6_routing_table) { 01627 if (interface_id == r->info.interface_id && r->info.source == source && !r->on_link && addr_ipv6_equal(addr, r->info.next_hop_addr)) { 01628 ipv6_route_entry_remove(r); 01629 } 01630 } 01631 } 01632 01633 /* Somewhat specialised - allow on-the-fly modification of metrics. Masking 01634 * allows for top "preference" bits to be preserved. 01635 */ 01636 void ipv6_route_table_modify_router_metric(int8_t interface_id, const uint8_t *addr, ipv6_route_src_t source, uint8_t keep, uint8_t toggle) 01637 { 01638 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01639 if (interface_id == r->info.interface_id && r->info.source == source && !r->on_link && addr_ipv6_equal(addr, r->info.next_hop_addr)) { 01640 r->metric = (r->metric & keep) ^ toggle; 01641 } 01642 } 01643 01644 } 01645 01646 void ipv6_route_table_remove_info(int8_t interface_id, ipv6_route_src_t source, void *info) 01647 { 01648 ns_list_foreach_safe(ipv6_route_t, r, &ipv6_routing_table) { 01649 if ((interface_id == -1 || interface_id == r->info.interface_id) && r->info.source == source && (info == NULL || r->info.info == info)) { 01650 ipv6_route_entry_remove(r); 01651 } 01652 } 01653 } 01654 01655 static uint8_t ipv6_route_table_count_source(int8_t interface_id, ipv6_route_src_t source) 01656 { 01657 uint8_t count = 0; 01658 ns_list_foreach(ipv6_route_t, r, &ipv6_routing_table) { 01659 if (interface_id == r->info.interface_id && r->info.source == source) { 01660 count++; 01661 if (count == 0xff) { 01662 break; 01663 } 01664 } 01665 } 01666 return count; 01667 } 01668 01669 static void ipv6_route_table_remove_last_one_from_source(int8_t interface_id, ipv6_route_src_t source) 01670 { 01671 // Removes last i.e. oldest entry */ 01672 ns_list_foreach_reverse(ipv6_route_t, r, &ipv6_routing_table) { 01673 if (interface_id == r->info.interface_id && r->info.source == source) { 01674 ipv6_route_entry_remove(r); 01675 break; 01676 } 01677 } 01678 } 01679 01680 01681 void ipv6_route_table_ttl_update(uint16_t seconds) 01682 { 01683 ns_list_foreach_safe(ipv6_route_t, r, &ipv6_routing_table) { 01684 if (r->probe_timer) { 01685 if (r->probe_timer > seconds) { 01686 r->probe_timer -= seconds; 01687 } else { 01688 r->probe_timer = 0; 01689 } 01690 } 01691 01692 if (r->lifetime == 0xFFFFFFFF) { 01693 continue; 01694 } 01695 01696 if (r->lifetime > seconds) { 01697 r->lifetime -= seconds; 01698 continue; 01699 } 01700 01701 tr_debug("Route expired"); 01702 ipv6_route_entry_remove(r); 01703 } 01704 } 01705 01706 void ipv6_route_table_set_max_entries(int8_t interface_id, ipv6_route_src_t source, uint8_t max_entries) 01707 { 01708 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(interface_id); 01709 01710 if (ncache) { 01711 ncache->route_if_info.sources[source] = max_entries; 01712 } 01713 } 01714 01715 static uint8_t ipv6_route_table_get_max_entries(int8_t interface_id, ipv6_route_src_t source) 01716 { 01717 ipv6_neighbour_cache_t *ncache = ipv6_neighbour_cache_by_interface_id(interface_id); 01718 01719 if (ncache) { 01720 return ncache->route_if_info.sources[source]; 01721 } 01722 01723 return 0; 01724 } 01725 01726 bool ipv6_route_table_source_was_invalidated(ipv6_route_src_t src) 01727 { 01728 return ipv6_route_source_invalidated[src]; 01729 } 01730 01731 // Called when event queue is empty - no pending buffers so can clear invalidation flags. 01732 void ipv6_route_table_source_invalidated_reset(void) 01733 { 01734 memset(ipv6_route_source_invalidated, false, sizeof ipv6_route_source_invalidated); 01735 }
Generated on Tue Jul 12 2022 14:23:51 by
