Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers icmpv6_radv.c Source File

icmpv6_radv.c

00001 /*
00002  * Copyright (c) 2014-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  * icmpv6_radv.c
00019  *
00020  * ICMPv6 Router Advertisement transmission
00021  *
00022  * This file handles all RS reception, and deals with the timing of all RAs.
00023  * The actual contents of the RAs are still handled separately by
00024  * nd_router_object.c for 6LoWPAN and protocol_ipv6.c for Ethernet.
00025  *
00026  * We handle multicast transmission - once initially scheduled, these will
00027  * automatically be repeated, and will be accelerated by RS reception if
00028  * rtr_adv_unicast_to_rs is false.
00029  *
00030  * If rtr_adv_unicast_to_rs is true, then each RS gets a unicast RA response,
00031  * without affecting any scheduled multicast transmissions.
00032  *
00033  * For 6LR operation, with RFC 6775 multihop distribution, we allow the system
00034  * to queue multiple transmissions to each destination - 1 per border router
00035  * (ABRO 6LBR). These are independently scheduled and rate limited; 1 RS
00036  * will lead to multiple independently randomised responses, 1 per ABRO, and
00037  * per-ABRO multicast transmissions will be independently randomly scheduled.
00038  */
00039 
00040 #include "nsconfig.h"
00041 #include "ns_types.h"
00042 #include "ns_list.h"
00043 #include "randLIB.h"
00044 #include <string.h>
00045 #include "nsdynmemLIB.h"
00046 #include "ns_trace.h"
00047 #include "NWK_INTERFACE/Include/protocol.h"
00048 #include "ipv6_stack/protocol_ipv6.h"
00049 #include "6LoWPAN/ND/nd_router_object.h" // for nd_ra_timing()
00050 #include "Common_Protocols/icmpv6.h"
00051 #include "Common_Protocols/icmpv6_radv.h"
00052 
00053 #ifdef RADV_TX
00054 
00055 #define TRACE_GROUP "RAdv"
00056 
00057 typedef struct icmp_queued_ra {
00058     uint8_t         addr[16];           /* destination address */
00059     uint8_t         abro[16];           /* RFC 6775 ABRO 6LBR address (or ADDR_UNSPECIFIED if no ABRO) */
00060     bool            rs_triggered;       /* was queued by an RS */
00061     uint16_t        ticks;              /* ticks until transmission (relative to last list entry) */
00062     protocol_interface_info_entry_t *interface;
00063     ns_list_link_t  link;
00064 } icmp_queued_ra_t;
00065 
00066 static NS_LIST_DEFINE(icmp_ra_queue, icmp_queued_ra_t, link);
00067 
00068 void icmpv6_radv_init(protocol_interface_info_entry_t *cur)
00069 {
00070     /* Initialise basic static config */
00071     cur->adv_send_advertisements = false;
00072     cur->max_ra_delay_time = 5;
00073     cur->min_delay_between_ras = 30;
00074     cur->min_rtr_adv_interval = 2000; // 3 minutes 20 seconds (max/3)
00075     cur->max_rtr_adv_interval = 6000; // 10 minutes
00076     cur->max_initial_rtr_adv_interval =  160; // 16 seconds
00077     cur->max_initial_rtr_advertisements = 3;
00078     cur->adv_link_mtu = 0;
00079     cur->adv_cur_hop_limit = 0;
00080     cur->adv_reachable_time = 0;
00081     cur->adv_retrans_timer = 0;
00082     cur->rtr_adv_unicast_to_rs = false;
00083     cur->rtr_adv_flags = 0;
00084     cur->adv_copy_heard_flags = false;
00085 
00086     /* Initialise timing info for local RAs */
00087     cur->ra_timing.rtr_adv_last_send_time = protocol_core_monotonic_time - 0x10000;
00088     cur->ra_timing.initial_rtr_adv_count = 0;
00089 }
00090 
00091 /* Locate the timing info for a given source - we currently have a split in that
00092  * specified ABROs get their timing info stored over in the associated nd_router_t,
00093  * and if there's no ABRO, it's in the interface structure.
00094  *
00095  * When/if the "advert contents" gets centralised, this info can be stored there
00096  * too.
00097  */
00098 static ipv6_ra_timing_t *icmpv6_ra_timing_lookup(protocol_interface_info_entry_t *cur, const uint8_t abro[16])
00099 {
00100     if (addr_is_ipv6_unspecified(abro)) {
00101         return &cur->ra_timing;
00102     }
00103 
00104     return nd_ra_timing(abro);
00105 }
00106 
00107 /* Queue an RA - on entry new_ra->ticks is ticks from now; we need to
00108  * insert it into the list which has relative ticks, so it gets decremented
00109  * by the ticks of all preceding queue members. */
00110 void icmpv6_queue_ra(icmp_queued_ra_t *new_ra)
00111 {
00112     ns_list_foreach(icmp_queued_ra_t, ra, &icmp_ra_queue) {
00113         if (ra->ticks > new_ra->ticks) {
00114             ns_list_add_before(&icmp_ra_queue, ra, new_ra);
00115             ra->ticks -= new_ra->ticks;
00116             return;
00117         }
00118         new_ra->ticks -= ra->ticks;
00119     }
00120 
00121     ns_list_add_to_end(&icmp_ra_queue, new_ra);
00122 }
00123 
00124 void icmpv6_unqueue_ra(icmp_queued_ra_t *ra)
00125 {
00126     icmp_queued_ra_t *before = ns_list_get_next(&icmp_ra_queue, ra);
00127 
00128     if (before) {
00129         before->ticks += ra->ticks;
00130     }
00131 
00132     ns_list_remove(&icmp_ra_queue, ra);
00133 }
00134 
00135 /* If there's an advert already queued to the specified destination for the specified ABRO, return it, and its scheduled time */
00136 static icmp_queued_ra_t *icmpv6_queued_ra_lookup(const uint8_t *dest_addr, const uint8_t *abro, int8_t interface_id, uint16_t *abstime_out)
00137 {
00138     uint16_t abstime = 0;
00139     ns_list_foreach(icmp_queued_ra_t, ra, &icmp_ra_queue) {
00140         abstime += ra->ticks;
00141         if (interface_id == ra->interface->id && addr_ipv6_equal(dest_addr, ra->addr) && addr_ipv6_equal(abro, ra->abro)) {
00142             if (abstime_out) {
00143                 *abstime_out = abstime;
00144             }
00145             return ra;
00146         }
00147     }
00148 
00149     return NULL;
00150 }
00151 
00152 /* Trigger a single RA from an RS - must be called multiple times if we have multiple ABROs */
00153 void icmpv6_trigger_ra_from_rs(protocol_interface_info_entry_t *cur, const uint8_t dest[16], const uint8_t abro[16])
00154 {
00155     uint16_t scheduled;
00156 
00157     /* Check if we've already scheduled an RA to this destination for this ABRO */
00158     icmp_queued_ra_t *ra = icmpv6_queued_ra_lookup(dest, abro, cur->id, &scheduled);
00159 
00160     /* Delay "0" means next tick, ie somewhere between 0 and 100ms, so "(0, 4)"
00161      * gives us [0ms..500ms).
00162      */
00163     uint16_t delay = randLIB_get_random_in_range(0, cur->max_ra_delay_time - 1);
00164     if (ra) {
00165         /* If we've already handled an RS for this destination, or we'd be
00166          * delaying an already-scheduled RA, ignore this RS.
00167          */
00168         if (ra->rs_triggered || delay >= scheduled) {
00169             return;
00170         }
00171         /* Unqueue, and we'll requeue for an earlier time */
00172         icmpv6_unqueue_ra(ra);
00173     } else {
00174         ra = ns_dyn_mem_alloc(sizeof(icmp_queued_ra_t));
00175         if (!ra) {
00176             return;
00177         }
00178         memcpy(ra->addr, dest, 16);
00179         memcpy(ra->abro, abro, 16);
00180         ra->interface = cur;
00181     }
00182 
00183     ra->rs_triggered = true;
00184 
00185     /* Rate-limit multicasts - independently for each ABRO */
00186     if (dest == ADDR_LINK_LOCAL_ALL_NODES) {
00187         ipv6_ra_timing_t *t = icmpv6_ra_timing_lookup(cur, abro);
00188         uint32_t time_since_last_ra;
00189         time_since_last_ra = protocol_core_monotonic_time - t->rtr_adv_last_send_time;
00190 
00191         if (time_since_last_ra < cur->min_delay_between_ras) {
00192             delay += cur->min_delay_between_ras - time_since_last_ra;
00193         }
00194     } else {
00195         delay = 1;
00196     }
00197     ra->ticks = delay;
00198 
00199     icmpv6_queue_ra(ra);
00200 }
00201 
00202 buffer_t *icmpv6_rs_handler(buffer_t *buf, protocol_interface_info_entry_t *cur)
00203 {
00204     const uint8_t *sllao;
00205 
00206     if (buf->options .hop_limit  != 255 || buf->options .code  != 0) {
00207         return buffer_free(buf);
00208     }
00209 
00210     if (!icmpv6_options_well_formed_in_buffer(buf, 4)) {
00211         tr_debug("Malformed RS");
00212         return buffer_free(buf);
00213     }
00214 
00215     sllao = icmpv6_find_option_in_buffer(buf, 4, ICMPV6_OPT_SRC_LL_ADDR, 0);
00216 
00217     if (addr_is_ipv6_unspecified(buf->src_sa .address ) && sllao) {
00218         return buffer_free(buf);
00219     }
00220 
00221     if (!cur->adv_send_advertisements) {
00222         return buffer_free(buf);
00223     }
00224 
00225     ipv6_neighbour_t *neighbour;
00226 
00227     if (sllao && cur->if_llao_parse(cur, sllao, &buf->dst_sa )) {
00228         neighbour = ipv6_neighbour_update_unsolicited(&cur->ipv6_neighbour_cache, buf->src_sa .address , buf->dst_sa .addr_type , buf->dst_sa .address );
00229     } else {
00230         neighbour = ipv6_neighbour_lookup(&cur->ipv6_neighbour_cache, buf->src_sa .address );
00231     }
00232 
00233     if (neighbour && neighbour->is_router) {
00234         ipv6_router_gone(&cur->ipv6_neighbour_cache, neighbour);
00235     }
00236 
00237     const uint8_t *dest;
00238     dest = cur->rtr_adv_unicast_to_rs ? buf->src_sa .address  : ADDR_LINK_LOCAL_ALL_NODES;
00239 
00240     /* Yuck - unify later */
00241     if (cur->nwk_id == IF_6LoWPAN) {
00242         /* This triggers 1 RA per ABRO (nd_router_t) */
00243         nd_trigger_ras_from_rs(dest, cur);
00244     } else {
00245         /* Just trigger 1 RA without ABRO */
00246         icmpv6_trigger_ra_from_rs(cur, dest, ADDR_UNSPECIFIED);
00247     }
00248 
00249     return buffer_free(buf);
00250 }
00251 
00252 /* (Re)start multicast router advertisements for a given ABRO, or unspecified. adv_send_advertisements must be set */
00253 void icmpv6_restart_router_advertisements(protocol_interface_info_entry_t *cur, const uint8_t abro[16])
00254 {
00255     icmp_queued_ra_t *ra;
00256 
00257     if (!cur->adv_send_advertisements) {
00258         return;
00259     }
00260 
00261     ipv6_ra_timing_t *t = icmpv6_ra_timing_lookup(cur, abro);
00262     if (!t) {
00263         return;
00264     }
00265 
00266     ra = icmpv6_queued_ra_lookup(ADDR_LINK_LOCAL_ALL_NODES, abro, cur->id, NULL);
00267     if (ra) {
00268         icmpv6_unqueue_ra(ra);
00269     } else {
00270         ra = ns_dyn_mem_alloc(sizeof(icmp_queued_ra_t));
00271         if (!ra) {
00272             return;
00273         }
00274 
00275         memcpy(ra->addr, ADDR_LINK_LOCAL_ALL_NODES, 16);
00276         memcpy(ra->abro, abro, 16);
00277         ra->rs_triggered = false;
00278         ra->interface = cur;
00279         /* For a new transmission, if this is 0, we don't send anything initially,
00280          * but we still want randomness; on the other hand there's no point
00281          * having a minimum delay. So let's do it like this - equal random range,
00282          * but starting immediately.
00283          */
00284         if (cur->max_initial_rtr_advertisements == 0) {
00285             ra->ticks = randLIB_get_random_in_range(0, cur->max_rtr_adv_interval - cur->min_rtr_adv_interval);
00286         }
00287     }
00288 
00289     /* If we are retriggering "initial" adverts, should allow some jitter in case
00290      * we're doing it in response to a multicast update.
00291      */
00292     if (cur->max_initial_rtr_advertisements != 0) {
00293         ra->ticks = randLIB_get_random_in_range(0, cur->max_initial_rtr_adv_interval);
00294     }
00295 
00296     t->initial_rtr_adv_count = cur->max_initial_rtr_advertisements;
00297 
00298     /* And enforce the rate limiting, if somehow we cancelled and restarted this TX */
00299     uint16_t time_since_last = protocol_core_monotonic_time - t->rtr_adv_last_send_time;
00300     if (time_since_last < cur->min_delay_between_ras) {
00301         ra->ticks += cur->min_delay_between_ras - time_since_last;
00302     }
00303 
00304     icmpv6_queue_ra(ra);
00305 }
00306 
00307 /* Cancel scheduled router advertisements, either for a given ABRO (real or ADDR_UNSPECIFIED), or any ABRO (NULL) */
00308 void icmpv6_stop_router_advertisements(protocol_interface_info_entry_t *cur, const uint8_t *abro)
00309 {
00310     ns_list_foreach_safe(icmp_queued_ra_t, ra, &icmp_ra_queue) {
00311         if (ra->interface == cur) {
00312             /* Match ABRO if specified */
00313             if (abro && !addr_ipv6_equal(abro, ra->abro)) {
00314                 continue;
00315             }
00316 
00317             icmpv6_unqueue_ra(ra);
00318             ns_dyn_mem_free(ra);
00319         }
00320     }
00321 }
00322 
00323 /* Actually send an RA - have to refer to protocol_ipv6.c and nd_router_object.c
00324  * at the moment, as the information is stored differently.
00325  */
00326 static void icmpv6_send_ra(protocol_interface_info_entry_t *cur, const uint8_t *dest, const uint8_t *abro)
00327 {
00328     if (cur->nwk_id == IF_6LoWPAN) {
00329         nd_ra_build_by_abro(abro, dest, cur);
00330     } else {
00331         ipv6_nd_ra_advert(cur, dest);
00332     }
00333 }
00334 
00335 void icmpv6_radv_timer(uint16_t ticks)
00336 {
00337     /* This initialises to empty (on every call) */
00338     NS_LIST_DEFINE(to_requeue, icmp_queued_ra_t, link);
00339 
00340     /* Ticks are relative in this queue - break once all ticks are consumed */
00341     ns_list_foreach_safe(icmp_queued_ra_t, ra, &icmp_ra_queue) {
00342         if (ra->ticks > ticks) {
00343             /* Next entry doesn't fire yet - just decrease its time and exit */
00344             ra->ticks -= ticks;
00345             break;
00346         }
00347 
00348         /* Have a firing entry - note that once ticks reaches 0, we can still
00349          * consume multiple simultaneous entries with ra->ticks == 0, so
00350          * we don't stop as soon as ticks hits 0. */
00351         ticks -= ra->ticks;
00352 
00353         /* Just remove, not "unqueue" here, as we're in the process of adjusting ticks */
00354         ns_list_remove(&icmp_ra_queue, ra);
00355 
00356         ipv6_ra_timing_t *t = icmpv6_ra_timing_lookup(ra->interface, ra->abro);
00357         if (!t) {
00358             ns_dyn_mem_free(ra);
00359         } else {
00360             /* Safety check - make sure we shut down okay if this gets flipped off */
00361             if (ra->interface->adv_send_advertisements) {
00362                 icmpv6_send_ra(ra->interface, ra->addr, ra->abro);
00363                 t->rtr_adv_last_send_time = protocol_core_monotonic_time;
00364             }
00365 
00366             /* Multicast adverts get automatically rescheduled */
00367             if (addr_is_ipv6_multicast(ra->addr) && ra->interface->adv_send_advertisements) {
00368                 /* reschedule - for safe list handling, stash and reinsert after the main loop */
00369                 ra->ticks = randLIB_get_random_in_range(ra->interface->min_rtr_adv_interval, ra->interface->max_rtr_adv_interval);
00370                 if (t->initial_rtr_adv_count && --t->initial_rtr_adv_count) {
00371                     uint16_t max = ra->interface->max_initial_rtr_adv_interval;
00372                     if (ra->ticks > max) {
00373                         ra->ticks = max;
00374                     }
00375                 }
00376                 ra->rs_triggered = false;
00377                 ns_list_add_to_end(&to_requeue, ra);
00378             } else {
00379                 ns_dyn_mem_free(ra);
00380             }
00381         }
00382     }
00383 
00384     ns_list_foreach_safe(icmp_queued_ra_t, ra, &to_requeue) {
00385         ns_list_remove(&to_requeue, ra);
00386         icmpv6_queue_ra(ra);
00387     }
00388 }
00389 
00390 #endif /* RADV_TX */