Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mld.c Source File

mld.c

00001 /*
00002  * Copyright (c) 2016-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  * MLD version 1 (RFC 2710) implementation
00019  *
00020  * RFC 6434 (IPv6 Node Requirements) says nodes MUST support MLDv1, and that
00021  * nodes SHOULD support MLDv2 (possibly lightweight).
00022  *
00023  * As we don't support Source-Specific Multicast, nothing is really gained
00024  * by supporting MLDv2. The statement in RFC 6434 that "the presence of a single
00025  * MLDv1 participant on a link requires that all other nodes on the link operate
00026  * in version 1 compatibility mode" seems to be misleading. Our presence doesn't
00027  * affect hosts, and it doesn't have much worse effect on the router or network
00028  * than the presence of an MLDv2 Any-Source-Multicast host for that group.
00029  */
00030 
00031 #include "nsconfig.h"
00032 #include "ns_types.h"
00033 #include "common_functions.h"
00034 #include "randLIB.h"
00035 #include <string.h>
00036 
00037 #include "NWK_INTERFACE/Include/protocol.h"
00038 #include "Common_Protocols/ip.h"
00039 #include "Common_Protocols/ipv6.h"
00040 #include "Common_Protocols/icmpv6.h"
00041 #include "Common_Protocols/mld.h"
00042 #include "6LoWPAN/Thread/thread_common.h"
00043 
00044 #define MLD_UNSOLICITED_REPORT_INTERVAL (10 * 10) /* 10 seconds */
00045 
00046 
00047 #if 0
00048 // MLDv2 fragments - not yet
00049 
00050 /* If Maximum Response Code >=32768, Maximum Response Code represents a
00051  * floating-point value as follows:
00052  *
00053  *     0 1 2 3 4 5 6 7 8 9 A B C D E F
00054  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00055  *    |1| exp |          mant         |
00056  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00057  *
00058  * Maximum Response Delay = (mant | 0x1000) << (exp+3)
00059  */
00060 
00061 /* Convert Maximum Response Code to Maximum Response Delay (milliseconds) */
00062 /* Maximum value is 0x7FFC00 (~2 hours) */
00063 static inline uint_least24_t mldv2_mrd_from_mrc(uint16_t mrc)
00064 {
00065     if (mrc < 0x8000) {
00066         return mrc;
00067     } else {
00068         uint_fast16_t exp = (mrc & 0x7000) >> 12;
00069         uint_fast16_t mant = mrc & 0x0FFF;
00070         return (UINT24_C(0x1000) | mant) << (exp + 3);
00071     }
00072 }
00073 
00074 /* Convert Maximum Response Delay (milliseconds) to Maximum Response Code */
00075 /* Maximum representable value is (0x1FFF << 10) = 0x7FFC00 (~2 hours) */
00076 static inline uint16_t mldv2_mrc_from_mrd(uint_least24_t mrd)
00077 {
00078     if (mrd < 0x8000) {
00079         return mrd;
00080     } else if (mrd >= 0x800000) {
00081         return 0xFFFF;
00082     } else {
00083         /* mrd is in range 0x8000 to 0x7FFFFF, so clz32 returns 9-16, so exp is 0-7 */
00084         /* Example: 0x123456 - clz = 11; exp = 5; mant = 0x234; result = 0x95 */
00085         uint16_t exp = 16 - common_count_leading_zeros_32(mrd);
00086         uint16_t mant = (mrd >> (exp + 3)) & 0x0FFF;
00087         return 0x8000 | (exp << 12) | mant;
00088     }
00089 }
00090 
00091 /*
00092  * If QQIC >= 128, QQIC represents a floating-point value as follows:
00093  *
00094  *     0 1 2 3 4 5 6 7
00095  *    +-+-+-+-+-+-+-+-+
00096  *    |1| exp | mant  |
00097  *    +-+-+-+-+-+-+-+-+
00098  *
00099  * QQI = (mant | 0x10) << (exp + 3)
00100  */
00101 
00102 /* Convert Querier's Query Interval Code to Querier's Query Interval (seconds) */
00103 /* Maximum value is 0x7C00 (~9 hours) */
00104 static inline uint16_t mldv2_qqi_from_qqic(uint8_t qqic)
00105 {
00106     if (qqic < 0x80) {
00107         return qqic;
00108     } else {
00109         uint_fast8_t exp = (qqic & 0x70) >> 4;
00110         uint_fast8_t mant = qqic & 0x0F;
00111         return (UINT16_C(0x10) | mant) << (exp + 3);
00112     }
00113 }
00114 
00115 /* Convert Querier's Query Interval (seconds) to Querier's Query Interval Code */
00116 /* Maximum representable value is (0x1f << 10) = 0x7C00 (~9 hours) */
00117 static inline uint8_t mldv2_qqic_from_qqi(uint16_t qqi)
00118 {
00119     if (qqi < 0x80) {
00120         return qqi;
00121     } else if (qqi >= 0x8000) {
00122         return 0xFF;
00123     } else {
00124         /* qqi is in range 0x80 to 0x7FFF, so clz16 returns 1-8, so exp is 0-7 */
00125         /* Example: 0x0151 - clz = 7; exp = 1; mant = 0x5; result = 0x95 */
00126         uint8_t exp = 8 - common_count_leading_zeros_16(qqi);
00127         uint8_t mant = (qqi >> (exp + 3)) & 0x0F;
00128         return 0x80 | (exp << 4) | mant;
00129     }
00130 }
00131 #endif
00132 
00133 /*
00134  * RFC 2710 Multicast Listener Discovery message
00135  *
00136  *  0                   1                   2                   3
00137  *  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
00138  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00139  * |     Type      |     Code      |          Checksum             |
00140  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00141  * |     Maximum Response Delay    |          Reserved             |
00142  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00143  * |                                                               |
00144  * +                                                               +
00145  * |                                                               |
00146  * +                       Multicast Address                       +
00147  * |                                                               |
00148  * +                                                               +
00149  * |                                                               |
00150  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00151  */
00152 buffer_t *mld_build(protocol_interface_info_entry_t *cur, uint8_t type, uint16_t max_response_delay, const uint8_t address[static 16])
00153 {
00154     uint8_t *ptr;
00155 
00156     /* Validity checks - must be multicast, scope >= link-local */
00157     if (!addr_is_ipv6_multicast(address) || addr_ipv6_multicast_scope(address) <= IPV6_SCOPE_INTERFACE_LOCAL) {
00158         /* Except we allow unspecified for general query */
00159         if (!(type == ICMPV6_TYPE_INFO_MCAST_LIST_QUERY && address == ADDR_UNSPECIFIED)) {
00160             return NULL;
00161         }
00162     }
00163 
00164     buffer_t *buf = buffer_get(20);
00165     if (!buf) {
00166         return NULL;
00167     }
00168 
00169     buf->options .type  = type;
00170     buf->options .code  = 0;
00171     buf->options .hop_limit  = 1;
00172     buf->options .ip_extflags  |= IPEXT_HBH_ROUTER_ALERT;
00173     buf->options .multicast_loop  = true;
00174     buf->options .mpl_permitted  = false;
00175     buf->options .traffic_class  = IP_DSCP_CS5 << IP_TCLASS_DSCP_SHIFT; /* RFC 4594 */
00176     buf->interface  = cur;
00177     buf->info  = (buffer_info_t)(B_DIR_DOWN | B_FROM_ICMP | B_TO_ICMP);
00178 
00179     buf->src_sa .addr_type  = ADDR_IPV6 ;
00180     if (addr_interface_get_ll_address(cur, buf->src_sa .address , 0) < 0) {
00181         memset(buf->src_sa .address , 0, 16);
00182     }
00183     buf->dst_sa .addr_type  = ADDR_IPV6 ;
00184     if (type == ICMPV6_TYPE_INFO_MCAST_LIST_DONE) {
00185         memcpy(buf->dst_sa .address , ADDR_LINK_LOCAL_ALL_ROUTERS, 16);
00186     } else if (type == ICMPV6_TYPE_INFO_MCAST_LIST_QUERY && address == ADDR_UNSPECIFIED) {
00187         memcpy(buf->dst_sa .address , ADDR_LINK_LOCAL_ALL_NODES, 16);
00188     } else {
00189         memcpy(buf->dst_sa .address , address, 16);
00190     }
00191 
00192     /* MLD packets are implicitly on-link - bypass routing */
00193     ipv6_buffer_route_to(buf, buf->dst_sa .address , cur);
00194     if (!buf->route) {
00195         goto invalid;
00196     }
00197 
00198     ptr = buffer_data_pointer(buf);
00199 
00200     ptr = common_write_16_bit(max_response_delay, ptr);
00201     ptr = common_write_16_bit(0, ptr);
00202     memcpy(ptr, address, 16);
00203     ptr += 16;
00204     buffer_data_end_set(buf, ptr);
00205 
00206     return buf;
00207 
00208 invalid:
00209     if (buf) {
00210         buffer_free(buf);
00211     }
00212 
00213     return NULL;
00214 }
00215 
00216 static void mld_query_entry(if_group_entry_t *entry, uint16_t mrd)
00217 {
00218     /* Don't do MLD for low-scope groups - ff02::1 already excluded by being in the implicit list */
00219     if (addr_ipv6_scope(entry->group, NULL) < IPV6_SCOPE_LINK_LOCAL) {
00220         return;
00221     }
00222 
00223     /* "Query received" event */
00224     /* Timer starts/resets if it's not running, or it's set to greater than MRD */
00225     if (entry->mld_timer == 0 || mrd < entry->mld_timer) {
00226         entry->mld_timer = randLIB_get_random_in_range(1, mrd);
00227     }
00228 }
00229 
00230 bool mld_querier = false;
00231 
00232 buffer_t *mld_query_handler(buffer_t *buf, protocol_interface_info_entry_t *cur)
00233 {
00234     /* Thread has its own mechanism (as ever), and borrows the mld_timer field,
00235      * so we can't do MLD.
00236      */
00237     if (thread_info(cur)) {
00238         goto invalid;
00239     }
00240 
00241     /* RFC 4541 (MLD Snooping Switches) says we should accept unspecified source */
00242     if (!(addr_is_ipv6_link_local(buf->src_sa .address ) || addr_is_ipv6_unspecified(buf->src_sa .address )) || buffer_data_length(buf) < 20) {
00243         goto invalid;
00244     }
00245 
00246     const uint8_t *ptr = buffer_data_pointer(buf);
00247     uint16_t mrd = common_read_16_bit(ptr);
00248     const uint8_t *group = ptr + 4;
00249     if (!addr_is_ipv6_multicast(group) && !addr_is_ipv6_unspecified(group)) {
00250         goto invalid;
00251     }
00252 
00253     if (mld_querier && !addr_is_ipv6_unspecified(buf->src_sa .address )) {
00254         uint8_t our_addr[16];
00255         if (addr_interface_get_ll_address(cur, our_addr, 0) >= 0) {
00256             if (memcmp(buf->src_sa .address , our_addr, 16) < 0) {
00257                 mld_querier = false;
00258             }
00259         }
00260     }
00261 
00262     /* Convert Maximum Response Delay from milliseconds to ticks. If they say "0" they get 1 tick */
00263     mrd /= 100;
00264     if (mrd == 0) {
00265         mrd = 1;
00266     }
00267 
00268     if (addr_is_ipv6_multicast(group)) {
00269         /* Address-Specific Query */
00270         if_group_entry_t *entry = addr_get_group_entry(cur, group);
00271         if (!entry) {
00272             goto invalid;
00273         }
00274         mld_query_entry(entry, mrd);
00275     } else {
00276         /* General Query */
00277         ns_list_foreach(if_group_entry_t, entry, &cur->ip_groups) {
00278             mld_query_entry(entry, mrd);
00279         }
00280     }
00281 
00282 invalid:
00283     if (buf) {
00284         buffer_free(buf);
00285     }
00286 
00287     return NULL;
00288 }
00289 
00290 buffer_t *mld_report_handler(buffer_t *buf, protocol_interface_info_entry_t *cur)
00291 {
00292     if (thread_info(cur)) {
00293         goto invalid;
00294     }
00295 
00296     if (!addr_is_ipv6_link_local(buf->src_sa .address ) || buffer_data_length(buf) < 20) {
00297         goto invalid;
00298     }
00299 
00300     const uint8_t *ptr = buffer_data_pointer(buf);
00301     const uint8_t *group = ptr + 4;
00302     if (!addr_is_ipv6_multicast(group)) {
00303         goto invalid;
00304     }
00305 
00306     if_group_entry_t *entry = addr_get_group_entry(cur, group);
00307     if (!entry) {
00308         goto invalid;
00309     }
00310 
00311     /* "Report received" event - stop timer, clear flag, become Idle Listener */
00312 
00313     if (entry->mld_timer && !addr_is_assigned_to_interface(cur, buf->src_sa .address )) {
00314         entry->mld_timer = 0;
00315         entry->mld_last_reporter = false;
00316     }
00317 
00318 invalid:
00319     if (buf) {
00320         buffer_free(buf);
00321     }
00322 
00323     return NULL;
00324 }
00325 
00326 void mld_start_listening(protocol_interface_info_entry_t *interface, if_group_entry_t *entry)
00327 {
00328     if (thread_info(interface)) {
00329         thread_mcast_group_change(interface, entry, true);
00330         return;
00331     }
00332 
00333     /* "Send MLD" flag only controls sending unsolicited reports when we join. We will still always respond if queried */
00334     if (interface->send_mld && addr_ipv6_multicast_scope(entry->group) >= IPV6_SCOPE_LINK_LOCAL) {
00335         entry->mld_timer = randLIB_get_random_in_range(1, MLD_UNSOLICITED_REPORT_INTERVAL);
00336         entry->mld_last_reporter = true;
00337         protocol_push(mld_build(interface, ICMPV6_TYPE_INFO_MCAST_LIST_REPORT, 0, entry->group));
00338     } else {
00339         entry->mld_timer = 0;
00340         entry->mld_last_reporter = false;
00341     }
00342 }
00343 
00344 void mld_stop_listening(protocol_interface_info_entry_t *interface, if_group_entry_t *entry)
00345 {
00346     if (thread_info(interface)) {
00347         thread_mcast_group_change(interface, entry, false);
00348         return;
00349     }
00350 
00351     if (entry->mld_last_reporter) {
00352         protocol_push(mld_build(interface, ICMPV6_TYPE_INFO_MCAST_LIST_DONE, 0, entry->group));
00353     }
00354 }
00355 
00356 void mld_slow_timer(protocol_interface_info_entry_t *interface, uint_fast16_t seconds)
00357 {
00358     static uint8_t query_timer;
00359     query_timer += seconds;
00360     if (query_timer >= 125) {
00361         query_timer = 0;
00362         if (mld_querier) {
00363             protocol_push(mld_build(interface, ICMPV6_TYPE_INFO_MCAST_LIST_QUERY, 10000, ADDR_UNSPECIFIED));
00364         }
00365     }
00366 }
00367 
00368 void mld_fast_timer(protocol_interface_info_entry_t *interface, uint_fast16_t ticks)
00369 {
00370     if (thread_info(interface)) {
00371         return;
00372     }
00373 
00374     ns_list_foreach(if_group_entry_t, entry, &interface->ip_groups) {
00375         if (entry->mld_timer == 0) {
00376             continue;
00377         }
00378 
00379         if (entry->mld_timer > ticks) {
00380             entry->mld_timer -= ticks;
00381             continue;
00382         }
00383 
00384         entry->mld_timer = 0;
00385         entry->mld_last_reporter = true;
00386         protocol_push(mld_build(interface, ICMPV6_TYPE_INFO_MCAST_LIST_REPORT, 0, entry->group));
00387     }
00388 }