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