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