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.
Dependents: cobaLCDJoyMotor_Thread odometry_omni_3roda_v3 odometry_omni_3roda_v1 odometry_omni_3roda_v2 ... more
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 } else { 01132 uint8_t *iphdr = buffer_data_pointer(buf); 01133 uint8_t *ext = iphdr + IPV6_HDRLEN; 01134 } 01135 *result = 0; 01136 return buf; 01137 default: 01138 return buffer_free(buf); 01139 } 01140 } 01141 01142 #endif /* HAVE_MPL */ 01143
Generated on Tue Jul 12 2022 13:03:05 by
