BA
/
BaBoRo1
Embed:
(wiki syntax)
Show/hide line numbers
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 12:22:12 by
