Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 }
Generated on Tue Jul 12 2022 14:24:28 by
