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.
mpl.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 #include "nsconfig.h" 00018 00019 #ifdef HAVE_MPL 00020 00021 #include "ns_types.h" 00022 #include "ns_list.h" 00023 #include "ns_trace.h" 00024 #include "common_functions.h" 00025 #include "nsdynmemLIB.h" 00026 #include "randLIB.h" 00027 #include <string.h> 00028 #include "Core/include/ns_buffer.h" 00029 #include "NWK_INTERFACE/Include/protocol.h" 00030 #include "NWK_INTERFACE/Include/protocol_timer.h" 00031 #include "Common_Protocols/ipv6.h" 00032 #include "Common_Protocols/icmpv6.h" 00033 #include "Service_Libs/Trickle/trickle.h" 00034 #include "6LoWPAN/MAC/mac_helper.h" 00035 #include "6LoWPAN/Thread/thread_common.h" 00036 #include "MPL/mpl.h" 00037 00038 #define TRACE_GROUP "mpl" 00039 00040 #define MPL_OPT_S_MASK 0xC0 00041 #define MPL_OPT_S_SHIFT 6 00042 #define MPL_OPT_M 0x20 00043 #define MPL_OPT_V 0x10 00044 00045 #define MPL_SEED_IPV6_SRC 0 00046 #define MPL_SEED_16_BIT 1 00047 #define MPL_SEED_64_BIT 2 00048 #define MPL_SEED_128_BIT 3 00049 00050 #define MAX_BUFFERED_MESSAGES_SIZE 2048 00051 #define MAX_BUFFERED_MESSAGE_LIFETIME 600 // 1/10 s ticks 00052 00053 static bool mpl_timer_running; 00054 static uint16_t mpl_total_buffered; 00055 00056 const trickle_params_t rfc7731_default_data_message_trickle_params = { 00057 .Imin = MPL_MS_TO_TICKS(512), /* RFC 7731 says 10 * expected link latency; ZigBee IP says 512 ms */ 00058 .Imax = MPL_MS_TO_TICKS(512), /* RFC 7731 says equal to Imin; ZigBee IP says 512 ms */ 00059 .k = 1, /* RFC 7731 says 1; ZigBee IP says infinite */ 00060 .TimerExpirations = 3 /* RFC 7731 says 3; ZigBee IP says 2 for routers, 0 for hosts */ 00061 }; 00062 00063 const trickle_params_t rfc7731_default_control_message_trickle_params = { 00064 .Imin = MPL_MS_TO_TICKS(512), /* RFC 7731 says 10 * worst-case link latency */ 00065 .Imax = MPL_MS_TO_TICKS(300000),/* 5 minutes, as per RFC 7731 */ 00066 .k = 1, 00067 .TimerExpirations = 10 00068 }; 00069 00070 /* Note that we don't use a buffer_t, to save a little RAM. We don't need 00071 * any of the metadata it stores... 00072 */ 00073 typedef struct mpl_data_message 00074 { 00075 bool running; 00076 bool colour; 00077 uint32_t timestamp; 00078 trickle_t trickle; 00079 ns_list_link_t link; 00080 uint16_t mpl_opt_data_offset; /* offset to option data of MPL option */ 00081 uint8_t message[]; 00082 } mpl_buffered_message_t; 00083 00084 typedef struct mpl_seed 00085 { 00086 ns_list_link_t link; 00087 bool colour; 00088 uint16_t lifetime; 00089 uint8_t min_sequence; 00090 uint8_t id_len; 00091 NS_LIST_HEAD (mpl_buffered_message_t, link) messages; /* sequence number order */ 00092 uint8_t id[]; 00093 } mpl_seed_t; 00094 00095 /* For simplicity, we assume each MPL domain is on exactly 1 interface */ 00096 struct mpl_domain 00097 { 00098 protocol_interface_info_entry_t *interface; 00099 uint8_t address[16]; 00100 uint8_t sequence; 00101 bool colour; 00102 bool proactive_forwarding; 00103 uint16_t seed_set_entry_lifetime; 00104 NS_LIST_HEAD (mpl_seed_t, link) seeds; 00105 trickle_t trickle; // Control timer 00106 trickle_params_t data_trickle_params; 00107 trickle_params_t control_trickle_params; 00108 ns_list_link_t link; 00109 multicast_mpl_seed_id_mode_e seed_id_mode; 00110 uint8_t seed_id[]; 00111 }; 00112 00113 static NS_LIST_DEFINE(mpl_domains, mpl_domain_t, link); 00114 00115 static void mpl_buffer_delete(mpl_seed_t *seed, mpl_buffered_message_t *message); 00116 static void mpl_control_reset_or_start(mpl_domain_t *domain); 00117 static void mpl_schedule_timer(void); 00118 static void mpl_fast_timer(uint16_t ticks); 00119 static buffer_t *mpl_exthdr_provider(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result); 00120 static void mpl_seed_delete(mpl_domain_t *domain, mpl_seed_t *seed); 00121 00122 static bool mpl_initted; 00123 00124 static void mpl_init(void) 00125 { 00126 if (mpl_initted) { 00127 return; 00128 } 00129 mpl_initted = true; 00130 00131 ipv6_set_exthdr_provider(ROUTE_MPL, mpl_exthdr_provider); 00132 } 00133 00134 static uint8_t mpl_buffer_sequence(const mpl_buffered_message_t *message) 00135 { 00136 return message->message[message->mpl_opt_data_offset + 1]; 00137 } 00138 00139 static uint16_t mpl_buffer_size(const mpl_buffered_message_t *message) 00140 { 00141 return IPV6_HDRLEN + common_read_16_bit(message->message + IPV6_HDROFF_PAYLOAD_LENGTH); 00142 } 00143 00144 mpl_domain_t *mpl_domain_lookup(protocol_interface_info_entry_t *cur, const uint8_t address[16]) 00145 { 00146 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00147 if (domain->interface == cur && addr_ipv6_equal(domain->address, address)) { 00148 return domain; 00149 } 00150 } 00151 return NULL; 00152 } 00153 00154 mpl_domain_t *mpl_domain_lookup_with_realm_check(protocol_interface_info_entry_t *cur, const uint8_t address[16]) 00155 { 00156 if (!addr_is_ipv6_multicast(address)) { 00157 return NULL; 00158 } 00159 00160 if (addr_ipv6_multicast_scope(address) == IPV6_SCOPE_REALM_LOCAL && cur->mpl_treat_realm_domains_as_one) { 00161 address = ADDR_ALL_MPL_FORWARDERS; 00162 } 00163 00164 return mpl_domain_lookup(cur, address); 00165 } 00166 00167 /* Look up domain by address, ignoring the scop field, so ff22::1 matches ff23::1 */ 00168 /* We assume all addresses are multicast, so don't bother checking the first byte */ 00169 static mpl_domain_t *mpl_domain_lookup_ignoring_scop(protocol_interface_info_entry_t *cur, const uint8_t address[16]) 00170 { 00171 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00172 if (domain->interface == cur && 00173 memcmp(address + 2, domain->address + 2, 14) == 0 && 00174 (address[1] & 0xf0) == (domain->address[1] & 0xf0)) { 00175 return domain; 00176 } 00177 } 00178 return NULL; 00179 } 00180 00181 static int mpl_domain_count_on_interface(protocol_interface_info_entry_t *cur) 00182 { 00183 int count = 0; 00184 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00185 if (domain->interface == cur) { 00186 count++; 00187 } 00188 } 00189 return count; 00190 } 00191 00192 mpl_domain_t *mpl_domain_create(protocol_interface_info_entry_t *cur, const uint8_t address[16], 00193 const uint8_t *seed_id, multicast_mpl_seed_id_mode_e seed_id_mode, 00194 int_fast8_t proactive_forwarding, 00195 uint16_t seed_set_entry_lifetime, 00196 const trickle_params_t *data_trickle_params, 00197 const trickle_params_t *control_trickle_params) 00198 { 00199 if (!addr_is_ipv6_multicast(address) || addr_ipv6_multicast_scope(address) < IPV6_SCOPE_REALM_LOCAL) { 00200 return NULL; 00201 } 00202 00203 if (addr_ipv6_multicast_scope(address) == IPV6_SCOPE_REALM_LOCAL && cur->mpl_treat_realm_domains_as_one && 00204 !addr_ipv6_equal(address, ADDR_ALL_MPL_FORWARDERS)) { 00205 return NULL; 00206 } 00207 00208 mpl_init(); 00209 00210 /* We lock out attempts to join two domains differing only by scop - this 00211 * is because we couldn't distinguish control messages, which are sent 00212 * to the link-local version of the same address. Seems to be a 00213 * specification limitation? 00214 */ 00215 if (mpl_domain_lookup_ignoring_scop(cur, address)) { 00216 return NULL; 00217 } 00218 00219 if (seed_id_mode == MULTICAST_MPL_SEED_ID_DEFAULT) { 00220 seed_id_mode = cur->mpl_seed_id_mode; 00221 seed_id = cur->mpl_seed_id; 00222 } 00223 00224 uint8_t seed_id_len; 00225 if (seed_id_mode > 0) { 00226 seed_id_len = seed_id_mode; 00227 } else { 00228 seed_id_len = 0; 00229 } 00230 00231 mpl_domain_t *domain = ns_dyn_mem_alloc(sizeof *domain + seed_id_len); 00232 if (!domain) { 00233 return NULL; 00234 } 00235 memcpy(domain->address, address, 16); 00236 domain->interface = cur; 00237 domain->sequence = randLIB_get_8bit(); 00238 domain->colour = false; 00239 ns_list_init(&domain->seeds); 00240 domain->proactive_forwarding = proactive_forwarding >= 0 ? proactive_forwarding 00241 : cur->mpl_proactive_forwarding; 00242 domain->seed_set_entry_lifetime = seed_set_entry_lifetime ? seed_set_entry_lifetime 00243 : cur->mpl_seed_set_entry_lifetime; 00244 domain->data_trickle_params = data_trickle_params ? *data_trickle_params 00245 : cur->mpl_data_trickle_params; 00246 domain->control_trickle_params = control_trickle_params ? *control_trickle_params 00247 : cur->mpl_control_trickle_params; 00248 trickle_start(&domain->trickle, &domain->control_trickle_params); 00249 trickle_stop(&domain->trickle); 00250 domain->seed_id_mode = seed_id_mode; 00251 memcpy(domain->seed_id, seed_id, seed_id_len); 00252 ns_list_add_to_end(&mpl_domains, domain); 00253 00254 //ipv6_route_add_with_info(address, 128, cur->id, NULL, ROUTE_MPL, domain, 0, 0xffffffff, 0); 00255 addr_add_group(cur, address); 00256 if (domain->control_trickle_params.TimerExpirations != 0) { 00257 uint8_t ll_scope[16]; 00258 memcpy(ll_scope, address, 16); 00259 ll_scope[1] = (ll_scope[1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL; 00260 addr_add_group(cur, ll_scope); 00261 } 00262 00263 /* If we just created the first domain on an interface, auto-create the all-forwarders domain (this does nothing if we're already a member) */ 00264 if (mpl_domain_count_on_interface(cur) == 1) { 00265 /* Use default interface parameters */ 00266 mpl_domain_create(cur, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL); 00267 cur->mpl_seed = true; 00268 } 00269 00270 return domain; 00271 } 00272 00273 bool mpl_domain_delete(protocol_interface_info_entry_t *cur, const uint8_t address[16]) 00274 { 00275 mpl_domain_t *domain = mpl_domain_lookup(cur, address); 00276 if (!domain) { 00277 return false; 00278 } 00279 int count = mpl_domain_count_on_interface(cur); 00280 00281 /* Don't let them delete all-mpl-forwarders unless it's the last */ 00282 if (addr_ipv6_equal(address, ADDR_ALL_MPL_FORWARDERS)) { 00283 if (count != 1) { 00284 return true; 00285 } 00286 cur->mpl_seed = false; 00287 } 00288 00289 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) { 00290 mpl_seed_delete(domain, seed); 00291 } 00292 00293 //ipv6_route_delete(address, 128, cur->id, NULL, ROUTE_MPL); 00294 addr_delete_group(cur, address); 00295 if (domain->control_trickle_params.TimerExpirations != 0) { 00296 uint8_t ll_scope[16]; 00297 memcpy(ll_scope, domain->address, 16); 00298 ll_scope[1] = (ll_scope[1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL; 00299 addr_delete_group(cur, ll_scope); 00300 } 00301 ns_list_remove(&mpl_domains, domain); 00302 ns_dyn_mem_free(domain); 00303 return true; 00304 } 00305 00306 void mpl_domain_change_timing(mpl_domain_t *domain, const struct trickle_params *data_trickle_params, uint16_t seed_set_entry_lifetime) 00307 { 00308 domain->data_trickle_params = *data_trickle_params; 00309 domain->seed_set_entry_lifetime = seed_set_entry_lifetime; 00310 } 00311 00312 static void mpl_domain_inconsistent(mpl_domain_t *domain) 00313 { 00314 trickle_inconsistent_heard(&domain->trickle, &domain->control_trickle_params); 00315 mpl_schedule_timer(); 00316 } 00317 00318 static mpl_seed_t *mpl_seed_lookup(const mpl_domain_t *domain, uint8_t id_len, const uint8_t *seed_id) 00319 { 00320 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00321 if (seed->id_len == id_len && memcmp(seed->id, seed_id, id_len) == 0) { 00322 return seed; 00323 } 00324 } 00325 00326 return NULL; 00327 } 00328 00329 static mpl_seed_t *mpl_seed_create(mpl_domain_t *domain, uint8_t id_len, const uint8_t *seed_id, uint8_t sequence) 00330 { 00331 mpl_seed_t *seed = ns_dyn_mem_alloc(sizeof(mpl_seed_t) + id_len); 00332 if (!seed) { 00333 return NULL; 00334 } 00335 00336 seed->min_sequence = sequence; 00337 seed->lifetime = domain->seed_set_entry_lifetime; 00338 seed->id_len = id_len; 00339 seed->colour = domain->colour; 00340 ns_list_init(&seed->messages); 00341 memcpy(seed->id, seed_id, id_len); 00342 ns_list_add_to_end(&domain->seeds, seed); 00343 return seed; 00344 } 00345 00346 static void mpl_seed_delete(mpl_domain_t *domain, mpl_seed_t *seed) 00347 { 00348 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) { 00349 mpl_buffer_delete(seed, message); 00350 } 00351 ns_list_remove(&domain->seeds, seed); 00352 ns_dyn_mem_free(seed); 00353 } 00354 00355 static void mpl_seed_advance_min_sequence(mpl_seed_t *seed, uint8_t min_sequence) 00356 { 00357 seed->min_sequence = min_sequence; 00358 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) { 00359 if (common_serial_number_greater_8(min_sequence, mpl_buffer_sequence(message))) { 00360 mpl_buffer_delete(seed, message); 00361 } 00362 } 00363 } 00364 00365 static mpl_buffered_message_t *mpl_buffer_lookup(mpl_seed_t *seed, uint8_t sequence) 00366 { 00367 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) { 00368 if (mpl_buffer_sequence(message) == sequence) { 00369 return message; 00370 } 00371 } 00372 return NULL; 00373 } 00374 00375 static void mpl_free_space(void) 00376 { 00377 mpl_seed_t *oldest_seed = NULL; 00378 mpl_buffered_message_t *oldest_message = NULL; 00379 00380 /* We'll free one message - earliest sequence number from one seed */ 00381 /* Choose which seed by looking at the timestamp - oldest one first */ 00382 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00383 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00384 mpl_buffered_message_t *message = ns_list_get_first(&seed->messages); 00385 if (!message) { 00386 continue; 00387 } 00388 if (!oldest_message || 00389 protocol_core_monotonic_time - message->timestamp > protocol_core_monotonic_time - oldest_message->timestamp) { 00390 oldest_message = message; 00391 oldest_seed = seed; 00392 } 00393 } 00394 } 00395 00396 if (!oldest_message) { 00397 return; 00398 } 00399 00400 oldest_seed->min_sequence = mpl_buffer_sequence(oldest_message) + 1; 00401 mpl_buffer_delete(oldest_seed, oldest_message); 00402 } 00403 00404 00405 static mpl_buffered_message_t *mpl_buffer_create(buffer_t *buf, mpl_domain_t *domain, mpl_seed_t *seed, uint8_t sequence, uint8_t hop_limit) 00406 { 00407 /* IP layer ensures buffer length == IP length */ 00408 uint16_t ip_len = buffer_data_length(buf); 00409 00410 while (mpl_total_buffered + ip_len > MAX_BUFFERED_MESSAGES_SIZE) { 00411 mpl_free_space(); 00412 } 00413 00414 /* As we came in, message sequence was >= min_sequence, but mpl_free_space 00415 * could end up pushing min_sequence forward. We must take care and 00416 * re-check min_sequence. 00417 * 00418 * For example, let's say min_sequence=1, we're holding 1,3,5, and we receive 2. 00419 * a) If mpl_free_space doesn't touch this seed, we're fine. 00420 * b) If it frees 1, it will advance min_sequence to 2, and we're fine. 00421 * c) If it frees 1 and 3, it will advance min_sequence to 4, and we cannot 00422 * accept this message. (If we forced min_sequence to 2, we'd end up processing 00423 * message 3 again). 00424 */ 00425 if (common_serial_number_greater_8(seed->min_sequence, sequence)) { 00426 tr_debug("Can no longer accept %"PRIu8" < %"PRIu8, sequence, seed->min_sequence); 00427 return NULL; 00428 } 00429 00430 mpl_buffered_message_t *message = ns_dyn_mem_alloc(sizeof(mpl_buffered_message_t) + ip_len); 00431 if (!message) { 00432 return NULL; 00433 } 00434 memcpy(message->message, buffer_data_pointer(buf), ip_len); 00435 message->message[IPV6_HDROFF_HOP_LIMIT] = hop_limit; 00436 message->mpl_opt_data_offset = buf->mpl_option_data_offset; 00437 message->colour = seed->colour; 00438 message->timestamp = protocol_core_monotonic_time; 00439 /* Make sure trickle structure is initialised */ 00440 trickle_start(&message->trickle, &domain->data_trickle_params); 00441 if (domain->proactive_forwarding) { 00442 mpl_schedule_timer(); 00443 } else { 00444 /* Then stop it if not proactive */ 00445 trickle_stop(&message->trickle); 00446 } 00447 00448 /* Messages held ordered - eg for benefit of mpl_seed_bm_len() */ 00449 bool inserted = false; 00450 ns_list_foreach_reverse(mpl_buffered_message_t, m, &seed->messages) { 00451 if (common_serial_number_greater_8(sequence, mpl_buffer_sequence(m))) { 00452 ns_list_add_after(&seed->messages, m, message); 00453 inserted = true; 00454 break; 00455 } 00456 } 00457 if (!inserted) { 00458 ns_list_add_to_start(&seed->messages, message); 00459 } 00460 mpl_total_buffered += ip_len; 00461 00462 /* Does MPL spec intend this distinction between start and reset? */ 00463 mpl_control_reset_or_start(domain); 00464 00465 return message; 00466 } 00467 00468 static void mpl_buffer_delete(mpl_seed_t *seed, mpl_buffered_message_t *message) 00469 { 00470 mpl_total_buffered -= mpl_buffer_size(message); 00471 ns_list_remove(&seed->messages, message); 00472 ns_dyn_mem_free(message); 00473 } 00474 00475 static void mpl_buffer_transmit(mpl_domain_t *domain, mpl_buffered_message_t *message, bool newest) 00476 { 00477 uint16_t ip_len = mpl_buffer_size(message); 00478 buffer_t *buf = buffer_get(ip_len); 00479 if (!buf) { 00480 return; 00481 } 00482 00483 buffer_data_add(buf, message->message, ip_len); 00484 00485 /* Modify the M flag [Thread says it must be clear] */ 00486 uint8_t *flag = buffer_data_pointer(buf) + message->mpl_opt_data_offset; 00487 if (newest && !thread_info(domain->interface)) { 00488 *flag |= MPL_OPT_M; 00489 } else { 00490 *flag &= ~MPL_OPT_M; 00491 } 00492 00493 // Make sure ip_routed_up is set, even on locally-seeded packets, to 00494 // distinguishes the "forwarded" copies from the original seed. 00495 // Used to suppress extra copies to sleepy children. 00496 buf->ip_routed_up = true; 00497 buf->dst_sa .addr_type = ADDR_IPV6 ; 00498 buf->src_sa .addr_type = ADDR_IPV6 ; 00499 memcpy(buf->dst_sa .address , message->message + IPV6_HDROFF_DST_ADDR, 16); 00500 memcpy(buf->src_sa .address , message->message + IPV6_HDROFF_SRC_ADDR, 16); 00501 00502 ipv6_transmit_multicast_on_interface(buf, domain->interface ); 00503 } 00504 00505 static void mpl_buffer_inconsistent(const mpl_domain_t *domain, mpl_buffered_message_t *message) 00506 { 00507 trickle_inconsistent_heard(&message->trickle, &domain->data_trickle_params); 00508 mpl_schedule_timer(); 00509 } 00510 00511 static uint8_t mpl_seed_bm_len(const mpl_seed_t *seed) 00512 { 00513 mpl_buffered_message_t *last = ns_list_get_last(&seed->messages); 00514 if (last) { 00515 return ((uint8_t) (mpl_buffer_sequence(last) - seed->min_sequence)) / 8 + 1; 00516 } else { 00517 return 0; 00518 } 00519 } 00520 00521 /* Attempt to optimise by saying ID is source IPv6 */ 00522 static uint16_t mpl_seed_info_size(const mpl_seed_t *seed, const uint8_t *src) 00523 { 00524 uint8_t id_len = seed->id_len; 00525 if (id_len == 16 && src && addr_ipv6_equal(src, seed->id)) { 00526 id_len = 0; 00527 } 00528 return 2 + id_len + mpl_seed_bm_len(seed); 00529 } 00530 00531 static uint8_t *mpl_write_seed_info(uint8_t *ptr, const mpl_seed_t *seed, const uint8_t *src) 00532 { 00533 uint8_t bm_len = mpl_seed_bm_len(seed); 00534 ptr[0] = seed->min_sequence; 00535 ptr[1] = bm_len << 2; 00536 uint8_t id_len = seed->id_len; 00537 if (id_len == 16 && src && addr_ipv6_equal(src, seed->id)) { 00538 id_len = 0; 00539 } 00540 switch (id_len) { 00541 case 0: ptr[1] |= MPL_SEED_IPV6_SRC; break; 00542 case 2: ptr[1] |= MPL_SEED_16_BIT; break; 00543 case 8: ptr[1] |= MPL_SEED_64_BIT; break; 00544 case 16: ptr[1] |= MPL_SEED_128_BIT; break; 00545 default: return ptr; 00546 } 00547 ptr += 2; 00548 memcpy(ptr, seed->id, id_len); 00549 ptr += id_len; 00550 memset(ptr, 0, bm_len); 00551 ns_list_foreach(mpl_buffered_message_t, buffer, &seed->messages) { 00552 uint8_t i = mpl_buffer_sequence(buffer) - seed->min_sequence; 00553 bit_set(ptr, i); 00554 } 00555 ptr += bm_len; 00556 return ptr; 00557 } 00558 00559 /* Does MPL spec really intend this distinction between start and reset? */ 00560 /* (Reset sets interval to Imin, Start puts it somewhere random between Imin and Imax) */ 00561 static void mpl_control_reset_or_start(mpl_domain_t *domain) 00562 { 00563 if (trickle_running(&domain->trickle, &domain->control_trickle_params)) { 00564 trickle_inconsistent_heard(&domain->trickle, &domain->control_trickle_params); 00565 } else { 00566 trickle_start(&domain->trickle, &domain->control_trickle_params); 00567 } 00568 mpl_schedule_timer(); 00569 } 00570 00571 static uint8_t mpl_seed_id_len(uint8_t seed_id_type) 00572 { 00573 static const uint8_t len[] = { 00574 [MPL_SEED_IPV6_SRC] = 0, 00575 [MPL_SEED_16_BIT] = 2, 00576 [MPL_SEED_64_BIT] = 8, 00577 [MPL_SEED_128_BIT] = 16 00578 }; 00579 return len[seed_id_type]; 00580 } 00581 00582 static uint8_t mpl_seed_id_type(uint8_t seed_id_len) 00583 { 00584 switch (seed_id_len) { 00585 default: return MPL_SEED_IPV6_SRC; 00586 case 2: return MPL_SEED_16_BIT; 00587 case 8: return MPL_SEED_64_BIT; 00588 case 16: return MPL_SEED_128_BIT; 00589 } 00590 } 00591 00592 /* 00593 * 0 1 2 3 00594 * 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 00595 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00596 * | min-seqno | bm-len | S | seed-id (0/2/8/16 octets) | 00597 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00598 * | | 00599 * . buffered-mpl-messages (variable length) . 00600 * . . 00601 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00602 */ 00603 static void mpl_send_control(mpl_domain_t *domain) 00604 { 00605 uint16_t size = 0; 00606 const uint8_t *src = NULL; 00607 00608 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00609 /* If not chosen yet, pick source to match a seed id, to save 16 bytes */ 00610 if (!src && seed->id_len == 16 && addr_is_assigned_to_interface(domain->interface, seed->id)) { 00611 src = seed->id; 00612 } 00613 size += mpl_seed_info_size(seed, src); 00614 } 00615 buffer_t *buf = buffer_get(size); 00616 if (!buf) { 00617 return; 00618 } 00619 uint8_t *ptr = buffer_data_pointer(buf); 00620 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00621 ptr = mpl_write_seed_info(ptr, seed, src); 00622 } 00623 buffer_data_end_set(buf, ptr); 00624 buf->options .type = ICMPV6_TYPE_INFO_MPL_CONTROL; 00625 buf->options .code = 0; 00626 buf->options .hop_limit = 255; 00627 memcpy(buf->dst_sa .address , domain->address, 16); 00628 buf->dst_sa .address [1] = (buf->dst_sa .address [1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL; 00629 buf->dst_sa .addr_type = ADDR_IPV6 ; 00630 if (src) { 00631 buf->src_sa .addr_type = ADDR_IPV6 ; 00632 memcpy(buf->src_sa .address , src, 16); 00633 } 00634 buf->info = (buffer_info_t)(B_FROM_ICMP | B_TO_ICMP | B_DIR_DOWN); 00635 buf->interface = domain->interface; 00636 protocol_push(buf); 00637 } 00638 00639 /* 00640 * There is an edge case in control handling when the hop limit runs out. This 00641 * is handled as follows: 00642 * 00643 * Hop Limit 2 Hop Limit 1 [Won't Forward] 00644 * Seed ---------------> Forwarder -------------> Final Node -------X------> Neighbour Node 00645 * In Message Set In Message Set MinSequence advanced Not In Message Set 00646 * 00647 * The Final Node does NOT add the message to its buffered message set, and it 00648 * advances MinSequence so that doesn't have to report about the message either 00649 * positively or negatively in control messages. 00650 * 00651 * If it reported "present" in control messages, the Neighbour Node would see a "missing" 00652 * message and reset its control timer. If it reported "absent", the Forwarder would 00653 * notice the inconsistency and resend. So we sidestep the issue by advancing MinSequence. 00654 * This also saves RAM - we'd never retransmit the message anyway, so why buffer it? 00655 * 00656 * This means we drop out-of-order packets at the edge of a hop limit boundary, 00657 * but this isn't a huge deal. 00658 */ 00659 buffer_t *mpl_control_handler(buffer_t *buf, protocol_interface_info_entry_t *cur) 00660 { 00661 if (!addr_is_ipv6_multicast(buf->dst_sa .address ) || addr_ipv6_multicast_scope(buf->dst_sa .address ) != IPV6_SCOPE_LINK_LOCAL || buf->options .hop_limit != 255) { 00662 tr_warn("Invalid control"); 00663 return buffer_free(buf); 00664 } 00665 00666 /* Um, how do we distinguish between multiple domains with different scop? 00667 * Control messages just have the domain address with scop 2. Currently 00668 * deal with that by not letting users join two domains differing only in 00669 * scop. 00670 */ 00671 00672 mpl_domain_t *domain = mpl_domain_lookup_ignoring_scop(cur, buf->dst_sa .address ); 00673 if (!domain) { 00674 return buffer_free(buf); 00675 } 00676 00677 bool they_have_new_data = false; 00678 bool we_have_new_data = false; 00679 const uint8_t *ptr = buffer_data_pointer(buf); 00680 const uint8_t *end = buffer_data_end(buf); 00681 00682 // All objects will currently have the same colour. The scan 00683 // of the control message will flip the colour of every mentioned seed 00684 // and data message. Then the omission of anything we have will be detected 00685 // by its colour not being flipped. 00686 // This is equivalent to having a "mentioned" flag, except we don't have 00687 // to have a separate "reset" loop. 00688 bool new_colour = --domain->colour; // smart-alec binary flip 00689 00690 while (ptr < end) { 00691 if (end - ptr < 2) { 00692 tr_err("MPL control error"); 00693 break; 00694 } 00695 uint8_t min_seqno = ptr[0]; 00696 uint8_t bm_len = ptr[1] >> 2; 00697 uint8_t seed_id_type = ptr[1] & 3; 00698 uint8_t seed_id_len = mpl_seed_id_len(seed_id_type); 00699 ptr += 2; 00700 /* Sequence number is 8-bit, so bitmask should never be bigger than 32 bytes */ 00701 if (bm_len > 32 || end - ptr < seed_id_len + bm_len) { 00702 tr_err("MPL control error"); 00703 break; 00704 } 00705 const uint8_t *seed_id; 00706 if (seed_id_type == MPL_SEED_IPV6_SRC) { 00707 seed_id = buf->src_sa .address ; 00708 seed_id_len = 16; 00709 /* Thread spec says, or at least implies, that ML16/RLOC address is 00710 * matched against corresponding 16-bit seed id (although 00711 * Thread doesn't use control messages...) */ 00712 if (thread_addr_is_mesh_local_16(seed_id, cur)) { 00713 seed_id += 14; 00714 seed_id_len = 2; 00715 } 00716 } else { 00717 seed_id = ptr; 00718 ptr += seed_id_len; 00719 } 00720 mpl_seed_t *seed = mpl_seed_lookup(domain, seed_id_len, seed_id); 00721 if (!seed) { 00722 they_have_new_data = true; 00723 ptr += bm_len; 00724 continue; 00725 } 00726 00727 seed->colour = new_colour; 00728 /* They are assumed to not be interested in messages lower than their min_seqno */ 00729 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) { 00730 if (common_serial_number_greater_8(min_seqno, mpl_buffer_sequence(message))) { 00731 message->colour = new_colour; 00732 } 00733 } 00734 for (uint8_t i = 0; i / 8 < bm_len; i++) { 00735 if (bit_test(ptr, i)) { 00736 mpl_buffered_message_t *message = mpl_buffer_lookup(seed, min_seqno + i); 00737 00738 if (!message && common_serial_number_greater_8(min_seqno + i, seed->min_sequence)) { 00739 they_have_new_data = true; 00740 } else if (message) { 00741 message->colour = new_colour; 00742 } 00743 } 00744 } 00745 ptr += bm_len; 00746 } 00747 00748 /* Search for seeds or messages they haven't mentioned */ 00749 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00750 if (seed->colour != new_colour) { 00751 seed->colour = new_colour; 00752 we_have_new_data = true; 00753 } 00754 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) { 00755 if (message->colour != new_colour) { 00756 message->colour = new_colour; 00757 mpl_buffer_inconsistent(domain, message); 00758 we_have_new_data = true; 00759 } 00760 } 00761 } 00762 00763 if (they_have_new_data || we_have_new_data) { 00764 if (they_have_new_data) { 00765 tr_info("%s has new MPL data", trace_ipv6(buf->src_sa .address )); 00766 } 00767 if (we_have_new_data) { 00768 tr_info("We have new MPL data for %s", trace_ipv6(buf->src_sa .address )); 00769 } 00770 mpl_domain_inconsistent(domain); 00771 } else { 00772 trickle_consistent_heard(&domain->trickle); 00773 } 00774 00775 00776 return buffer_free(buf); 00777 } 00778 00779 bool mpl_hbh_len_check(const uint8_t *opt_data, uint8_t opt_data_len) 00780 { 00781 if (opt_data_len < 2) { 00782 return false; 00783 } 00784 if (opt_data[0] & MPL_OPT_V) { 00785 return true; /* No length complaint - we let "process" drop */ 00786 } 00787 00788 uint8_t seed_id_type = (opt_data[0] & MPL_OPT_S_MASK) >> MPL_OPT_S_SHIFT; 00789 /* Note that option is allowed to be longer - spec allows for extension 00790 * beyond seed-id. 00791 */ 00792 if (opt_data_len < 2 + mpl_seed_id_len(seed_id_type)) { 00793 return false; 00794 } 00795 return true; 00796 } 00797 00798 /* 0 1 2 3 00799 * 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 00800 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00801 * | Option Type | Opt Data Len | 00802 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00803 * | S |M|V| rsv | sequence | seed-id (optional) | 00804 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00805 */ 00806 00807 bool mpl_process_hbh(buffer_t *buf, protocol_interface_info_entry_t *cur, uint8_t *opt_data) 00808 { 00809 if ((buf->options .ip_extflags & IPEXT_HBH_MPL) || buf->options .ll_security_bypass_rx ) { 00810 tr_warn("Bad MPL"); 00811 return false; 00812 } 00813 00814 /* mpl_hbh_len_check has already returned true, so know length is okay */ 00815 00816 /* V flag indicates incompatible new version - packets MUST be dropped */ 00817 if (opt_data[0] & MPL_OPT_V) { 00818 tr_warn("MPL V!"); 00819 return false; 00820 } 00821 00822 mpl_domain_t *domain = mpl_domain_lookup_with_realm_check(cur, buf->dst_sa .address ); 00823 if (!domain) { 00824 tr_debug("No MPL domain"); 00825 return false; 00826 } 00827 00828 buf->options .ip_extflags |= IPEXT_HBH_MPL; 00829 buf->mpl_option_data_offset = opt_data - buffer_data_pointer(buf); 00830 00831 return true; 00832 // return mpl_forwarder_process_message(buf, domain, opt_data); 00833 } 00834 00835 /* seeding is true if this is processing an outgoing message */ 00836 bool mpl_forwarder_process_message(buffer_t *buf, mpl_domain_t *domain, bool seeding) 00837 { 00838 const uint8_t *opt_data = buffer_data_pointer(buf) + buf->mpl_option_data_offset; 00839 uint8_t sequence = opt_data[1]; 00840 uint8_t seed_id_type = (opt_data[0] & MPL_OPT_S_MASK) >> MPL_OPT_S_SHIFT; 00841 const uint8_t *seed_id = opt_data + 2; 00842 uint8_t seed_id_len = mpl_seed_id_len(seed_id_type); 00843 00844 /* Special handling - just ignore the MPL option if receiving loopback copy. 00845 * (MPL gets to process the outgoing message, and with seeding true - when 00846 * looping back, we want to accept it without MPL getting in the way). 00847 */ 00848 if (!seeding && buf->options .multicast_loop ) { 00849 return true; 00850 } 00851 00852 if (!domain) { 00853 domain = mpl_domain_lookup_with_realm_check(buf->interface , buf->dst_sa .address ); 00854 if (!domain) { 00855 tr_debug("No domain %s %s", tr_ipv6(domain->address), tr_array(seed_id, seed_id_len)); 00856 return false; 00857 } 00858 } 00859 00860 if (seed_id_type == MPL_SEED_IPV6_SRC) { 00861 seed_id = buf->src_sa .address ; 00862 seed_id_len = 16; 00863 /* Thread spec says, or at least implies, that ML16/RLOC address is 00864 * matched against corresponding 16-bit seed id */ 00865 if (thread_addr_is_mesh_local_16(seed_id, buf->interface )) { 00866 seed_id += 14; 00867 seed_id_len = 2; 00868 } 00869 } 00870 00871 tr_debug("seed %s seq %"PRIu8, tr_array(seed_id, seed_id_len), sequence); 00872 mpl_seed_t *seed = mpl_seed_lookup(domain, seed_id_len, seed_id); 00873 if (!seed) { 00874 seed = mpl_seed_create(domain, seed_id_len, seed_id, sequence); 00875 if (!seed) { 00876 tr_debug("No seed %s %s", tr_ipv6(domain->address), tr_array(seed_id, seed_id_len)); 00877 return false; 00878 } 00879 } 00880 00881 /* If the M flag is set, we report an inconsistency against any messages with higher sequences */ 00882 if ((opt_data[0] & MPL_OPT_M) && !thread_info(buf->interface )) { 00883 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) { 00884 if (common_serial_number_greater_8(mpl_buffer_sequence(message), sequence)) { 00885 mpl_buffer_inconsistent(domain, message); 00886 } 00887 } 00888 } 00889 00890 /* Drop old messages (sequence < MinSequence) */ 00891 if (common_serial_number_greater_8(seed->min_sequence, sequence)) { 00892 tr_debug("Old MPL message %"PRIu8" < %"PRIu8, sequence, seed->min_sequence); 00893 return false; 00894 } 00895 00896 mpl_buffered_message_t *message = mpl_buffer_lookup(seed, sequence); 00897 if (message) { 00898 tr_debug("Repeated MPL message %"PRIu8, sequence); 00899 trickle_consistent_heard(&message->trickle); 00900 return false; 00901 } 00902 00903 seed->lifetime = domain->seed_set_entry_lifetime; 00904 00905 uint8_t hop_limit = buffer_data_pointer(buf)[IPV6_HDROFF_HOP_LIMIT]; 00906 if (!seeding && hop_limit != 0) { 00907 hop_limit--; 00908 } 00909 00910 if (domain->data_trickle_params.TimerExpirations == 0 || hop_limit == 0 || 00911 (thread_info(domain->interface) && !thread_i_am_router(domain->interface))) { 00912 /* As a non-forwarder, just accept the packet and advance the 00913 * min_sequence - means we will drop anything arriving out-of-order, but 00914 * old implementation always did this in all cases anyway (even if 00915 * being a forwarder). 00916 * 00917 * We also do this if hop limit is 0, so we are not going to forward. 00918 * This avoids the edge case discussed in the comment above mpl_control_handler. 00919 * 00920 * And finally, also treat Thread non-routers like this, to avoid 00921 * need to dynamically changing TimerExpirations. 00922 */ 00923 mpl_seed_advance_min_sequence(seed, sequence + 1); 00924 return true; 00925 } 00926 00927 message = mpl_buffer_create(buf, domain, seed, sequence, hop_limit); 00928 00929 return true; 00930 } 00931 00932 00933 static void mpl_schedule_timer(void) 00934 { 00935 if (!mpl_timer_running) { 00936 mpl_timer_running = true; 00937 protocol_timer_start(PROTOCOL_TIMER_MULTICAST_TIM, mpl_fast_timer, MPL_TICK_MS); 00938 } 00939 } 00940 00941 static void mpl_fast_timer(uint16_t ticks) 00942 { 00943 bool need_timer = false; 00944 mpl_timer_running = false; 00945 00946 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00947 if (trickle_timer(&domain->trickle, &domain->control_trickle_params, ticks)) { 00948 mpl_send_control(domain); 00949 } 00950 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) { 00951 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) { 00952 if (trickle_timer(&message->trickle, &domain->data_trickle_params, ticks)) { 00953 mpl_buffer_transmit(domain, message, ns_list_get_next(&seed->messages, message) == NULL); 00954 } 00955 need_timer = need_timer || trickle_running(&message->trickle, &domain->data_trickle_params); 00956 } 00957 } 00958 need_timer = need_timer || trickle_running(&domain->trickle, &domain->control_trickle_params); 00959 } 00960 00961 if (need_timer) { 00962 mpl_schedule_timer(); 00963 } 00964 } 00965 00966 void mpl_slow_timer(uint16_t seconds) 00967 { 00968 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 00969 uint32_t message_age_limit = (domain->seed_set_entry_lifetime * UINT32_C(10)) / 4; 00970 if (message_age_limit > MAX_BUFFERED_MESSAGE_LIFETIME) { 00971 message_age_limit = MAX_BUFFERED_MESSAGE_LIFETIME; 00972 } 00973 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) { 00974 /* Count down seed lifetime, and expire immediately when hit */ 00975 if (seed->lifetime > seconds) { 00976 seed->lifetime -= seconds; 00977 } else { 00978 mpl_seed_delete(domain, seed); 00979 continue; 00980 } 00981 /* Once data trickle timer has stopped, we MAY delete a message by 00982 * advancing MinSequence. We use timestamp to control this, so we 00983 * can hold beyond just the initial data transmission, permitting 00984 * it to be restarted by control messages. 00985 */ 00986 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) { 00987 if (!trickle_running(&message->trickle, &domain->data_trickle_params) && 00988 protocol_core_monotonic_time - message->timestamp >= message_age_limit) { 00989 seed->min_sequence = mpl_buffer_sequence(message) + 1; 00990 mpl_buffer_delete(seed, message); 00991 } else { 00992 break; 00993 } 00994 } 00995 } 00996 } 00997 } 00998 00999 void mpl_clear_realm_scope_seeds(protocol_interface_info_entry_t *cur) 01000 { 01001 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) { 01002 if (domain->interface == cur && addr_ipv6_multicast_scope(domain->address) <= IPV6_SCOPE_REALM_LOCAL) { 01003 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) { 01004 mpl_seed_delete(domain, seed); 01005 } 01006 } 01007 } 01008 } 01009 01010 static buffer_t *mpl_exthdr_provider(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result) 01011 { 01012 mpl_domain_t *domain = mpl_domain_lookup_with_realm_check(buf->interface , buf->dst_sa .address ); 01013 if (!domain) { 01014 // We need to tunnel 01015 01016 if (stage != IPV6_EXTHDR_MODIFY) { 01017 *result = 0; 01018 return buf; 01019 } 01020 01021 *result = IPV6_EXTHDR_MODIFY_TUNNEL; 01022 memcpy(buf->dst_sa .address , ADDR_ALL_MPL_FORWARDERS, 16); 01023 buf->src_sa .addr_type = ADDR_NONE ; // force auto-selection 01024 return buf; 01025 } 01026 01027 const uint8_t *seed_id; 01028 uint8_t seed_id_len; 01029 uint8_t seed_id_buf[16]; 01030 if (domain->seed_id_mode > 0) { 01031 seed_id_len = domain->seed_id_mode; 01032 seed_id = domain->seed_id; 01033 } else switch (domain->seed_id_mode) { 01034 case MULTICAST_MPL_SEED_ID_MAC_SHORT: { 01035 uint16_t addr = mac_helper_mac16_address_get(buf->interface ); 01036 if (addr < 0xfffe) { 01037 common_write_16_bit(addr, seed_id_buf); 01038 seed_id = seed_id_buf; 01039 seed_id_len = 2; 01040 break; 01041 } 01042 // Otherwise fall through to extended 01043 case MULTICAST_MPL_SEED_ID_MAC: 01044 seed_id = buf->interface ->mac; 01045 seed_id_len = 8; 01046 break; 01047 01048 case MULTICAST_MPL_SEED_ID_IID_EUI64: 01049 seed_id = buf->interface ->iid_eui64; 01050 seed_id_len = 8; 01051 break; 01052 01053 case MULTICAST_MPL_SEED_ID_IID_SLAAC: 01054 seed_id = buf->interface ->iid_slaac; 01055 seed_id_len = 8; 01056 break; 01057 } 01058 01059 default: 01060 case MULTICAST_MPL_SEED_ID_IPV6_SRC_FOR_DOMAIN: 01061 seed_id = addr_select_source(buf->interface , domain->address, 0); 01062 seed_id_len = 16; 01063 break; 01064 } 01065 01066 if (!seed_id) { 01067 tr_err("No MPL Seed ID"); 01068 return buffer_free(buf); 01069 } 01070 01071 /* "Compress" seed ID if it's the IPv6 source address */ 01072 /* (For Thread, also compress if source is the 16-bit address) */ 01073 if (seed_id_len == 16 && addr_ipv6_equal(seed_id, buf->src_sa .address )) { 01074 seed_id_len = 0; 01075 } else if (seed_id_len == 2 && thread_addr_is_mesh_local_16(buf->src_sa .address , buf->interface ) && 01076 seed_id[0] == buf->src_sa .address [14] && seed_id[1] == buf->src_sa .address [15]) { 01077 seed_id_len = 0; 01078 } 01079 01080 switch (stage) { 01081 case IPV6_EXTHDR_SIZE: 01082 *result = 4 + seed_id_len; 01083 return buf; 01084 case IPV6_EXTHDR_INSERT: { 01085 /* Only have 4 possible lengths/padding patterns to consider: 01086 * HbH 2 + Option header 4 + Seed 0 + Padding 2 = 8 01087 * HbH 2 + Option header 4 + Seed 2 + Padding 0 = 8 01088 * HbH 2 + Option header 4 + Seed 8 + Padding 2 = 16 01089 * HbH 2 + Option header 4 + Seed 16 + Padding 2 = 24 01090 */ 01091 uint8_t extlen = (6 + seed_id_len + 7) &~ 7; 01092 buf = buffer_headroom(buf, extlen); 01093 if (!buf) { 01094 return NULL; 01095 } 01096 uint8_t *ext = buffer_data_reserve_header(buf, extlen); 01097 ext[0] = buf->options .type ; 01098 buf->options .type = IPV6_NH_HOP_BY_HOP; 01099 ext[1] = (extlen / 8) - 1; 01100 ext[2] = IPV6_OPTION_MPL; 01101 ext[3] = 2 + seed_id_len; 01102 ext[4] = (mpl_seed_id_type(seed_id_len) << MPL_OPT_S_SHIFT); 01103 ext[5] = 0; // sequence placeholder 01104 memcpy(ext + 6, seed_id, seed_id_len); 01105 if (seed_id_len != 2) { 01106 ext[extlen - 2] = IPV6_OPTION_PADN; 01107 ext[extlen - 1] = 0; 01108 } 01109 01110 *result = 0; 01111 buf->options .ip_extflags |= IPEXT_HBH_MPL | IPEXT_HBH_MPL_UNFILLED; 01112 return buf; 01113 } 01114 case IPV6_EXTHDR_MODIFY: 01115 if (buf->options .ip_extflags & IPEXT_HBH_MPL_UNFILLED) { 01116 /* We assume we created this, therefore our option is in place 01117 * in the expected place. Sequence is set now, AFTER 01118 * fragmentation. 01119 */ 01120 uint8_t *iphdr = buffer_data_pointer(buf); 01121 uint8_t *ext = iphdr + IPV6_HDRLEN; 01122 if (iphdr[IPV6_HDROFF_NH] != IPV6_NH_HOP_BY_HOP || ext[2] != IPV6_OPTION_MPL) { 01123 tr_err("modify"); 01124 return buffer_free(buf); 01125 } 01126 /* We don't bother setting the M flag on these initial packets. Setting to 0 is always acceptable. */ 01127 ext[5] = domain->sequence++; 01128 buf->options .ip_extflags &=~ IPEXT_HBH_MPL_UNFILLED; 01129 buf->mpl_option_data_offset = IPV6_HDRLEN + 4; 01130 mpl_forwarder_process_message(buf, domain, true); 01131 } 01132 *result = 0; 01133 return buf; 01134 default: 01135 return buffer_free(buf); 01136 } 01137 } 01138 01139 #endif /* HAVE_MPL */ 01140
Generated on Tue Jul 12 2022 14:24:28 by
