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.
Fork of OmniWheels by
rpl_data.c
00001 /* 00002 * Copyright (c) 2015-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 /* rpl_data.c deals with handling of the packet data (hop-by-hop header) 00019 * and Source Routing headers in RPL-routed packets. 00020 * 00021 * rpl_domain_t is accessible, but not normally manipulated - all routines in 00022 * this file works on a specific instance. 00023 * 00024 * rpl_instance_t, rpl_dodag_t, rpl_dodag_version_t, rpl_neighbour_t are all accessible. 00025 */ 00026 00027 #include "nsconfig.h" 00028 00029 #ifdef HAVE_RPL 00030 00031 #include "common_functions.h" 00032 #include "nsdynmemLIB.h" 00033 #include "ns_trace.h" 00034 #include <string.h> 00035 00036 #include "Core/include/ns_buffer.h" 00037 #include "NWK_INTERFACE/Include/protocol.h" 00038 #include "NWK_INTERFACE/Include/protocol_stats.h" 00039 #include "Common_Protocols/ipv6.h" 00040 #include "Common_Protocols/ipv6_resolution.h" 00041 #include "Common_Protocols/icmpv6.h" 00042 00043 #include "RPL/rpl_protocol.h" 00044 #include "RPL/rpl_upward.h" 00045 #include "RPL/rpl_downward.h" 00046 #include "RPL/rpl_structures.h" 00047 #include "RPL/rpl_policy.h" 00048 #include "RPL/rpl_data.h" 00049 00050 #define TRACE_GROUP "RPLa" 00051 00052 #define RPL_DATA_SR_INIT_SIZE (16*4) 00053 00054 #ifdef HAVE_RPL_ROOT 00055 typedef struct rpl_data_sr { 00056 rpl_dao_target_t *target; /* Target - note may be a prefix */ 00057 uint16_t iaddr_size; 00058 uint8_t ihops; /* Number of intermediate hops (= addresses in SRH) */ 00059 uint8_t final_dest[16]; /* Final destination (used only temporarily during header construction) */ 00060 uint8_t iaddr[]; /* Intermediate address list is built backwards, contiguous with final_dest */ 00061 } rpl_data_sr_t; 00062 00063 static rpl_data_sr_t *rpl_data_sr; 00064 #endif 00065 00066 static const uint8_t *rpl_data_get_dodagid(const buffer_t *buf); 00067 00068 bool rpl_data_is_rpl_route(ipv6_route_src_t source) { 00069 switch (source) { 00070 case ROUTE_RPL_DAO: 00071 case ROUTE_RPL_DAO_SR: 00072 case ROUTE_RPL_SRH: 00073 case ROUTE_RPL_DIO: 00074 case ROUTE_RPL_INSTANCE: 00075 case ROUTE_RPL_ROOT: 00076 case ROUTE_RPL_FWD_ERROR: 00077 return true; 00078 default: 00079 return false; 00080 } 00081 } 00082 00083 bool rpl_data_is_rpl_parent_route(ipv6_route_src_t source) { 00084 switch (source) { 00085 case ROUTE_RPL_DIO: 00086 case ROUTE_RPL_INSTANCE: 00087 case ROUTE_RPL_ROOT: 00088 return true; 00089 default: 00090 return false; 00091 } 00092 } 00093 00094 static bool rpl_data_is_rpl_downward_route(ipv6_route_src_t source) { 00095 switch (source) { 00096 case ROUTE_RPL_DAO: 00097 case ROUTE_RPL_DAO_SR: 00098 case ROUTE_RPL_SRH: 00099 return true; 00100 default: 00101 return false; 00102 } 00103 } 00104 00105 static bool rpl_data_handle_fwd_error(buffer_t *buf, protocol_interface_info_entry_t *cur, uint8_t *opt, const sockaddr_t *ll_src) 00106 { 00107 if (!ll_src) { 00108 tr_warn("Forwarding-Error - dst=%s, neighbour unknown", trace_ipv6(buf->dst_sa .address )); 00109 return false; 00110 } else { 00111 tr_warn("Forwarding-Error - dst=%s, neighbour=%s", trace_ipv6(buf->dst_sa .address ), trace_sockaddr(ll_src, true)); 00112 } 00113 00114 rpl_instance_t *instance = rpl_lookup_instance(cur->rpl_domain, opt[1], rpl_data_get_dodagid(buf)); 00115 if (!instance) { 00116 return false; 00117 } 00118 00119 #if 1 00120 return false; 00121 #else 00122 /* Work needed */ 00123 if (rpl_instance_am_root(instance)) { 00124 /* We are looking for a target that has us as its transit */ 00125 } 00126 bool deleted = ipv6_route_delete_by_info_and_ll(buf->dst_sa .address , ROUTE_RPL_DAO, ll_src); 00127 deleted |= ipv6_route_delete_by_info_and_ll(buf->dst_sa .address , ROUTE_RPL_DAO_SR, ll_src); 00128 opt[0] &=~ RPL_OPT_FWD_ERROR; 00129 00130 return true; 00131 #endif 00132 } 00133 00134 bool rpl_data_process_hbh(buffer_t *buf, protocol_interface_info_entry_t *cur, uint8_t *opt, const sockaddr_t *ll_src) 00135 { 00136 buf->rpl_instance = opt[1]; 00137 buf->rpl_instance_known = true; 00138 00139 /* Act on the forwarding error */ 00140 if (opt[0] & RPL_OPT_FWD_ERROR) { 00141 if (!rpl_data_handle_fwd_error(buf, cur, opt, ll_src)) { 00142 return false; 00143 } 00144 } 00145 00146 /* We don't actually do much now. If the packet is addressed 00147 * to us, we don't need really need much (or any) info. 00148 * 00149 * If we are going to forward forwarding_down will find the option 00150 * manually. If we come to unwrap the tunnel, then we need to take a 00151 * copy before the outer IP header is stripped. 00152 */ 00153 buf->options .ip_extflags |= IPEXT_HBH_RPL; 00154 buf->options .need_predecessor = true; 00155 buf->rpl_flag_error = opt[0]; 00156 00157 return true; 00158 } 00159 00160 /* We assume the packet is basically well-formed, as it will have either 00161 * cleared initial input parsing, or we formed it ourselves. hbh and srh 00162 * are set to point to the RPL Hop-by-Hop option and/or RPL Source Routing 00163 * Header, if present. 00164 */ 00165 static void rpl_data_locate_info(buffer_t *buf, uint8_t **hbh, uint8_t **srh) 00166 { 00167 uint8_t *ptr = buffer_data_pointer(buf); 00168 uint16_t len = buffer_data_length(buf); 00169 00170 if (hbh) { 00171 *hbh = NULL; 00172 } 00173 if (srh) { 00174 *srh = NULL; 00175 } 00176 00177 if (len < IPV6_HDRLEN) { 00178 return; 00179 } 00180 uint16_t ip_len = common_read_16_bit(ptr + IPV6_HDROFF_PAYLOAD_LENGTH); 00181 uint8_t nh = ptr[6]; 00182 ptr += IPV6_HDRLEN; 00183 len -= IPV6_HDRLEN; 00184 if (ip_len > len) { 00185 return; 00186 } 00187 len = ip_len; 00188 while (len) { 00189 uint16_t hdrlen; 00190 switch (nh) { 00191 case IPV6_NH_HOP_BY_HOP: 00192 { 00193 if (len < 8) { 00194 return; 00195 } 00196 nh = ptr[0]; 00197 hdrlen = (ptr[1] + 1) * 8; 00198 /* Move on if they're not interested in HbH (looking for SRH) */ 00199 if (!hbh) { 00200 break; 00201 } 00202 if (hdrlen > len) { 00203 return; 00204 } 00205 uint8_t *opt_ptr = ptr + 2; 00206 uint8_t *opt_end = ptr + hdrlen; 00207 while (opt_ptr < opt_end) { 00208 switch (opt_ptr[0]) { 00209 case IPV6_OPTION_PAD1: 00210 opt_ptr++; 00211 break; 00212 case IPV6_OPTION_RPL: 00213 *hbh = opt_ptr; 00214 goto found_option; 00215 default: 00216 opt_ptr += 2 + opt_ptr[1]; 00217 break; 00218 } 00219 } 00220 found_option: 00221 /* If they're not looking for SRH, finish now */ 00222 if (!srh) { 00223 return; 00224 } 00225 break; 00226 } 00227 case IPV6_NH_DEST_OPT: 00228 // Destination option permitted to appear before routing 00229 if (len < 8) { 00230 return; 00231 } 00232 nh = ptr[0]; 00233 hdrlen = (ptr[1] + 1) * 8; 00234 /* If they're not looking for SRH, finish now - past HbH */ 00235 if (!srh) { 00236 return; 00237 } 00238 break; 00239 case IPV6_NH_ROUTING: 00240 if (!srh) { 00241 return; 00242 } 00243 if (ptr[2] == IPV6_ROUTING_TYPE_RPL) { 00244 *srh = ptr; 00245 } 00246 // No need to examine past routing header 00247 return; 00248 default: 00249 // No other headers can appear before routing - last we care about 00250 return; 00251 } 00252 if (hdrlen > len) { 00253 return; 00254 } 00255 ptr += hdrlen; 00256 len -= hdrlen; 00257 } 00258 return; 00259 } 00260 00261 bool rpl_data_remember_outer(buffer_t *buf) 00262 { 00263 /* We're stripping the IP header - need the HBH header for future reference */ 00264 uint8_t *hbh; 00265 rpl_data_locate_info(buf, &hbh, NULL); 00266 if (hbh) { 00267 uint8_t instance_id = hbh[3]; 00268 /* For local instances, also need to extract the DODAG ID from src/dst */ 00269 bool local = rpl_instance_id_is_local(instance_id); 00270 /* Copy the length byte and the option data (and optionally DODAG ID) */ 00271 buf->rpl_option = ns_dyn_mem_temporary_alloc(hbh[1] + 1 + (local ? 16 : 0)); 00272 if (buf->rpl_option) { 00273 memcpy(buf->rpl_option, hbh + 1, hbh[1] + 1); 00274 if (local) { 00275 uint8_t *dodagid = instance_id & RPL_INSTANCE_DEST ? buf->dst_sa .address : buf->src_sa .address ; 00276 memcpy(buf->rpl_option + hbh[1] + 1, dodagid, 16); 00277 } 00278 } 00279 } 00280 00281 if ((buf->options .ip_extflags & IPEXT_HBH_RPL) && !buf->rpl_option) { 00282 tr_warn("RPL tunnel exit HbH fail"); 00283 return false; 00284 } 00285 00286 return true; 00287 } 00288 00289 /* Get the DODAG ID if it's a local DODAG packet */ 00290 static const uint8_t *rpl_data_get_dodagid(const buffer_t *buf) 00291 { 00292 if (!buf->rpl_instance_known || rpl_instance_id_is_global(buf->rpl_instance)) { 00293 return NULL; 00294 } 00295 /* rpl_data_remember_outer() stores it in the rpl_option metatdata */ 00296 if (buf->rpl_option) { 00297 return buf->rpl_option + 1 + buf->rpl_option[1]; 00298 } 00299 else { 00300 return buf->rpl_instance & RPL_INSTANCE_DEST ? buf->dst_sa .address 00301 : buf->src_sa .address ; 00302 } 00303 } 00304 00305 /* 00306 * 0 1 2 3 00307 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00308 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00309 * | Option Type | Opt Data Len | 00310 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00311 * |O|R|F|0|0|0|0|0| RPLInstanceID | SenderRank | 00312 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00313 * | (sub-TLVs) | 00314 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00315 * 00316 * Figure 1: RPL Option 00317 */ 00318 static buffer_t *rpl_data_exthdr_provider_hbh_2(buffer_t *buf, rpl_instance_t *instance, rpl_neighbour_t *neighbour, ipv6_exthdr_stage_t stage, int16_t *result) 00319 { 00320 ipv6_route_info_t *route_info = &buf->route->route_info; 00321 00322 /* This can be called both for routes which only use HbH headers (eg DIO) 00323 * as well as one-hop DAO_SR routes which would normally use source routing 00324 * headers, if there was more than one hop. For DAO_SR, neighbour will be 00325 * NULL. 00326 */ 00327 00328 rpl_dodag_t *dodag = rpl_instance_current_dodag(instance); 00329 if (!dodag) { 00330 *result = -1; 00331 return buf; 00332 } 00333 00334 bool destination_in_instance = false; 00335 uint16_t ext_size = 0; 00336 if (addr_ipv6_equal(route_info->next_hop_addr, buf->dst_sa .address ) || 00337 addr_ipv6_equal(buf->dst_sa .address , dodag->id)) { 00338 destination_in_instance = true; 00339 00340 if (buf->rpl_option) { 00341 /* Forwarding an existing option - preserve it */ 00342 uint8_t opt_size = buf->rpl_option[0]; 00343 ext_size = 2 + opt_size; 00344 ext_size = (ext_size + 7) &~ 7; 00345 } else { 00346 /* Generating our own option - fixed size, no TLVs */ 00347 ext_size = 8; 00348 } 00349 } 00350 00351 switch (stage) { 00352 case IPV6_EXTHDR_SIZE: 00353 *result = ext_size; 00354 return buf; 00355 00356 case IPV6_EXTHDR_INSERT: { 00357 if (!destination_in_instance) { 00358 /* We don't add a header - we'll do it on the tunnel */ 00359 *result = 0; 00360 return buf; 00361 } 00362 buf = buffer_headroom(buf, ext_size); 00363 if (!buf) { 00364 return NULL; 00365 } 00366 uint8_t *ext = buffer_data_reserve_header(buf, ext_size); 00367 ext[0] = buf->options .type ; 00368 buf->options .type = IPV6_NH_HOP_BY_HOP; 00369 ext[1] = ext_size / 8 - 1; 00370 uint8_t *opt = ext + 2; 00371 opt[0] = IPV6_OPTION_RPL; 00372 if (buf->rpl_option) { 00373 /* Get back the RPL option we stripped off an outer IP header */ 00374 memcpy(opt + 1, buf->rpl_option, 1 + buf->rpl_option[0]); 00375 ns_dyn_mem_free(buf->rpl_option); 00376 buf->rpl_option = NULL; 00377 } else { 00378 opt[1] = 4; // option length 00379 opt[2] = 0; // placeholder 00380 opt[3] = instance->id; 00381 /* For upwards routes we can deduce that DODAGID must be 00382 * the destination, so set the D flag. 00383 */ 00384 if (rpl_instance_id_is_local(instance->id) && !rpl_data_is_rpl_downward_route(route_info->source)) { 00385 opt[3] |= RPL_INSTANCE_DEST; 00386 } 00387 common_write_16_bit(RPL_RANK_INFINITE, opt + 4); // SenderRank (placeholder) 00388 } 00389 /* Pad HbH header if necessary. */ 00390 uint8_t pad_len = ext + ext_size - (opt + 2 + opt[1]); 00391 if (pad_len == 1) { 00392 opt[0] = IPV6_OPTION_PAD1; 00393 } else if (pad_len > 1) { 00394 opt[0] = IPV6_OPTION_PADN; 00395 opt[1] = pad_len - 2; 00396 memset(opt + 2, 0, pad_len - 2); 00397 } 00398 // don't forget to set the "RPL option present" marker 00399 buf->options .ip_extflags |= IPEXT_HBH_RPL; 00400 *result = 0; 00401 return buf; 00402 } 00403 00404 case IPV6_EXTHDR_MODIFY: { 00405 uint8_t *opt; 00406 uint16_t sender_rank; 00407 00408 rpl_data_locate_info(buf, &opt, NULL); 00409 if (!opt) { 00410 *result = IPV6_EXTHDR_MODIFY_TUNNEL; 00411 // Tunnel to next hop in general case, but if going to DODAGID, 00412 // it can tunnel all the way (and it HAS to if it is a local 00413 // DODAG). 00414 if (!addr_ipv6_equal(buf->dst_sa .address , dodag->id)) { 00415 memcpy(buf->dst_sa .address , route_info->next_hop_addr, 16); 00416 } 00417 buf->src_sa .addr_type = ADDR_NONE ; // force auto-selection 00418 return buf; 00419 } 00420 00421 if (buf->ip_routed_up) { 00422 /* Check for rank errors - RFC 6550 11.2.2.2. */ 00423 /* Note that RPL spec does not say that packets from nodes of 00424 * equal rank are errors, but we treat them as such to get 00425 * reliable sibling loop detection - we require sender rank to be 00426 * strictly less for Down packets and strictly greater for Up. 00427 */ 00428 sender_rank = common_read_16_bit(opt + 4); 00429 rpl_cmp_t cmp = rpl_rank_compare_dagrank_rank(dodag, sender_rank, instance->current_rank); 00430 rpl_cmp_t expected_cmp = (opt[2] & RPL_OPT_DOWN) ? RPL_CMP_LESS : RPL_CMP_GREATER; 00431 if (cmp != expected_cmp) { 00432 /* Set the Rank-Error bit; if already set, drop */ 00433 if (opt[2] & RPL_OPT_RANK_ERROR) { 00434 protocol_stats_update(STATS_RPL_ROUTELOOP, 1); 00435 tr_info("Forwarding inconsistency R"); 00436 rpl_instance_inconsistency(instance); 00437 *result = -1; 00438 return buf; 00439 } else { 00440 opt[2] |= RPL_OPT_RANK_ERROR; 00441 } 00442 } 00443 } 00444 00445 if (buf->rpl_flag_error & RPL_OPT_FWD_ERROR) { 00446 opt[2] |= RPL_OPT_FWD_ERROR; 00447 } else if (rpl_data_is_rpl_downward_route(route_info->source)) { 00448 opt[2] |= RPL_OPT_DOWN; 00449 } else { 00450 opt[2] &= ~RPL_OPT_DOWN; 00451 } 00452 00453 /* Set the D flag for local instances */ 00454 if (rpl_instance_id_is_local(instance->id)) { 00455 if (addr_ipv6_equal(dodag->id, buf->dst_sa .address )) { 00456 opt[3] |= RPL_INSTANCE_DEST; 00457 } else if (addr_ipv6_equal(dodag->id, buf->src_sa .address )) { 00458 opt[3] &=~ RPL_INSTANCE_DEST; 00459 } else { 00460 tr_error("Local instance invalid %s[%d]: %s -> %s", trace_ipv6(dodag->id), instance->id, trace_ipv6(buf->src_sa .address ), trace_ipv6(buf->dst_sa .address )); 00461 *result = -1; 00462 return buf; 00463 } 00464 } 00465 00466 /* RPL 11.2.2.2. says we set SenderRank to infinite when forwarding 00467 * across a version discontinuity. (Must be up - we don't know versions 00468 * of downward routes). 00469 */ 00470 if ((buf->rpl_flag_error & RPL_OPT_FWD_ERROR) || rpl_data_is_rpl_downward_route(route_info->source) || !neighbour || neighbour->dodag_version == instance->current_dodag_version) { 00471 sender_rank = nrpl_dag_rank(dodag, instance->current_rank); 00472 } else { 00473 sender_rank = RPL_RANK_INFINITE; 00474 } 00475 common_write_16_bit(sender_rank, opt + 4); 00476 *result = 0; 00477 return buf; 00478 } 00479 default: 00480 return buffer_free(buf); 00481 } 00482 } 00483 00484 static buffer_t *rpl_data_exthdr_provider_hbh(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result) 00485 { 00486 ipv6_route_info_t *route_info = &buf->route->route_info; 00487 rpl_neighbour_t *neighbour = route_info->info; 00488 00489 rpl_instance_t *instance = rpl_neighbour_instance(neighbour); 00490 if (!instance) { 00491 *result = -1; 00492 return buf; 00493 } 00494 00495 return rpl_data_exthdr_provider_hbh_2(buf, instance, neighbour, stage, result); 00496 } 00497 00498 static buffer_t *rpl_data_exthdr_provider_fwd_error_hbh(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result) 00499 { 00500 ipv6_route_info_t *route_info = &buf->route->route_info; 00501 rpl_instance_t *instance =route_info->info; 00502 00503 return rpl_data_exthdr_provider_hbh_2(buf, instance, NULL, stage, result); 00504 00505 } 00506 00507 /* This could live in address.c or ipv6.c */ 00508 /* Can get false negatives if we don't already have a neighbour cache entry, but in practice 00509 * will be solid as we are basically matching link-local 6LoWPAN addresses, which can be mapped 00510 * to link-layer without an existing entry. Could conceivably get a false positive if we have 00511 * a stale entry and MAC addresses have been reassigned, but very unlikely. 00512 */ 00513 static bool rpl_downward_ip_addr_matches_ll_addr(protocol_interface_info_entry_t *cur, const uint8_t ip_addr_a[static 16], const sockaddr_t *ll_addr_b) 00514 { 00515 if (!ll_addr_b) { 00516 return false; 00517 } 00518 addrtype_t ll_type_a; 00519 const uint8_t *ll_addr_a; 00520 if (!ipv6_map_ip_to_ll(cur, NULL, ip_addr_a, &ll_type_a, &ll_addr_a)) { 00521 return false; 00522 } 00523 00524 return ll_type_a == ll_addr_b->addr_type && 00525 memcmp(ll_addr_a, ll_addr_b->address , addr_len_from_type(ll_type_a)) == 0; 00526 } 00527 00528 /* ROUTE_RPL_INSTANCE routes are the default for the instance - not valid, unless 00529 * instance is already known. 00530 */ 00531 static bool rpl_data_route_predicate_instance_default(const ipv6_route_info_t *route, bool valid) 00532 { 00533 (void)route; 00534 (void)valid; 00535 00536 return false; 00537 } 00538 00539 static rpl_instance_t *predicate_instance; 00540 static bool predicate_down; 00541 static const sockaddr_t *predicate_predecessor; 00542 00543 /* Override predicate for choosing routes given a specific instance (ie when 00544 * forwarding a packet that had a specified instance ID). That instance is 00545 * loaded into the static predicate_instance above. 00546 */ 00547 static bool rpl_data_route_predicate_specific_instance(const ipv6_route_info_t *route, bool valid) 00548 { 00549 /* We will permit forwarding out of RPL into a non-RPL interface (eg border routers) */ 00550 /* XXX - what if we're the boundary between two RPL domains? */ 00551 protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(route->interface_id); 00552 if (!cur || !cur->rpl_domain) { 00553 return valid; 00554 } 00555 00556 /* If forwarding onto the same interface, think more */ 00557 switch (route->source) { 00558 /* These are the type of routes we will forward onto out of RPL - they 00559 * must include all types of non-owned routes that could be advertised 00560 * through a DAO. 00561 */ 00562 case ROUTE_ARO: 00563 return valid; 00564 00565 /* Upward routes */ 00566 case ROUTE_RPL_DIO: 00567 case ROUTE_RPL_INSTANCE: 00568 case ROUTE_RPL_ROOT: { 00569 /* Packets going down can't go back up */ 00570 if (predicate_down) { 00571 return false; 00572 } 00573 /* Never route to predecessor */ 00574 if (rpl_downward_ip_addr_matches_ll_addr(cur, route->next_hop_addr, predicate_predecessor)) { 00575 return false; 00576 } 00577 /* Info for these upward routes is a back pointer to the neighbour */ 00578 rpl_neighbour_t *neighbour = route->info; 00579 /* From there, we can get the instance info */ 00580 rpl_instance_t *instance = rpl_neighbour_instance(neighbour); 00581 /* Note that this overrides the default "false" for INSTANCE routes */ 00582 return instance == predicate_instance; 00583 } 00584 00585 /* Downward routes */ 00586 case ROUTE_RPL_DAO: 00587 case ROUTE_RPL_DAO_SR: { 00588 /* Info for these downward routes is a back pointer to the target */ 00589 rpl_dao_target_t *target = route->info; 00590 /* Going to predecessor is fine if it was going up - we are reversing to down. Otherwise block */ 00591 if (predicate_down) { 00592 if (rpl_downward_ip_addr_matches_ll_addr(cur, route->next_hop_addr, predicate_predecessor)) { 00593 return false; 00594 } 00595 } 00596 return target->instance == predicate_instance; 00597 } 00598 /* Unknown */ 00599 default: 00600 return false; 00601 } 00602 } 00603 00604 ipv6_route_predicate_fn_t *rpl_data_get_route_predicate(rpl_domain_t *domain, const buffer_t *buf) 00605 { 00606 const uint8_t *dodagid = rpl_data_get_dodagid(buf); 00607 00608 predicate_instance = rpl_lookup_instance(domain, buf->rpl_instance, dodagid); 00609 predicate_down = buf->rpl_flag_error & RPL_OPT_DOWN; 00610 predicate_predecessor = buf->predecessor ; 00611 00612 return rpl_data_route_predicate_specific_instance; 00613 } 00614 00615 /* Returns true if we are going to pass this back with the 'F' bit set */ 00616 /* If we return false, a "no route" ICMP error will occur as normal */ 00617 /* For the ICMP error case, we may treat as a DIO Trickle inconsistency, as 00618 * suggested by RFC 6550 11.1(8) */ 00619 bool rpl_data_forwarding_error(buffer_t *buf) 00620 { 00621 /* This is called when we have no route to send a packet - first check 00622 * if we were forwarding it for RPL - indicated by rpl_instance_known. 00623 */ 00624 if (!buf->rpl_instance_known) { 00625 return false; 00626 } 00627 00628 protocol_interface_info_entry_t *cur = buf->interface ; 00629 if (!cur) { 00630 return false; 00631 } 00632 00633 rpl_instance_t *instance = rpl_lookup_instance(cur->rpl_domain, buf->rpl_instance, rpl_data_get_dodagid(buf)); 00634 if (!instance) { 00635 tr_err("rpl_data_forwarding_error: unknown instance"); 00636 return false; 00637 } 00638 00639 /* To use Forwarding-Error, packet must be Down, and must know predecessor */ 00640 if (!((buf->rpl_flag_error & RPL_OPT_DOWN) && buf->predecessor )) { 00641 goto not_forwarding_error; 00642 } 00643 00644 /* Must then be able to map predecessor to IP address */ 00645 uint8_t predecessor_ip[16]; 00646 if (!ipv6_map_ll_to_ip_link_local(cur, buf->predecessor ->addr_type , buf->predecessor ->address , predecessor_ip)) { 00647 return false; 00648 } 00649 00650 buf->rpl_flag_error |= RPL_OPT_FWD_ERROR; 00651 buffer_free_route(buf); 00652 if (!ipv6_buffer_route_to(buf, predecessor_ip, buf->interface )) { 00653 return false; 00654 } 00655 00656 buf->route->route_info.info = instance; 00657 buf->route->route_info.source = ROUTE_RPL_FWD_ERROR; 00658 00659 return true; 00660 00661 not_forwarding_error: 00662 /* If we're not signalling a Forwarding-Error, we will be sending 00663 * an ICMP Destination Unreachable to the source as normal. But we 00664 * may still want to attempt some sort of RPL repair. If it was coming 00665 * upwards, and we're _not_ the root of the instance, a failure to 00666 * pass it on can only mean some sort of RPL routing problem (eg refusing 00667 * to pass to the predecessor), as we should normally be able to send 00668 * towards the DODAG root. We attempt to aid repair by triggering 00669 * a DIO inconsistency. 00670 * 00671 * If we are the root, then it will just be a perfectly normal 00672 * "destination unreachable" - it doesn't suggest a RPL repair is needed. 00673 */ 00674 if (!(buf->rpl_flag_error & RPL_OPT_DOWN) && !rpl_instance_am_root(instance)) { 00675 protocol_stats_update(STATS_RPL_ROUTELOOP, 1); 00676 tr_info("Forwarding inconsistency 2"); 00677 rpl_instance_inconsistency(instance); 00678 } 00679 return false; 00680 } 00681 00682 #ifdef HAVE_RPL_ROOT 00683 /* TODO - every target involved here should be non-External. Add checks */ 00684 static bool rpl_data_compute_source_route(const uint8_t *final_dest, rpl_dao_target_t * const target) 00685 { 00686 if (!rpl_data_sr) { 00687 rpl_data_sr = rpl_alloc(sizeof(rpl_data_sr_t) + RPL_DATA_SR_INIT_SIZE); 00688 if (!rpl_data_sr) { 00689 return false; 00690 } 00691 rpl_data_sr->iaddr_size = RPL_DATA_SR_INIT_SIZE; 00692 rpl_data_sr->target = NULL; 00693 } else if (rpl_data_sr->target == target && addr_ipv6_equal(rpl_data_sr->final_dest, final_dest)) { 00694 return true; 00695 } 00696 00697 /* This does all the heavy lifting - after running, the optimum path from 00698 * every target node is at the front of the transit list, and the connected 00699 * flag is set if we've any prospect. 00700 */ 00701 rpl_downward_compute_paths(target->instance); 00702 if (!target->connected) { 00703 return false; 00704 } 00705 00706 /* Wipe the "data valid" marker */ 00707 rpl_data_sr->target = NULL; 00708 rpl_data_sr->ihops = 0; 00709 00710 /* Final destination written explicitly (last target could be a prefix) */ 00711 memcpy(rpl_data_sr->final_dest, final_dest, 16); 00712 00713 /* We just work backwards from the target, following the first transit 00714 * each time, which is the shortest path after the compute_paths call. 00715 */ 00716 rpl_dao_target_t *t = target; 00717 for (;;) { 00718 /* First transit is best path, thanks to root computation above */ 00719 rpl_dao_root_transit_t *transit = ns_list_get_first(&t->info.root.transits); 00720 rpl_dao_target_t *parent = transit->parent; 00721 /* Finished if we hit NULL - ourselves */ 00722 if (parent == NULL) { 00723 /* Mark "valid" */ 00724 rpl_data_sr->target = target; 00725 return true; 00726 } 00727 if (!parent->connected) { 00728 tr_err("Parent %s disconnected", trace_ipv6_prefix(parent->prefix, parent->prefix_len)); 00729 return false; 00730 } 00731 /* Check transit address isn't already in table. Should not be possible */ 00732 for (int i = 16 * rpl_data_sr->ihops; i >= 0; i -= 16){ 00733 if (addr_ipv6_equal(rpl_data_sr->final_dest + i, transit->transit)) { 00734 protocol_stats_update(STATS_RPL_ROUTELOOP, 1); 00735 tr_err("SR loop %s->%s", trace_ipv6_prefix(t->prefix, t->prefix_len), trace_ipv6(transit->transit)); 00736 return false; 00737 } 00738 } 00739 /* Increase size of table if necessary */ 00740 if (16 * (rpl_data_sr->ihops + 1) > rpl_data_sr->iaddr_size) { 00741 rpl_data_sr = rpl_realloc(rpl_data_sr, sizeof(rpl_data_sr_t) + rpl_data_sr->iaddr_size, sizeof(rpl_data_sr_t) + 2 * rpl_data_sr->iaddr_size); 00742 if (!rpl_data_sr) { 00743 return false; 00744 } 00745 rpl_data_sr->iaddr_size *= 2; 00746 } 00747 memcpy(rpl_data_sr->iaddr + 16 * rpl_data_sr->ihops, transit->transit, 16); 00748 rpl_data_sr->ihops += 1; 00749 00750 t = parent; 00751 } 00752 } 00753 00754 /* Return the next hop, if there is an intermediate. If it's direct, NULL 00755 * is returned. This call must follow a successful call to 00756 * rpl_data_compute_source_route(). 00757 */ 00758 const uint8_t *rpl_data_sr_next_hop(void) 00759 { 00760 if (rpl_data_sr->ihops == 0) { 00761 return NULL; 00762 } 00763 return rpl_data_sr->iaddr + 16 * (rpl_data_sr->ihops - 1); 00764 } 00765 00766 static bool rpl_data_route_next_hop(const uint8_t *dest, ipv6_route_info_t *route) 00767 { 00768 rpl_dao_target_t *target = route->info; 00769 00770 if (!rpl_data_compute_source_route(dest, target)) { 00771 return false; 00772 } 00773 00774 const uint8_t *next_hop = rpl_data_sr_next_hop(); 00775 if (next_hop) { 00776 memcpy(route->next_hop_addr, next_hop, 16); 00777 } else { 00778 memcpy(route->next_hop_addr, dest, 16); 00779 } 00780 00781 return true; 00782 } 00783 00784 void rpl_data_sr_invalidate(void) 00785 { 00786 if (rpl_data_sr) { 00787 rpl_data_sr->target = NULL; 00788 rpl_data_sr->ihops = 0; 00789 } 00790 /* We could invalidate the next hops remembered in the system routing table. 00791 * but it's not necessary - recomputation happens every time. Does mean that 00792 * the routing table printout may contain stale info, though. 00793 */ 00794 } 00795 00796 typedef struct rpl_srh_info 00797 { 00798 uint8_t hlen; 00799 uint8_t segments; 00800 uint8_t cmprI; 00801 uint8_t cmprE; 00802 uint8_t pad; 00803 } rpl_srh_info_t; 00804 00805 /* Count matching bytes (max 15) for SRH compression */ 00806 static uint_fast8_t rpl_data_matching_addr_bytes(const uint8_t *a, const uint8_t *b, uint_fast8_t len) 00807 { 00808 uint_fast8_t m = 0; 00809 while (m < len && a[m] == b[m]) { 00810 m++; 00811 } 00812 return m; 00813 } 00814 00815 static const rpl_srh_info_t *rpl_data_sr_compute_header_size(const uint8_t final_dest[16], uint8_t hop_limit) 00816 { 00817 static rpl_srh_info_t info; 00818 uint8_t hops = 1 + rpl_data_sr->ihops; 00819 if (hops > hop_limit) { 00820 hops = hop_limit; 00821 } 00822 if (hops <= 1) { 00823 return NULL; 00824 } 00825 memcpy(rpl_data_sr->final_dest, final_dest, 16); 00826 /* first_hop is the address that will go into the IP destination */ 00827 const uint8_t *first_hop = rpl_data_sr->iaddr + 16 * (rpl_data_sr->ihops - 1); 00828 /* addr is the first address for the SRH */ 00829 const uint8_t *addr = first_hop - 16; 00830 00831 /* Must be at least 2 hops, so at least 1 segment in the SRH */ 00832 info.segments = hops - 1; 00833 00834 /* First, scan for compression of all except last against initial destination */ 00835 /* (CmprI bytes will remain unchanged at each hop, rest can change) */ 00836 info.cmprI = 15; 00837 for (uint8_t seg = 0; seg < info.segments - 1; seg++) { 00838 info.cmprI = rpl_data_matching_addr_bytes(addr, first_hop, info.cmprI); 00839 hops--; 00840 addr -= 16; 00841 } 00842 00843 /* Compress last hop against previous destination */ 00844 /* Debatable whether we should let cmprE be > cmprI - it means the final 00845 * address won't be IP_dest[0:cmprI)+Address_n[cmprI:64) until the final 00846 * hop (segments left = 1): 00847 * 00848 * CmprI = 14, CmprE = 15 00849 * IP dest Segs Left Addresses 00850 * x:1234 3 2345, 3456, 22 <- meaning "3422", not "1222" 00851 * x:2345 2 1234, 3456, 22 00852 * x:3456 1 1234, 2345, 22 00853 * x:3422 0 1234, 2345, 56 00854 * 00855 * But then similar issues arise if cmprE < cmprI: 00856 * 00857 * CmprI = 15, CmprE = 14 00858 * IP dest Segs Left Addresses 00859 * x:1234 3 45, 56, ABCD 00860 * x:1245 2 34, 56, ABCD 00861 * x:1256 1 34, 45, ABCD 00862 * x:ABCD 0 34, 45, 1256 <- "45" means "1245", not "AB45" 00863 * 00864 * Basically, there's no loss of information, but it's not as straightforward 00865 * as RFC 6554 says. If cmprI != cmprE, not all Address entries represent 00866 * addresses with the same prefix as the IP destination at any given 00867 * instant. (But the next address to process does line up with the current 00868 * IP destination). 00869 * 00870 */ 00871 info.cmprE = rpl_data_matching_addr_bytes(addr, addr + 16, 15 /* info.cmprI */); 00872 00873 uint16_t total_size; 00874 00875 total_size = (16 - info.cmprE) + (16 - info.cmprI) * (info.segments - 1); 00876 if (total_size & 7) { 00877 info.pad = 8 - (total_size & 7); 00878 total_size += info.pad; 00879 } else { 00880 info.pad = 0; 00881 } 00882 info.hlen = total_size >> 3; 00883 00884 return &info; 00885 } 00886 00887 /* 00888 * 0 1 2 3 00889 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 00890 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00891 * | Next Header | Hdr Ext Len | Routing Type | Segments Left | 00892 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00893 * | CmprI | CmprE | Pad | Reserved | 00894 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00895 * | | 00896 * . . 00897 * . Addresses[1..n] . 00898 * . . 00899 * | | 00900 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00901 */ 00902 static uint8_t *rpl_data_sr_write_header(const rpl_srh_info_t *info, uint8_t *ptr, uint8_t nh) 00903 { 00904 ptr[0] = nh; 00905 ptr[1] = info->hlen; 00906 ptr[2] = IPV6_ROUTING_TYPE_RPL; 00907 ptr[3] = info->segments; 00908 ptr[4] = (info->cmprI << 4) | info->cmprE; 00909 ptr[5] = (info->pad << 4); 00910 common_write_16_bit(0, ptr + 6); 00911 ptr += 8; 00912 const uint8_t *addr = rpl_data_sr->iaddr + 16 * (rpl_data_sr->ihops - 2); 00913 for (int n = 0; n < info->segments - 1; n++) { 00914 memcpy(ptr, addr + info->cmprI, 16 - info->cmprI); 00915 ptr += 16 - info->cmprI; 00916 addr -= 16; 00917 } 00918 memcpy(ptr, addr + info->cmprE, 16 - info->cmprE); 00919 ptr += 16 - info->cmprE; 00920 if (info->pad) { 00921 memset(ptr, 0, info->pad); 00922 ptr += info->pad; 00923 } 00924 return ptr; 00925 } 00926 00927 static buffer_t *rpl_data_exthdr_provider_srh(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result) 00928 { 00929 ipv6_route_info_t *route_info = &buf->route->route_info; 00930 rpl_dao_target_t *target = route_info->info; 00931 rpl_instance_t *instance = target->instance; 00932 if (!instance) { 00933 *result = -1; 00934 return buf; 00935 } 00936 00937 uint16_t ext_size = 0; 00938 const rpl_srh_info_t *srh_info = NULL; 00939 00940 const uint8_t *final_rpl_dest = buf->dst_sa .address ; 00941 00942 if (target->external) { 00943 /* If we haven't yet tunnelled, then there's no insertion */ 00944 if (!buf->options .tunnelled ) { 00945 if (stage == IPV6_EXTHDR_SIZE || stage == IPV6_EXTHDR_INSERT) { 00946 *result = 0; 00947 return buf; 00948 } 00949 } 00950 00951 /* If it's an external target, we need to only go as far as its transit */ 00952 /* Modify target to point to that instead */ 00953 rpl_dao_root_transit_t *transit = ns_list_get_first(&target->info.root.transits); 00954 if (!transit) { 00955 *result = -1; 00956 return buf; 00957 } 00958 final_rpl_dest = transit->transit; 00959 target = rpl_instance_match_dao_target(instance, final_rpl_dest, 128); 00960 if (!target) { 00961 *result = -1; 00962 return buf; 00963 } 00964 } 00965 00966 if (!rpl_data_compute_source_route(final_rpl_dest, target)) { 00967 *result = -1; 00968 return buf; 00969 } 00970 00971 /* When tunnelling (only), we truncate the route in the outer packet, 00972 * according to the hop limit, so it exits the tunnel at the hop limit - 00973 * that router will then generate "time exceeded" on the inner packet. 00974 * (RFC 6554 4.1). When not tunnelling, we include all hops regardless, 00975 * which means the final destination is there as needed. 00976 */ 00977 srh_info = rpl_data_sr_compute_header_size(final_rpl_dest, buf->options .tunnelled && buf->options .type == IPV6_NH_IPV6 ? buf->options .hop_limit : 0xFF); 00978 if (!srh_info) { 00979 /* No source routing header required - this must be because it's one hop. */ 00980 /* In this case, we do need to add a HbH option header */ 00981 return rpl_data_exthdr_provider_hbh_2(buf, instance, NULL, stage, result); 00982 } 00983 ext_size = 8 * (srh_info->hlen + 1); 00984 00985 switch (stage) { 00986 case IPV6_EXTHDR_SIZE: 00987 *result = ext_size; 00988 return buf; 00989 00990 case IPV6_EXTHDR_INSERT: { 00991 buf = buffer_headroom(buf, ext_size); 00992 if (!buf) { 00993 return NULL; 00994 } 00995 uint8_t *ext = buffer_data_reserve_header(buf, ext_size); 00996 rpl_data_sr_write_header(srh_info, ext, buf->options .type ); 00997 buf->route->ip_dest = rpl_data_sr_next_hop(); 00998 buf->options .type = IPV6_NH_ROUTING; 00999 // don't forget to set the "RPL option present" marker 01000 buf->options .ip_extflags |= IPEXT_SRH_RPL; 01001 *result = 0; 01002 return buf; 01003 } 01004 01005 case IPV6_EXTHDR_MODIFY: 01006 if (buf->options .ip_extflags & IPEXT_SRH_RPL) { 01007 *result = 0; 01008 return buf; 01009 } 01010 if (final_rpl_dest != buf->dst_sa .address ) { 01011 memcpy(buf->dst_sa .address , final_rpl_dest, 16); 01012 } 01013 *result = IPV6_EXTHDR_MODIFY_TUNNEL; 01014 buf->src_sa .addr_type = ADDR_NONE ; // force auto-selection 01015 return buf; 01016 01017 default: 01018 return buffer_free(buf); 01019 } 01020 } 01021 #endif // HAVE_RPL_ROOT 01022 01023 buffer_t *rpl_data_process_routing_header(buffer_t *buf, protocol_interface_info_entry_t *cur, uint8_t *ptr, uint16_t *hdrlen_out, bool *forward_out) 01024 { 01025 /* Handling procedures based on RFC 6554 4.2 */ 01026 01027 /* Do not process RPL source routing headers unless they arrive on a RPL interface */ 01028 if (!cur->rpl_domain) { 01029 tr_warn("SRH RX non-RPL if"); 01030 drop: 01031 protocol_stats_update(STATS_IP_RX_DROP, 1); 01032 return buffer_free(buf); 01033 } 01034 01035 buf->options .ip_extflags |= IPEXT_SRH_RPL; 01036 01037 uint16_t hlen = (ptr[1] + 1) * 8; 01038 uint8_t segs_left = ptr[3]; 01039 01040 if (segs_left == 0) { 01041 *hdrlen_out = hlen; 01042 return buf; 01043 } 01044 uint8_t cmprI = ptr[4] >> 4; 01045 uint8_t cmprE = ptr[4] & 0xF; 01046 uint8_t pad = ptr[5] >> 4; 01047 01048 01049 /* Should really be more rigorous here */ 01050 uint_fast16_t n_addrs = ((hlen - 8 - pad - (16 - cmprE)) / (16 - cmprI)) + 1; 01051 if (segs_left > n_addrs) { 01052 return icmpv6_error(buf, cur, ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM, ICMPV6_CODE_PARAM_PRB_HDR_ERR, (ptr + 3) - buffer_data_pointer(buf)); 01053 } 01054 01055 uint8_t *ip_dst = buffer_data_pointer(buf) + 24; 01056 01057 /* Decrement segments left */ 01058 segs_left = --ptr[3]; 01059 01060 if (addr_is_ipv6_multicast(buf->dst_sa .address )) { 01061 goto drop; 01062 } 01063 01064 /* Next address index (starting at 1, as per RFC 6554) */ 01065 uint_fast16_t next_addr_i = n_addrs - segs_left; 01066 01067 /* Locate next address: aptr -> Address[i] */ 01068 uint8_t *aptr = ptr + 8 + (next_addr_i - 1) * (16 - cmprI); 01069 uint8_t cmpr = next_addr_i == n_addrs ? cmprE : cmprI; 01070 uint8_t asize = 16 - cmpr; 01071 01072 /* Next address can only be multicast if compression is 0, otherwise 01073 * it inherits an already-checked non-0xFF start byte from IP destination 01074 */ 01075 if (cmpr == 0 && addr_is_ipv6_multicast(aptr)) { 01076 goto drop; 01077 } 01078 01079 /* Look for a loop. Scanning all addresses 1..n would be a pain, given 01080 * the possibility of weird cmprI/E combinations. But there's logically 01081 * no need to look at _previous_ addresses. And we know the packet was 01082 * addressed to us to reach us - Address[i-1] must have been ours. So 01083 * to ensure we do the test in RFC 6554: 01084 * 01085 * if 2 or more entries in Address[1..n] are assigned to 01086 * local interface and are separated by at least one 01087 * address not assigned to local interface 01088 * 01089 * We just check Address[i..n], knowing that i-1 was ours. It's impossible 01090 * for anything older than i-1 to be ours, as we would have failed the 01091 * check previously... 01092 * 01093 * There need to be at least 2 more addresses (i and i+1) for this check 01094 * to be useful. 01095 */ 01096 if (next_addr_i + 1 <= n_addrs) { 01097 uint8_t addr[16]; 01098 /* We know Address[i-1] was ours */ 01099 bool prev_was_local = true; 01100 /* Initialise pointers to examine Address[i] */ 01101 memcpy(addr, ip_dst, 16); 01102 const uint8_t *a = aptr; 01103 /* Then scan remaining */ 01104 for (uint_fast16_t i = next_addr_i; i <= n_addrs; i++) { 01105 uint8_t cpr = i == n_addrs ? cmprE : cmprI; 01106 uint8_t asz = 16 - cpr; 01107 memcpy(addr + cpr, a, asz); 01108 a += asz; 01109 bool local_addr = addr_interface_address_compare(cur, addr) == 0; 01110 if (local_addr) { 01111 if (!prev_was_local) { 01112 protocol_stats_update(STATS_RPL_ROUTELOOP, 1); 01113 tr_warn("SRH loop"); 01114 return icmpv6_error(buf, cur, ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM, ICMPV6_CODE_PARAM_PRB_HDR_ERR, aptr - buffer_data_pointer(buf)); 01115 } 01116 } 01117 prev_was_local = local_addr; 01118 } 01119 } 01120 01121 /* Swap the destination and Address[i] */ 01122 memswap(ip_dst + cmpr, aptr, asize); 01123 01124 /* And update the metadata */ 01125 memcpy(buf->dst_sa .address , ip_dst, 16); 01126 01127 /* Need to fake up routing here. Basically, for the common (shared-prefix) 01128 * case, we have to assume that the destination is on-link, on an assumed 01129 * interface. We have no direct record of the people trying to use us 01130 * as DAO parents, so we will get totally unknown addresses in SRHs. 01131 * We add routing info to the buffer to say that the IP destination is 01132 * the next hop, and the source being "SRH" is the IP layer's cue to send 01133 * "Error in Source Routing Header" codes instead of "Address Unreachable". 01134 */ 01135 buffer_free_route(buf); 01136 01137 /* THINK: May want to check to see if the address is already known to be on-link on 01138 * an interface. Won't be the usual 6LoWPAN case though. 01139 */ 01140 01141 /* Policy gets to decide whether we will take this - it can do neighbour state checks */ 01142 protocol_interface_info_entry_t *next_if = 01143 protocol_stack_interface_info_get_by_id( 01144 rpl_policy_srh_next_hop_interface(cur->rpl_domain, cur->id, 01145 buf->dst_sa .address )); 01146 if (!next_if) { 01147 goto error; 01148 } 01149 01150 buffer_routing_info_t *route = ipv6_buffer_route_to(buf, buf->dst_sa .address , next_if); 01151 if (!route) { 01152 /* Shouldn't happen here? Out of memory case? */ 01153 goto error; 01154 } 01155 route->route_info.source = ROUTE_RPL_SRH; 01156 01157 *forward_out = true; 01158 return buf; 01159 01160 error: 01161 return icmpv6_error(buf, cur, ICMPV6_TYPE_ERROR_DESTINATION_UNREACH, ICMPV6_CODE_DST_UNREACH_SRC_RTE_HDR_ERR, 0); 01162 } 01163 01164 bool rpl_data_get_srh_last_address(const uint8_t *rh, uint8_t *addr_out) 01165 { 01166 uint_fast8_t len = rh[1]; 01167 uint_fast8_t segs_left = rh[3]; 01168 uint_fast8_t cmpr_i = rh[4] >> 4; 01169 uint_fast8_t cmpr_e = rh[4] & 0xF; 01170 uint_fast8_t pad = rh[5] >> 4; 01171 01172 const uint8_t *last_addr_ptr = rh + 8 + (len * 8) - pad - (16 - cmpr_e); 01173 01174 if (segs_left == 0) { 01175 return true; 01176 } 01177 01178 if (segs_left > 1) { 01179 /* Get last "I" destination in */ 01180 memcpy(addr_out + cmpr_i, last_addr_ptr - (16 - cmpr_i), 16 - cmpr_i); 01181 } 01182 01183 /* Then modify "E" destination */ 01184 memcpy(addr_out + cmpr_e, last_addr_ptr, 16 - cmpr_e); 01185 return true; 01186 } 01187 01188 /* Set up handlers for general RPL nodes (hop-by-hop headers, DIO routes) */ 01189 void rpl_data_init(void) 01190 { 01191 ipv6_route_table_set_predicate_fn(ROUTE_RPL_INSTANCE, rpl_data_route_predicate_instance_default); 01192 ipv6_set_exthdr_provider(ROUTE_RPL_INSTANCE, rpl_data_exthdr_provider_hbh); 01193 ipv6_set_exthdr_provider(ROUTE_RPL_DIO, rpl_data_exthdr_provider_hbh); 01194 ipv6_set_exthdr_provider(ROUTE_RPL_ROOT, rpl_data_exthdr_provider_hbh); 01195 ipv6_set_exthdr_provider(ROUTE_RPL_FWD_ERROR, rpl_data_exthdr_provider_fwd_error_hbh); 01196 } 01197 01198 #ifdef HAVE_RPL_ROOT 01199 /* Set up handlers for DODAG root (creation of source routing headers) */ 01200 void rpl_data_init_root(void) 01201 { 01202 ipv6_set_exthdr_provider(ROUTE_RPL_DAO_SR, rpl_data_exthdr_provider_srh); 01203 ipv6_route_table_set_next_hop_fn(ROUTE_RPL_DAO_SR, rpl_data_route_next_hop); 01204 } 01205 #endif 01206 01207 #endif /* HAVE_RPL */
Generated on Fri Jul 22 2022 04:53:59 by
 1.7.2
 1.7.2 
    