Nicolas Borla / Mbed OS BBR_1Ebene
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ipv6_resolution.c Source File

ipv6_resolution.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 #include "nsconfig.h"
00019 #include "string.h"
00020 #include "ns_types.h"
00021 #include "ns_list.h"
00022 #include "ns_trace.h"
00023 #include "nsdynmemLIB.h"
00024 #include "Core/include/socket.h"
00025 #include "NWK_INTERFACE/Include/protocol.h"
00026 #include "Common_Protocols/ipv6.h"
00027 #include "Common_Protocols/icmpv6.h"
00028 #include "Common_Protocols/icmpv6_prefix.h"
00029 #include "Common_Protocols/icmpv6_radv.h"
00030 #include "Common_Protocols/udp.h"
00031 #include "6LoWPAN/ND/nd_router_object.h" // for gp_address_ functions - better place?
00032 #include "ipv6_stack/ipv6_routing_table.h"
00033 #include "ipv6_stack/protocol_ipv6.h"
00034 #include "Common_Protocols/ipv6_resolution.h"
00035 #include "Common_Protocols/tcp.h"
00036 #include "Service_Libs/whiteboard/whiteboard.h"
00037 #include "Service_Libs/etx/etx.h"
00038 #include "platform/arm_hal_interrupt.h"
00039 #include "common_functions.h"
00040 
00041 #define TRACE_GROUP "ip6r"
00042 
00043 #ifndef RESOLUTION_QUEUE_LIMIT
00044 #define RESOLUTION_QUEUE_LIMIT 2
00045 #endif
00046 
00047 void ipv6_interface_resolve_send_ns(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry, bool unicast, uint_fast8_t seq)
00048 {
00049     protocol_interface_info_entry_t *cur_interface = NS_CONTAINER_OF(cache, protocol_interface_info_entry_t, ipv6_neighbour_cache);
00050 
00051     if (cur_interface->if_ns_transmit) {
00052         /* Thread uses DHCP Leasequery (!) instead of NS for address resolution */
00053         /* We still allow unicast NUD probes using NS, although I expect them to be disabled */
00054         if (cur_interface->if_ns_transmit(cur_interface, entry, unicast, seq)) {
00055             return;
00056         }
00057     }
00058 
00059 #ifdef HAVE_IPV6_ND
00060     tr_debug("Sending %s NS for: %s",
00061            (unicast ? "unicast" : "multicast"), trace_ipv6(entry->ip_address));
00062 
00063     buffer_t *prompting_packet = ns_list_get_first(&entry->queue);
00064     buffer_t *buf = icmpv6_build_ns(cur_interface, entry->ip_address,
00065                                     prompting_packet ? prompting_packet->src_sa .address  : NULL,
00066                                     unicast, false, NULL);
00067     protocol_push(buf);
00068 #else
00069     tr_error("No NS handler for interface %d", cur_interface->id);
00070 #endif
00071 }
00072 
00073 /* Entry has already been removed from cache, and is about to be freed. Hence entry->queue can't change while we process it */
00074 void ipv6_interface_resolution_failed(ipv6_neighbour_cache_t *cache, ipv6_neighbour_t *entry)
00075 {
00076     protocol_interface_info_entry_t *cur_interface = NS_CONTAINER_OF(cache, protocol_interface_info_entry_t, ipv6_neighbour_cache);
00077 
00078     tr_warn("LL addr of %s not found", trace_ipv6(entry->ip_address));
00079     ns_list_foreach_safe(buffer_t, buf, &entry->queue) {
00080         ns_list_remove(&entry->queue, buf);
00081         uint8_t code;
00082         if (buf->options.ip_extflags & IPEXT_SRH_RPL) {
00083             /* Note that as the border router we loopback SRH errors to ourselves if the first hop doesn't resolve */
00084             code = ICMPV6_CODE_DST_UNREACH_SRC_RTE_HDR_ERR;
00085         } else {
00086             code = ICMPV6_CODE_DST_UNREACH_ADDR_UNREACH;
00087         }
00088         /* XXX note no special handling for our own socket transmissions,
00089          * unlike original case. If we want this, we should do it in ICMP
00090          * RX handling, so we get events for external errors.
00091          */
00092         buf = socket_tx_buffer_event(buf, SOCKET_NO_ROUTE);
00093         if (buf) {
00094             buf = icmpv6_error(buf, cur_interface, ICMPV6_TYPE_ERROR_DESTINATION_UNREACH, code, 0);
00095             protocol_push(buf);
00096         }
00097     }
00098 }
00099 
00100 /* Silly bit of interface glue - ipv6_routing_table.c doesn't know about interface structures,
00101  * but it needs to be able to get from the interface id in the Routing Table and/or
00102  * Destination Cache to the relevant Neighbour Cache
00103  */
00104 ipv6_neighbour_cache_t *ipv6_neighbour_cache_by_interface_id(int8_t interface_id)
00105 {
00106     protocol_interface_info_entry_t *interface = protocol_stack_interface_info_get_by_id(interface_id);
00107 
00108     return interface ? &interface->ipv6_neighbour_cache : NULL;
00109 }
00110 
00111 void ipv6_send_queued(ipv6_neighbour_t *entry)
00112 {
00113     ns_list_foreach_safe(buffer_t, buf, &entry->queue) {
00114         ns_list_remove(&entry->queue, buf);
00115         tr_debug("Destination solved");
00116         protocol_push(buf);
00117     }
00118 }
00119 
00120 static void ipv6_trigger_resolve_query(protocol_interface_info_entry_t *cur_interface, buffer_t *buf, ipv6_neighbour_t *n)
00121 {
00122     if (n->state != IP_NEIGHBOUR_NEW && n->state != IP_NEIGHBOUR_INCOMPLETE) {
00123         tr_debug("ipv6_resolve_query");
00124         buffer_free(buf);
00125         return;
00126     }
00127 
00128     uint_fast16_t count = ns_list_count(&n->queue);
00129     while (count >= RESOLUTION_QUEUE_LIMIT) {
00130         buffer_t *b = ns_list_get_first(&n->queue);
00131         ns_list_remove(&n->queue, b);
00132         socket_tx_buffer_event_and_free(b, SOCKET_NO_ROUTE);
00133         count--;
00134     }
00135     tr_debug("Queueing for: %s", trace_ipv6(n->ip_address));
00136     ns_list_add_to_end(&n->queue, buf);
00137 
00138     if (n->state == IP_NEIGHBOUR_NEW) {
00139         /* Start NS timers, send first NS */
00140         ipv6_neighbour_set_state(&cur_interface->ipv6_neighbour_cache, n, IP_NEIGHBOUR_INCOMPLETE);
00141         ipv6_interface_resolve_send_ns(&cur_interface->ipv6_neighbour_cache, n, false, 0);
00142     }
00143 
00144 }
00145 
00146 
00147 /* Given a buffer with IP next-hop address and outgoing interface, find the
00148  * neighbour entry, and if complete, write the link-layer address into the buffer
00149  * destination, and return the Neighbour Cache entry.
00150  * If we have an incomplete Neighbour Cache entry, start address resolution
00151  * and queue the buffer, returning NULL.
00152  */
00153 ipv6_neighbour_t *ipv6_interface_resolve_new(protocol_interface_info_entry_t *cur, buffer_t *buf)
00154 {
00155     buffer_routing_info_t *route = ipv6_buffer_route(buf);
00156     if (!route) {
00157         tr_warn("XXX ipv6_interface_resolve no route!");
00158         // Can this happen? How did it get to this interface in the first place?
00159         // If it can happen, send ICMP Destination Unreachable
00160         buffer_free(buf);
00161         return NULL;
00162     }
00163     ipv6_neighbour_t *n = ipv6_neighbour_lookup_or_create(&cur->ipv6_neighbour_cache, route->route_info.next_hop_addr);
00164     if (!n) {
00165         // If it can happen, send ICMP Destination Unreachable
00166         tr_warn("No heap for address resolve");
00167         buffer_free(buf);
00168         return NULL;
00169     }
00170 
00171     if (n->state == IP_NEIGHBOUR_NEW || n->state == IP_NEIGHBOUR_INCOMPLETE) {
00172         addrtype_t ll_type;
00173         const uint8_t *ll_addr;
00174 
00175         if (cur->if_map_ip_to_link_addr &&
00176                 cur->if_map_ip_to_link_addr(cur, route->route_info.next_hop_addr, &ll_type, &ll_addr)) {
00177             ipv6_neighbour_update_from_na(&cur->ipv6_neighbour_cache, n, NA_O, ll_type, ll_addr);
00178         }
00179     }
00180 
00181     if (n->state == IP_NEIGHBOUR_NEW || n->state == IP_NEIGHBOUR_INCOMPLETE) {
00182         ipv6_trigger_resolve_query(cur, buf, n);
00183         return NULL;
00184     }
00185 
00186     buf->dst_sa .addr_type  = n->ll_type;
00187     memcpy(buf->dst_sa .address , n->ll_address, addr_len_from_type(n->ll_type));
00188 
00189     /* Optimisation trick - if security bypass is set, this is presumably some
00190      * sort of MLE-type link management packet. Not worth sending extra NS/NA
00191      * noise for these.
00192      */
00193     if (!(buf->options .ll_security_bypass_tx  && addr_is_ipv6_link_local(route->route_info.next_hop_addr))) {
00194         n = ipv6_neighbour_used(&cur->ipv6_neighbour_cache, n);
00195     }
00196     return n;
00197 }
00198 
00199 /* Attempt a mapping from current information (neighbour cache, hard mappings) */
00200 bool ipv6_map_ip_to_ll(protocol_interface_info_entry_t *cur, ipv6_neighbour_t *n, const uint8_t ip_addr[16], addrtype_t *ll_type, const uint8_t **ll_addr_out)
00201 {
00202     if (!n) {
00203         n = ipv6_neighbour_lookup(&cur->ipv6_neighbour_cache, ip_addr);
00204     }
00205     if (n && !(n->state == IP_NEIGHBOUR_NEW || n->state == IP_NEIGHBOUR_INCOMPLETE)) {
00206         *ll_type = n->ll_type;
00207         *ll_addr_out = n->ll_address;
00208         return true;
00209     }
00210 
00211     if (cur->if_map_ip_to_link_addr &&
00212             cur->if_map_ip_to_link_addr(cur, ip_addr, ll_type, ll_addr_out)) {
00213         return true;
00214     }
00215 
00216     return false;
00217 }
00218 
00219 /* Attempt a mapping from current information (neighbour cache, hard mappings) */
00220 bool ipv6_map_ll_to_ip_link_local(protocol_interface_info_entry_t *cur, addrtype_t ll_type, const uint8_t *ll_addr, uint8_t ip_addr_out[16])
00221 {
00222     if (cur->if_map_link_addr_to_ip &&
00223             cur->if_map_link_addr_to_ip(cur, ll_type, ll_addr, ip_addr_out)) {
00224         return true;
00225     }
00226 
00227     ns_list_foreach(ipv6_neighbour_t, n, &cur->ipv6_neighbour_cache.list) {
00228         if (ipv6_neighbour_ll_addr_match(n, ll_type, ll_addr) && addr_is_ipv6_link_local(n->ip_address)) {
00229             memcpy(ip_addr_out, n->ip_address, 16);
00230             return true;
00231         }
00232     }
00233 
00234     return false;
00235 }
00236 
00237 /* To comply with ETX returns 0xFFFF when neighbor doesn't exist and 0 when neighbor is currently unknown. */
00238 uint16_t ipv6_map_ip_to_ll_and_call_ll_addr_handler(protocol_interface_info_entry_t *cur, int8_t interface_id, ipv6_neighbour_t *n, const uint8_t ipaddr[16], ll_addr_handler_t *ll_addr_handler_ptr)
00239 {
00240     addrtype_t ll_type;
00241     const uint8_t *ll_addr;
00242 
00243     if (!cur) {
00244         cur = protocol_stack_interface_info_get_by_id(interface_id);
00245         if (!cur) {
00246             return 0xFFFF;
00247         }
00248     }
00249 
00250     if (!ipv6_map_ip_to_ll(cur, n, ipaddr, &ll_type, &ll_addr)) {
00251         return 0;
00252     }
00253 
00254     return ll_addr_handler_ptr(cur->id, ll_type, ll_addr);
00255 }