EL4121 Embedded System / mbed-os

Dependents:   cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers rpl_data.c Source File

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