takashi kadono / Mbed OS Nucleo446_SSD1331

Dependencies:   ssd1331

Committer:
kadonotakashi
Date:
Thu Oct 11 02:27:46 2018 +0000
Revision:
3:f3764f852aa8
Parent:
0:8fdf9a60065b
Nucreo 446 + SSD1331 test version;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kadonotakashi 0:8fdf9a60065b 1 /*
kadonotakashi 0:8fdf9a60065b 2 * Copyright (c) 2015-2018, Arm Limited and affiliates.
kadonotakashi 0:8fdf9a60065b 3 * SPDX-License-Identifier: Apache-2.0
kadonotakashi 0:8fdf9a60065b 4 *
kadonotakashi 0:8fdf9a60065b 5 * Licensed under the Apache License, Version 2.0 (the "License");
kadonotakashi 0:8fdf9a60065b 6 * you may not use this file except in compliance with the License.
kadonotakashi 0:8fdf9a60065b 7 * You may obtain a copy of the License at
kadonotakashi 0:8fdf9a60065b 8 *
kadonotakashi 0:8fdf9a60065b 9 * http://www.apache.org/licenses/LICENSE-2.0
kadonotakashi 0:8fdf9a60065b 10 *
kadonotakashi 0:8fdf9a60065b 11 * Unless required by applicable law or agreed to in writing, software
kadonotakashi 0:8fdf9a60065b 12 * distributed under the License is distributed on an "AS IS" BASIS,
kadonotakashi 0:8fdf9a60065b 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
kadonotakashi 0:8fdf9a60065b 14 * See the License for the specific language governing permissions and
kadonotakashi 0:8fdf9a60065b 15 * limitations under the License.
kadonotakashi 0:8fdf9a60065b 16 */
kadonotakashi 0:8fdf9a60065b 17 #include "nsconfig.h"
kadonotakashi 0:8fdf9a60065b 18
kadonotakashi 0:8fdf9a60065b 19 #ifdef HAVE_MPL
kadonotakashi 0:8fdf9a60065b 20
kadonotakashi 0:8fdf9a60065b 21 #include "ns_types.h"
kadonotakashi 0:8fdf9a60065b 22 #include "ns_list.h"
kadonotakashi 0:8fdf9a60065b 23 #include "ns_trace.h"
kadonotakashi 0:8fdf9a60065b 24 #include "common_functions.h"
kadonotakashi 0:8fdf9a60065b 25 #include "nsdynmemLIB.h"
kadonotakashi 0:8fdf9a60065b 26 #include "randLIB.h"
kadonotakashi 0:8fdf9a60065b 27 #include <string.h>
kadonotakashi 0:8fdf9a60065b 28 #include "Core/include/ns_buffer.h"
kadonotakashi 0:8fdf9a60065b 29 #include "NWK_INTERFACE/Include/protocol.h"
kadonotakashi 0:8fdf9a60065b 30 #include "NWK_INTERFACE/Include/protocol_timer.h"
kadonotakashi 0:8fdf9a60065b 31 #include "Common_Protocols/ipv6.h"
kadonotakashi 0:8fdf9a60065b 32 #include "Common_Protocols/icmpv6.h"
kadonotakashi 0:8fdf9a60065b 33 #include "Service_Libs/Trickle/trickle.h"
kadonotakashi 0:8fdf9a60065b 34 #include "6LoWPAN/MAC/mac_helper.h"
kadonotakashi 0:8fdf9a60065b 35 #include "6LoWPAN/Thread/thread_common.h"
kadonotakashi 0:8fdf9a60065b 36 #include "MPL/mpl.h"
kadonotakashi 0:8fdf9a60065b 37
kadonotakashi 0:8fdf9a60065b 38 #define TRACE_GROUP "mpl"
kadonotakashi 0:8fdf9a60065b 39
kadonotakashi 0:8fdf9a60065b 40 #define MPL_OPT_S_MASK 0xC0
kadonotakashi 0:8fdf9a60065b 41 #define MPL_OPT_S_SHIFT 6
kadonotakashi 0:8fdf9a60065b 42 #define MPL_OPT_M 0x20
kadonotakashi 0:8fdf9a60065b 43 #define MPL_OPT_V 0x10
kadonotakashi 0:8fdf9a60065b 44
kadonotakashi 0:8fdf9a60065b 45 #define MPL_SEED_IPV6_SRC 0
kadonotakashi 0:8fdf9a60065b 46 #define MPL_SEED_16_BIT 1
kadonotakashi 0:8fdf9a60065b 47 #define MPL_SEED_64_BIT 2
kadonotakashi 0:8fdf9a60065b 48 #define MPL_SEED_128_BIT 3
kadonotakashi 0:8fdf9a60065b 49
kadonotakashi 0:8fdf9a60065b 50 #define MAX_BUFFERED_MESSAGES_SIZE 2048
kadonotakashi 0:8fdf9a60065b 51 #define MAX_BUFFERED_MESSAGE_LIFETIME 600 // 1/10 s ticks
kadonotakashi 0:8fdf9a60065b 52
kadonotakashi 0:8fdf9a60065b 53 static bool mpl_timer_running;
kadonotakashi 0:8fdf9a60065b 54 static uint16_t mpl_total_buffered;
kadonotakashi 0:8fdf9a60065b 55
kadonotakashi 0:8fdf9a60065b 56 const trickle_params_t rfc7731_default_data_message_trickle_params = {
kadonotakashi 0:8fdf9a60065b 57 .Imin = MPL_MS_TO_TICKS(512), /* RFC 7731 says 10 * expected link latency; ZigBee IP says 512 ms */
kadonotakashi 0:8fdf9a60065b 58 .Imax = MPL_MS_TO_TICKS(512), /* RFC 7731 says equal to Imin; ZigBee IP says 512 ms */
kadonotakashi 0:8fdf9a60065b 59 .k = 1, /* RFC 7731 says 1; ZigBee IP says infinite */
kadonotakashi 0:8fdf9a60065b 60 .TimerExpirations = 3 /* RFC 7731 says 3; ZigBee IP says 2 for routers, 0 for hosts */
kadonotakashi 0:8fdf9a60065b 61 };
kadonotakashi 0:8fdf9a60065b 62
kadonotakashi 0:8fdf9a60065b 63 const trickle_params_t rfc7731_default_control_message_trickle_params = {
kadonotakashi 0:8fdf9a60065b 64 .Imin = MPL_MS_TO_TICKS(512), /* RFC 7731 says 10 * worst-case link latency */
kadonotakashi 0:8fdf9a60065b 65 .Imax = MPL_MS_TO_TICKS(300000),/* 5 minutes, as per RFC 7731 */
kadonotakashi 0:8fdf9a60065b 66 .k = 1,
kadonotakashi 0:8fdf9a60065b 67 .TimerExpirations = 10
kadonotakashi 0:8fdf9a60065b 68 };
kadonotakashi 0:8fdf9a60065b 69
kadonotakashi 0:8fdf9a60065b 70 /* Note that we don't use a buffer_t, to save a little RAM. We don't need
kadonotakashi 0:8fdf9a60065b 71 * any of the metadata it stores...
kadonotakashi 0:8fdf9a60065b 72 */
kadonotakashi 0:8fdf9a60065b 73 typedef struct mpl_data_message
kadonotakashi 0:8fdf9a60065b 74 {
kadonotakashi 0:8fdf9a60065b 75 bool running;
kadonotakashi 0:8fdf9a60065b 76 bool colour;
kadonotakashi 0:8fdf9a60065b 77 uint32_t timestamp;
kadonotakashi 0:8fdf9a60065b 78 trickle_t trickle;
kadonotakashi 0:8fdf9a60065b 79 ns_list_link_t link;
kadonotakashi 0:8fdf9a60065b 80 uint16_t mpl_opt_data_offset; /* offset to option data of MPL option */
kadonotakashi 0:8fdf9a60065b 81 uint8_t message[];
kadonotakashi 0:8fdf9a60065b 82 } mpl_buffered_message_t;
kadonotakashi 0:8fdf9a60065b 83
kadonotakashi 0:8fdf9a60065b 84 typedef struct mpl_seed
kadonotakashi 0:8fdf9a60065b 85 {
kadonotakashi 0:8fdf9a60065b 86 ns_list_link_t link;
kadonotakashi 0:8fdf9a60065b 87 bool colour;
kadonotakashi 0:8fdf9a60065b 88 uint16_t lifetime;
kadonotakashi 0:8fdf9a60065b 89 uint8_t min_sequence;
kadonotakashi 0:8fdf9a60065b 90 uint8_t id_len;
kadonotakashi 0:8fdf9a60065b 91 NS_LIST_HEAD(mpl_buffered_message_t, link) messages; /* sequence number order */
kadonotakashi 0:8fdf9a60065b 92 uint8_t id[];
kadonotakashi 0:8fdf9a60065b 93 } mpl_seed_t;
kadonotakashi 0:8fdf9a60065b 94
kadonotakashi 0:8fdf9a60065b 95 /* For simplicity, we assume each MPL domain is on exactly 1 interface */
kadonotakashi 0:8fdf9a60065b 96 struct mpl_domain
kadonotakashi 0:8fdf9a60065b 97 {
kadonotakashi 0:8fdf9a60065b 98 protocol_interface_info_entry_t *interface;
kadonotakashi 0:8fdf9a60065b 99 uint8_t address[16];
kadonotakashi 0:8fdf9a60065b 100 uint8_t sequence;
kadonotakashi 0:8fdf9a60065b 101 bool colour;
kadonotakashi 0:8fdf9a60065b 102 bool proactive_forwarding;
kadonotakashi 0:8fdf9a60065b 103 uint16_t seed_set_entry_lifetime;
kadonotakashi 0:8fdf9a60065b 104 NS_LIST_HEAD(mpl_seed_t, link) seeds;
kadonotakashi 0:8fdf9a60065b 105 trickle_t trickle; // Control timer
kadonotakashi 0:8fdf9a60065b 106 trickle_params_t data_trickle_params;
kadonotakashi 0:8fdf9a60065b 107 trickle_params_t control_trickle_params;
kadonotakashi 0:8fdf9a60065b 108 ns_list_link_t link;
kadonotakashi 0:8fdf9a60065b 109 multicast_mpl_seed_id_mode_e seed_id_mode;
kadonotakashi 0:8fdf9a60065b 110 uint8_t seed_id[];
kadonotakashi 0:8fdf9a60065b 111 };
kadonotakashi 0:8fdf9a60065b 112
kadonotakashi 0:8fdf9a60065b 113 static NS_LIST_DEFINE(mpl_domains, mpl_domain_t, link);
kadonotakashi 0:8fdf9a60065b 114
kadonotakashi 0:8fdf9a60065b 115 static void mpl_buffer_delete(mpl_seed_t *seed, mpl_buffered_message_t *message);
kadonotakashi 0:8fdf9a60065b 116 static void mpl_control_reset_or_start(mpl_domain_t *domain);
kadonotakashi 0:8fdf9a60065b 117 static void mpl_schedule_timer(void);
kadonotakashi 0:8fdf9a60065b 118 static void mpl_fast_timer(uint16_t ticks);
kadonotakashi 0:8fdf9a60065b 119 static buffer_t *mpl_exthdr_provider(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result);
kadonotakashi 0:8fdf9a60065b 120 static void mpl_seed_delete(mpl_domain_t *domain, mpl_seed_t *seed);
kadonotakashi 0:8fdf9a60065b 121
kadonotakashi 0:8fdf9a60065b 122 static bool mpl_initted;
kadonotakashi 0:8fdf9a60065b 123
kadonotakashi 0:8fdf9a60065b 124 static void mpl_init(void)
kadonotakashi 0:8fdf9a60065b 125 {
kadonotakashi 0:8fdf9a60065b 126 if (mpl_initted) {
kadonotakashi 0:8fdf9a60065b 127 return;
kadonotakashi 0:8fdf9a60065b 128 }
kadonotakashi 0:8fdf9a60065b 129 mpl_initted = true;
kadonotakashi 0:8fdf9a60065b 130
kadonotakashi 0:8fdf9a60065b 131 ipv6_set_exthdr_provider(ROUTE_MPL, mpl_exthdr_provider);
kadonotakashi 0:8fdf9a60065b 132 }
kadonotakashi 0:8fdf9a60065b 133
kadonotakashi 0:8fdf9a60065b 134 static uint8_t mpl_buffer_sequence(const mpl_buffered_message_t *message)
kadonotakashi 0:8fdf9a60065b 135 {
kadonotakashi 0:8fdf9a60065b 136 return message->message[message->mpl_opt_data_offset + 1];
kadonotakashi 0:8fdf9a60065b 137 }
kadonotakashi 0:8fdf9a60065b 138
kadonotakashi 0:8fdf9a60065b 139 static uint16_t mpl_buffer_size(const mpl_buffered_message_t *message)
kadonotakashi 0:8fdf9a60065b 140 {
kadonotakashi 0:8fdf9a60065b 141 return IPV6_HDRLEN + common_read_16_bit(message->message + IPV6_HDROFF_PAYLOAD_LENGTH);
kadonotakashi 0:8fdf9a60065b 142 }
kadonotakashi 0:8fdf9a60065b 143
kadonotakashi 0:8fdf9a60065b 144 mpl_domain_t *mpl_domain_lookup(protocol_interface_info_entry_t *cur, const uint8_t address[16])
kadonotakashi 0:8fdf9a60065b 145 {
kadonotakashi 0:8fdf9a60065b 146 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 147 if (domain->interface == cur && addr_ipv6_equal(domain->address, address)) {
kadonotakashi 0:8fdf9a60065b 148 return domain;
kadonotakashi 0:8fdf9a60065b 149 }
kadonotakashi 0:8fdf9a60065b 150 }
kadonotakashi 0:8fdf9a60065b 151 return NULL;
kadonotakashi 0:8fdf9a60065b 152 }
kadonotakashi 0:8fdf9a60065b 153
kadonotakashi 0:8fdf9a60065b 154 mpl_domain_t *mpl_domain_lookup_with_realm_check(protocol_interface_info_entry_t *cur, const uint8_t address[16])
kadonotakashi 0:8fdf9a60065b 155 {
kadonotakashi 0:8fdf9a60065b 156 if (!addr_is_ipv6_multicast(address)) {
kadonotakashi 0:8fdf9a60065b 157 return NULL;
kadonotakashi 0:8fdf9a60065b 158 }
kadonotakashi 0:8fdf9a60065b 159
kadonotakashi 0:8fdf9a60065b 160 if (addr_ipv6_multicast_scope(address) == IPV6_SCOPE_REALM_LOCAL && cur->mpl_treat_realm_domains_as_one) {
kadonotakashi 0:8fdf9a60065b 161 address = ADDR_ALL_MPL_FORWARDERS;
kadonotakashi 0:8fdf9a60065b 162 }
kadonotakashi 0:8fdf9a60065b 163
kadonotakashi 0:8fdf9a60065b 164 return mpl_domain_lookup(cur, address);
kadonotakashi 0:8fdf9a60065b 165 }
kadonotakashi 0:8fdf9a60065b 166
kadonotakashi 0:8fdf9a60065b 167 /* Look up domain by address, ignoring the scop field, so ff22::1 matches ff23::1 */
kadonotakashi 0:8fdf9a60065b 168 /* We assume all addresses are multicast, so don't bother checking the first byte */
kadonotakashi 0:8fdf9a60065b 169 static mpl_domain_t *mpl_domain_lookup_ignoring_scop(protocol_interface_info_entry_t *cur, const uint8_t address[16])
kadonotakashi 0:8fdf9a60065b 170 {
kadonotakashi 0:8fdf9a60065b 171 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 172 if (domain->interface == cur &&
kadonotakashi 0:8fdf9a60065b 173 memcmp(address + 2, domain->address + 2, 14) == 0 &&
kadonotakashi 0:8fdf9a60065b 174 (address[1] & 0xf0) == (domain->address[1] & 0xf0)) {
kadonotakashi 0:8fdf9a60065b 175 return domain;
kadonotakashi 0:8fdf9a60065b 176 }
kadonotakashi 0:8fdf9a60065b 177 }
kadonotakashi 0:8fdf9a60065b 178 return NULL;
kadonotakashi 0:8fdf9a60065b 179 }
kadonotakashi 0:8fdf9a60065b 180
kadonotakashi 0:8fdf9a60065b 181 static int mpl_domain_count_on_interface(protocol_interface_info_entry_t *cur)
kadonotakashi 0:8fdf9a60065b 182 {
kadonotakashi 0:8fdf9a60065b 183 int count = 0;
kadonotakashi 0:8fdf9a60065b 184 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 185 if (domain->interface == cur) {
kadonotakashi 0:8fdf9a60065b 186 count++;
kadonotakashi 0:8fdf9a60065b 187 }
kadonotakashi 0:8fdf9a60065b 188 }
kadonotakashi 0:8fdf9a60065b 189 return count;
kadonotakashi 0:8fdf9a60065b 190 }
kadonotakashi 0:8fdf9a60065b 191
kadonotakashi 0:8fdf9a60065b 192 mpl_domain_t *mpl_domain_create(protocol_interface_info_entry_t *cur, const uint8_t address[16],
kadonotakashi 0:8fdf9a60065b 193 const uint8_t *seed_id, multicast_mpl_seed_id_mode_e seed_id_mode,
kadonotakashi 0:8fdf9a60065b 194 int_fast8_t proactive_forwarding,
kadonotakashi 0:8fdf9a60065b 195 uint16_t seed_set_entry_lifetime,
kadonotakashi 0:8fdf9a60065b 196 const trickle_params_t *data_trickle_params,
kadonotakashi 0:8fdf9a60065b 197 const trickle_params_t *control_trickle_params)
kadonotakashi 0:8fdf9a60065b 198 {
kadonotakashi 0:8fdf9a60065b 199 if (!addr_is_ipv6_multicast(address) || addr_ipv6_multicast_scope(address) < IPV6_SCOPE_REALM_LOCAL) {
kadonotakashi 0:8fdf9a60065b 200 return NULL;
kadonotakashi 0:8fdf9a60065b 201 }
kadonotakashi 0:8fdf9a60065b 202
kadonotakashi 0:8fdf9a60065b 203 if (addr_ipv6_multicast_scope(address) == IPV6_SCOPE_REALM_LOCAL && cur->mpl_treat_realm_domains_as_one &&
kadonotakashi 0:8fdf9a60065b 204 !addr_ipv6_equal(address, ADDR_ALL_MPL_FORWARDERS)) {
kadonotakashi 0:8fdf9a60065b 205 return NULL;
kadonotakashi 0:8fdf9a60065b 206 }
kadonotakashi 0:8fdf9a60065b 207
kadonotakashi 0:8fdf9a60065b 208 mpl_init();
kadonotakashi 0:8fdf9a60065b 209
kadonotakashi 0:8fdf9a60065b 210 /* We lock out attempts to join two domains differing only by scop - this
kadonotakashi 0:8fdf9a60065b 211 * is because we couldn't distinguish control messages, which are sent
kadonotakashi 0:8fdf9a60065b 212 * to the link-local version of the same address. Seems to be a
kadonotakashi 0:8fdf9a60065b 213 * specification limitation?
kadonotakashi 0:8fdf9a60065b 214 */
kadonotakashi 0:8fdf9a60065b 215 if (mpl_domain_lookup_ignoring_scop(cur, address)) {
kadonotakashi 0:8fdf9a60065b 216 return NULL;
kadonotakashi 0:8fdf9a60065b 217 }
kadonotakashi 0:8fdf9a60065b 218
kadonotakashi 0:8fdf9a60065b 219 if (seed_id_mode == MULTICAST_MPL_SEED_ID_DEFAULT) {
kadonotakashi 0:8fdf9a60065b 220 seed_id_mode = cur->mpl_seed_id_mode;
kadonotakashi 0:8fdf9a60065b 221 seed_id = cur->mpl_seed_id;
kadonotakashi 0:8fdf9a60065b 222 }
kadonotakashi 0:8fdf9a60065b 223
kadonotakashi 0:8fdf9a60065b 224 uint8_t seed_id_len;
kadonotakashi 0:8fdf9a60065b 225 if (seed_id_mode > 0) {
kadonotakashi 0:8fdf9a60065b 226 seed_id_len = seed_id_mode;
kadonotakashi 0:8fdf9a60065b 227 } else {
kadonotakashi 0:8fdf9a60065b 228 seed_id_len = 0;
kadonotakashi 0:8fdf9a60065b 229 }
kadonotakashi 0:8fdf9a60065b 230
kadonotakashi 0:8fdf9a60065b 231 mpl_domain_t *domain = ns_dyn_mem_alloc(sizeof *domain + seed_id_len);
kadonotakashi 0:8fdf9a60065b 232 if (!domain) {
kadonotakashi 0:8fdf9a60065b 233 return NULL;
kadonotakashi 0:8fdf9a60065b 234 }
kadonotakashi 0:8fdf9a60065b 235 memcpy(domain->address, address, 16);
kadonotakashi 0:8fdf9a60065b 236 domain->interface = cur;
kadonotakashi 0:8fdf9a60065b 237 domain->sequence = randLIB_get_8bit();
kadonotakashi 0:8fdf9a60065b 238 domain->colour = false;
kadonotakashi 0:8fdf9a60065b 239 ns_list_init(&domain->seeds);
kadonotakashi 0:8fdf9a60065b 240 domain->proactive_forwarding = proactive_forwarding >= 0 ? proactive_forwarding
kadonotakashi 0:8fdf9a60065b 241 : cur->mpl_proactive_forwarding;
kadonotakashi 0:8fdf9a60065b 242 domain->seed_set_entry_lifetime = seed_set_entry_lifetime ? seed_set_entry_lifetime
kadonotakashi 0:8fdf9a60065b 243 : cur->mpl_seed_set_entry_lifetime;
kadonotakashi 0:8fdf9a60065b 244 domain->data_trickle_params = data_trickle_params ? *data_trickle_params
kadonotakashi 0:8fdf9a60065b 245 : cur->mpl_data_trickle_params;
kadonotakashi 0:8fdf9a60065b 246 domain->control_trickle_params = control_trickle_params ? *control_trickle_params
kadonotakashi 0:8fdf9a60065b 247 : cur->mpl_control_trickle_params;
kadonotakashi 0:8fdf9a60065b 248 trickle_start(&domain->trickle, &domain->control_trickle_params);
kadonotakashi 0:8fdf9a60065b 249 trickle_stop(&domain->trickle);
kadonotakashi 0:8fdf9a60065b 250 domain->seed_id_mode = seed_id_mode;
kadonotakashi 0:8fdf9a60065b 251 memcpy(domain->seed_id, seed_id, seed_id_len);
kadonotakashi 0:8fdf9a60065b 252 ns_list_add_to_end(&mpl_domains, domain);
kadonotakashi 0:8fdf9a60065b 253
kadonotakashi 0:8fdf9a60065b 254 //ipv6_route_add_with_info(address, 128, cur->id, NULL, ROUTE_MPL, domain, 0, 0xffffffff, 0);
kadonotakashi 0:8fdf9a60065b 255 addr_add_group(cur, address);
kadonotakashi 0:8fdf9a60065b 256 if (domain->control_trickle_params.TimerExpirations != 0) {
kadonotakashi 0:8fdf9a60065b 257 uint8_t ll_scope[16];
kadonotakashi 0:8fdf9a60065b 258 memcpy(ll_scope, address, 16);
kadonotakashi 0:8fdf9a60065b 259 ll_scope[1] = (ll_scope[1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL;
kadonotakashi 0:8fdf9a60065b 260 addr_add_group(cur, ll_scope);
kadonotakashi 0:8fdf9a60065b 261 }
kadonotakashi 0:8fdf9a60065b 262
kadonotakashi 0:8fdf9a60065b 263 /* 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) */
kadonotakashi 0:8fdf9a60065b 264 if (mpl_domain_count_on_interface(cur) == 1) {
kadonotakashi 0:8fdf9a60065b 265 /* Use default interface parameters */
kadonotakashi 0:8fdf9a60065b 266 mpl_domain_create(cur, ADDR_ALL_MPL_FORWARDERS, NULL, MULTICAST_MPL_SEED_ID_DEFAULT, -1, 0, NULL, NULL);
kadonotakashi 0:8fdf9a60065b 267 cur->mpl_seed = true;
kadonotakashi 0:8fdf9a60065b 268 }
kadonotakashi 0:8fdf9a60065b 269
kadonotakashi 0:8fdf9a60065b 270 return domain;
kadonotakashi 0:8fdf9a60065b 271 }
kadonotakashi 0:8fdf9a60065b 272
kadonotakashi 0:8fdf9a60065b 273 bool mpl_domain_delete(protocol_interface_info_entry_t *cur, const uint8_t address[16])
kadonotakashi 0:8fdf9a60065b 274 {
kadonotakashi 0:8fdf9a60065b 275 mpl_domain_t *domain = mpl_domain_lookup(cur, address);
kadonotakashi 0:8fdf9a60065b 276 if (!domain) {
kadonotakashi 0:8fdf9a60065b 277 return false;
kadonotakashi 0:8fdf9a60065b 278 }
kadonotakashi 0:8fdf9a60065b 279 int count = mpl_domain_count_on_interface(cur);
kadonotakashi 0:8fdf9a60065b 280
kadonotakashi 0:8fdf9a60065b 281 /* Don't let them delete all-mpl-forwarders unless it's the last */
kadonotakashi 0:8fdf9a60065b 282 if (addr_ipv6_equal(address, ADDR_ALL_MPL_FORWARDERS)) {
kadonotakashi 0:8fdf9a60065b 283 if (count != 1) {
kadonotakashi 0:8fdf9a60065b 284 return true;
kadonotakashi 0:8fdf9a60065b 285 }
kadonotakashi 0:8fdf9a60065b 286 cur->mpl_seed = false;
kadonotakashi 0:8fdf9a60065b 287 }
kadonotakashi 0:8fdf9a60065b 288
kadonotakashi 0:8fdf9a60065b 289 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 290 mpl_seed_delete(domain, seed);
kadonotakashi 0:8fdf9a60065b 291 }
kadonotakashi 0:8fdf9a60065b 292
kadonotakashi 0:8fdf9a60065b 293 //ipv6_route_delete(address, 128, cur->id, NULL, ROUTE_MPL);
kadonotakashi 0:8fdf9a60065b 294 addr_delete_group(cur, address);
kadonotakashi 0:8fdf9a60065b 295 if (domain->control_trickle_params.TimerExpirations != 0) {
kadonotakashi 0:8fdf9a60065b 296 uint8_t ll_scope[16];
kadonotakashi 0:8fdf9a60065b 297 memcpy(ll_scope, domain->address, 16);
kadonotakashi 0:8fdf9a60065b 298 ll_scope[1] = (ll_scope[1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL;
kadonotakashi 0:8fdf9a60065b 299 addr_delete_group(cur, ll_scope);
kadonotakashi 0:8fdf9a60065b 300 }
kadonotakashi 0:8fdf9a60065b 301 ns_list_remove(&mpl_domains, domain);
kadonotakashi 0:8fdf9a60065b 302 ns_dyn_mem_free(domain);
kadonotakashi 0:8fdf9a60065b 303 return true;
kadonotakashi 0:8fdf9a60065b 304 }
kadonotakashi 0:8fdf9a60065b 305
kadonotakashi 0:8fdf9a60065b 306 void mpl_domain_change_timing(mpl_domain_t *domain, const struct trickle_params *data_trickle_params, uint16_t seed_set_entry_lifetime)
kadonotakashi 0:8fdf9a60065b 307 {
kadonotakashi 0:8fdf9a60065b 308 domain->data_trickle_params = *data_trickle_params;
kadonotakashi 0:8fdf9a60065b 309 domain->seed_set_entry_lifetime = seed_set_entry_lifetime;
kadonotakashi 0:8fdf9a60065b 310 }
kadonotakashi 0:8fdf9a60065b 311
kadonotakashi 0:8fdf9a60065b 312 static void mpl_domain_inconsistent(mpl_domain_t *domain)
kadonotakashi 0:8fdf9a60065b 313 {
kadonotakashi 0:8fdf9a60065b 314 trickle_inconsistent_heard(&domain->trickle, &domain->control_trickle_params);
kadonotakashi 0:8fdf9a60065b 315 mpl_schedule_timer();
kadonotakashi 0:8fdf9a60065b 316 }
kadonotakashi 0:8fdf9a60065b 317
kadonotakashi 0:8fdf9a60065b 318 static mpl_seed_t *mpl_seed_lookup(const mpl_domain_t *domain, uint8_t id_len, const uint8_t *seed_id)
kadonotakashi 0:8fdf9a60065b 319 {
kadonotakashi 0:8fdf9a60065b 320 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 321 if (seed->id_len == id_len && memcmp(seed->id, seed_id, id_len) == 0) {
kadonotakashi 0:8fdf9a60065b 322 return seed;
kadonotakashi 0:8fdf9a60065b 323 }
kadonotakashi 0:8fdf9a60065b 324 }
kadonotakashi 0:8fdf9a60065b 325
kadonotakashi 0:8fdf9a60065b 326 return NULL;
kadonotakashi 0:8fdf9a60065b 327 }
kadonotakashi 0:8fdf9a60065b 328
kadonotakashi 0:8fdf9a60065b 329 static mpl_seed_t *mpl_seed_create(mpl_domain_t *domain, uint8_t id_len, const uint8_t *seed_id, uint8_t sequence)
kadonotakashi 0:8fdf9a60065b 330 {
kadonotakashi 0:8fdf9a60065b 331 mpl_seed_t *seed = ns_dyn_mem_alloc(sizeof(mpl_seed_t) + id_len);
kadonotakashi 0:8fdf9a60065b 332 if (!seed) {
kadonotakashi 0:8fdf9a60065b 333 return NULL;
kadonotakashi 0:8fdf9a60065b 334 }
kadonotakashi 0:8fdf9a60065b 335
kadonotakashi 0:8fdf9a60065b 336 seed->min_sequence = sequence;
kadonotakashi 0:8fdf9a60065b 337 seed->lifetime = domain->seed_set_entry_lifetime;
kadonotakashi 0:8fdf9a60065b 338 seed->id_len = id_len;
kadonotakashi 0:8fdf9a60065b 339 seed->colour = domain->colour;
kadonotakashi 0:8fdf9a60065b 340 ns_list_init(&seed->messages);
kadonotakashi 0:8fdf9a60065b 341 memcpy(seed->id, seed_id, id_len);
kadonotakashi 0:8fdf9a60065b 342 ns_list_add_to_end(&domain->seeds, seed);
kadonotakashi 0:8fdf9a60065b 343 return seed;
kadonotakashi 0:8fdf9a60065b 344 }
kadonotakashi 0:8fdf9a60065b 345
kadonotakashi 0:8fdf9a60065b 346 static void mpl_seed_delete(mpl_domain_t *domain, mpl_seed_t *seed)
kadonotakashi 0:8fdf9a60065b 347 {
kadonotakashi 0:8fdf9a60065b 348 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 349 mpl_buffer_delete(seed, message);
kadonotakashi 0:8fdf9a60065b 350 }
kadonotakashi 0:8fdf9a60065b 351 ns_list_remove(&domain->seeds, seed);
kadonotakashi 0:8fdf9a60065b 352 ns_dyn_mem_free(seed);
kadonotakashi 0:8fdf9a60065b 353 }
kadonotakashi 0:8fdf9a60065b 354
kadonotakashi 0:8fdf9a60065b 355 static void mpl_seed_advance_min_sequence(mpl_seed_t *seed, uint8_t min_sequence)
kadonotakashi 0:8fdf9a60065b 356 {
kadonotakashi 0:8fdf9a60065b 357 seed->min_sequence = min_sequence;
kadonotakashi 0:8fdf9a60065b 358 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 359 if (common_serial_number_greater_8(min_sequence, mpl_buffer_sequence(message))) {
kadonotakashi 0:8fdf9a60065b 360 mpl_buffer_delete(seed, message);
kadonotakashi 0:8fdf9a60065b 361 }
kadonotakashi 0:8fdf9a60065b 362 }
kadonotakashi 0:8fdf9a60065b 363 }
kadonotakashi 0:8fdf9a60065b 364
kadonotakashi 0:8fdf9a60065b 365 static mpl_buffered_message_t *mpl_buffer_lookup(mpl_seed_t *seed, uint8_t sequence)
kadonotakashi 0:8fdf9a60065b 366 {
kadonotakashi 0:8fdf9a60065b 367 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 368 if (mpl_buffer_sequence(message) == sequence) {
kadonotakashi 0:8fdf9a60065b 369 return message;
kadonotakashi 0:8fdf9a60065b 370 }
kadonotakashi 0:8fdf9a60065b 371 }
kadonotakashi 0:8fdf9a60065b 372 return NULL;
kadonotakashi 0:8fdf9a60065b 373 }
kadonotakashi 0:8fdf9a60065b 374
kadonotakashi 0:8fdf9a60065b 375 static void mpl_free_space(void)
kadonotakashi 0:8fdf9a60065b 376 {
kadonotakashi 0:8fdf9a60065b 377 mpl_seed_t *oldest_seed = NULL;
kadonotakashi 0:8fdf9a60065b 378 mpl_buffered_message_t *oldest_message = NULL;
kadonotakashi 0:8fdf9a60065b 379
kadonotakashi 0:8fdf9a60065b 380 /* We'll free one message - earliest sequence number from one seed */
kadonotakashi 0:8fdf9a60065b 381 /* Choose which seed by looking at the timestamp - oldest one first */
kadonotakashi 0:8fdf9a60065b 382 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 383 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 384 mpl_buffered_message_t *message = ns_list_get_first(&seed->messages);
kadonotakashi 0:8fdf9a60065b 385 if (!message) {
kadonotakashi 0:8fdf9a60065b 386 continue;
kadonotakashi 0:8fdf9a60065b 387 }
kadonotakashi 0:8fdf9a60065b 388 if (!oldest_message ||
kadonotakashi 0:8fdf9a60065b 389 protocol_core_monotonic_time - message->timestamp > protocol_core_monotonic_time - oldest_message->timestamp) {
kadonotakashi 0:8fdf9a60065b 390 oldest_message = message;
kadonotakashi 0:8fdf9a60065b 391 oldest_seed = seed;
kadonotakashi 0:8fdf9a60065b 392 }
kadonotakashi 0:8fdf9a60065b 393 }
kadonotakashi 0:8fdf9a60065b 394 }
kadonotakashi 0:8fdf9a60065b 395
kadonotakashi 0:8fdf9a60065b 396 if (!oldest_message) {
kadonotakashi 0:8fdf9a60065b 397 return;
kadonotakashi 0:8fdf9a60065b 398 }
kadonotakashi 0:8fdf9a60065b 399
kadonotakashi 0:8fdf9a60065b 400 oldest_seed->min_sequence = mpl_buffer_sequence(oldest_message) + 1;
kadonotakashi 0:8fdf9a60065b 401 mpl_buffer_delete(oldest_seed, oldest_message);
kadonotakashi 0:8fdf9a60065b 402 }
kadonotakashi 0:8fdf9a60065b 403
kadonotakashi 0:8fdf9a60065b 404
kadonotakashi 0:8fdf9a60065b 405 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)
kadonotakashi 0:8fdf9a60065b 406 {
kadonotakashi 0:8fdf9a60065b 407 /* IP layer ensures buffer length == IP length */
kadonotakashi 0:8fdf9a60065b 408 uint16_t ip_len = buffer_data_length(buf);
kadonotakashi 0:8fdf9a60065b 409
kadonotakashi 0:8fdf9a60065b 410 while (mpl_total_buffered + ip_len > MAX_BUFFERED_MESSAGES_SIZE) {
kadonotakashi 0:8fdf9a60065b 411 mpl_free_space();
kadonotakashi 0:8fdf9a60065b 412 }
kadonotakashi 0:8fdf9a60065b 413
kadonotakashi 0:8fdf9a60065b 414 /* As we came in, message sequence was >= min_sequence, but mpl_free_space
kadonotakashi 0:8fdf9a60065b 415 * could end up pushing min_sequence forward. We must take care and
kadonotakashi 0:8fdf9a60065b 416 * re-check min_sequence.
kadonotakashi 0:8fdf9a60065b 417 *
kadonotakashi 0:8fdf9a60065b 418 * For example, let's say min_sequence=1, we're holding 1,3,5, and we receive 2.
kadonotakashi 0:8fdf9a60065b 419 * a) If mpl_free_space doesn't touch this seed, we're fine.
kadonotakashi 0:8fdf9a60065b 420 * b) If it frees 1, it will advance min_sequence to 2, and we're fine.
kadonotakashi 0:8fdf9a60065b 421 * c) If it frees 1 and 3, it will advance min_sequence to 4, and we cannot
kadonotakashi 0:8fdf9a60065b 422 * accept this message. (If we forced min_sequence to 2, we'd end up processing
kadonotakashi 0:8fdf9a60065b 423 * message 3 again).
kadonotakashi 0:8fdf9a60065b 424 */
kadonotakashi 0:8fdf9a60065b 425 if (common_serial_number_greater_8(seed->min_sequence, sequence)) {
kadonotakashi 0:8fdf9a60065b 426 tr_debug("Can no longer accept %"PRIu8" < %"PRIu8, sequence, seed->min_sequence);
kadonotakashi 0:8fdf9a60065b 427 return NULL;
kadonotakashi 0:8fdf9a60065b 428 }
kadonotakashi 0:8fdf9a60065b 429
kadonotakashi 0:8fdf9a60065b 430 mpl_buffered_message_t *message = ns_dyn_mem_alloc(sizeof(mpl_buffered_message_t) + ip_len);
kadonotakashi 0:8fdf9a60065b 431 if (!message) {
kadonotakashi 0:8fdf9a60065b 432 return NULL;
kadonotakashi 0:8fdf9a60065b 433 }
kadonotakashi 0:8fdf9a60065b 434 memcpy(message->message, buffer_data_pointer(buf), ip_len);
kadonotakashi 0:8fdf9a60065b 435 message->message[IPV6_HDROFF_HOP_LIMIT] = hop_limit;
kadonotakashi 0:8fdf9a60065b 436 message->mpl_opt_data_offset = buf->mpl_option_data_offset;
kadonotakashi 0:8fdf9a60065b 437 message->colour = seed->colour;
kadonotakashi 0:8fdf9a60065b 438 message->timestamp = protocol_core_monotonic_time;
kadonotakashi 0:8fdf9a60065b 439 /* Make sure trickle structure is initialised */
kadonotakashi 0:8fdf9a60065b 440 trickle_start(&message->trickle, &domain->data_trickle_params);
kadonotakashi 0:8fdf9a60065b 441 if (domain->proactive_forwarding) {
kadonotakashi 0:8fdf9a60065b 442 mpl_schedule_timer();
kadonotakashi 0:8fdf9a60065b 443 } else {
kadonotakashi 0:8fdf9a60065b 444 /* Then stop it if not proactive */
kadonotakashi 0:8fdf9a60065b 445 trickle_stop(&message->trickle);
kadonotakashi 0:8fdf9a60065b 446 }
kadonotakashi 0:8fdf9a60065b 447
kadonotakashi 0:8fdf9a60065b 448 /* Messages held ordered - eg for benefit of mpl_seed_bm_len() */
kadonotakashi 0:8fdf9a60065b 449 bool inserted = false;
kadonotakashi 0:8fdf9a60065b 450 ns_list_foreach_reverse(mpl_buffered_message_t, m, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 451 if (common_serial_number_greater_8(sequence, mpl_buffer_sequence(m))) {
kadonotakashi 0:8fdf9a60065b 452 ns_list_add_after(&seed->messages, m, message);
kadonotakashi 0:8fdf9a60065b 453 inserted = true;
kadonotakashi 0:8fdf9a60065b 454 break;
kadonotakashi 0:8fdf9a60065b 455 }
kadonotakashi 0:8fdf9a60065b 456 }
kadonotakashi 0:8fdf9a60065b 457 if (!inserted) {
kadonotakashi 0:8fdf9a60065b 458 ns_list_add_to_start(&seed->messages, message);
kadonotakashi 0:8fdf9a60065b 459 }
kadonotakashi 0:8fdf9a60065b 460 mpl_total_buffered += ip_len;
kadonotakashi 0:8fdf9a60065b 461
kadonotakashi 0:8fdf9a60065b 462 /* Does MPL spec intend this distinction between start and reset? */
kadonotakashi 0:8fdf9a60065b 463 mpl_control_reset_or_start(domain);
kadonotakashi 0:8fdf9a60065b 464
kadonotakashi 0:8fdf9a60065b 465 return message;
kadonotakashi 0:8fdf9a60065b 466 }
kadonotakashi 0:8fdf9a60065b 467
kadonotakashi 0:8fdf9a60065b 468 static void mpl_buffer_delete(mpl_seed_t *seed, mpl_buffered_message_t *message)
kadonotakashi 0:8fdf9a60065b 469 {
kadonotakashi 0:8fdf9a60065b 470 mpl_total_buffered -= mpl_buffer_size(message);
kadonotakashi 0:8fdf9a60065b 471 ns_list_remove(&seed->messages, message);
kadonotakashi 0:8fdf9a60065b 472 ns_dyn_mem_free(message);
kadonotakashi 0:8fdf9a60065b 473 }
kadonotakashi 0:8fdf9a60065b 474
kadonotakashi 0:8fdf9a60065b 475 static void mpl_buffer_transmit(mpl_domain_t *domain, mpl_buffered_message_t *message, bool newest)
kadonotakashi 0:8fdf9a60065b 476 {
kadonotakashi 0:8fdf9a60065b 477 uint16_t ip_len = mpl_buffer_size(message);
kadonotakashi 0:8fdf9a60065b 478 buffer_t *buf = buffer_get(ip_len);
kadonotakashi 0:8fdf9a60065b 479 if (!buf) {
kadonotakashi 0:8fdf9a60065b 480 return;
kadonotakashi 0:8fdf9a60065b 481 }
kadonotakashi 0:8fdf9a60065b 482
kadonotakashi 0:8fdf9a60065b 483 buffer_data_add(buf, message->message, ip_len);
kadonotakashi 0:8fdf9a60065b 484
kadonotakashi 0:8fdf9a60065b 485 /* Modify the M flag [Thread says it must be clear] */
kadonotakashi 0:8fdf9a60065b 486 uint8_t *flag = buffer_data_pointer(buf) + message->mpl_opt_data_offset;
kadonotakashi 0:8fdf9a60065b 487 if (newest && !thread_info(domain->interface)) {
kadonotakashi 0:8fdf9a60065b 488 *flag |= MPL_OPT_M;
kadonotakashi 0:8fdf9a60065b 489 } else {
kadonotakashi 0:8fdf9a60065b 490 *flag &= ~MPL_OPT_M;
kadonotakashi 0:8fdf9a60065b 491 }
kadonotakashi 0:8fdf9a60065b 492
kadonotakashi 0:8fdf9a60065b 493 // Make sure ip_routed_up is set, even on locally-seeded packets, to
kadonotakashi 0:8fdf9a60065b 494 // distinguishes the "forwarded" copies from the original seed.
kadonotakashi 0:8fdf9a60065b 495 // Used to suppress extra copies to sleepy children.
kadonotakashi 0:8fdf9a60065b 496 buf->ip_routed_up = true;
kadonotakashi 0:8fdf9a60065b 497 buf->dst_sa.addr_type = ADDR_IPV6;
kadonotakashi 0:8fdf9a60065b 498 buf->src_sa.addr_type = ADDR_IPV6;
kadonotakashi 0:8fdf9a60065b 499 memcpy(buf->dst_sa.address, message->message + IPV6_HDROFF_DST_ADDR, 16);
kadonotakashi 0:8fdf9a60065b 500 memcpy(buf->src_sa.address, message->message + IPV6_HDROFF_SRC_ADDR, 16);
kadonotakashi 0:8fdf9a60065b 501
kadonotakashi 0:8fdf9a60065b 502 ipv6_transmit_multicast_on_interface(buf, domain->interface);
kadonotakashi 0:8fdf9a60065b 503 }
kadonotakashi 0:8fdf9a60065b 504
kadonotakashi 0:8fdf9a60065b 505 static void mpl_buffer_inconsistent(const mpl_domain_t *domain, mpl_buffered_message_t *message)
kadonotakashi 0:8fdf9a60065b 506 {
kadonotakashi 0:8fdf9a60065b 507 trickle_inconsistent_heard(&message->trickle, &domain->data_trickle_params);
kadonotakashi 0:8fdf9a60065b 508 mpl_schedule_timer();
kadonotakashi 0:8fdf9a60065b 509 }
kadonotakashi 0:8fdf9a60065b 510
kadonotakashi 0:8fdf9a60065b 511 static uint8_t mpl_seed_bm_len(const mpl_seed_t *seed)
kadonotakashi 0:8fdf9a60065b 512 {
kadonotakashi 0:8fdf9a60065b 513 mpl_buffered_message_t *last = ns_list_get_last(&seed->messages);
kadonotakashi 0:8fdf9a60065b 514 if (last) {
kadonotakashi 0:8fdf9a60065b 515 return ((uint8_t) (mpl_buffer_sequence(last) - seed->min_sequence)) / 8 + 1;
kadonotakashi 0:8fdf9a60065b 516 } else {
kadonotakashi 0:8fdf9a60065b 517 return 0;
kadonotakashi 0:8fdf9a60065b 518 }
kadonotakashi 0:8fdf9a60065b 519 }
kadonotakashi 0:8fdf9a60065b 520
kadonotakashi 0:8fdf9a60065b 521 /* Attempt to optimise by saying ID is source IPv6 */
kadonotakashi 0:8fdf9a60065b 522 static uint16_t mpl_seed_info_size(const mpl_seed_t *seed, const uint8_t *src)
kadonotakashi 0:8fdf9a60065b 523 {
kadonotakashi 0:8fdf9a60065b 524 uint8_t id_len = seed->id_len;
kadonotakashi 0:8fdf9a60065b 525 if (id_len == 16 && src && addr_ipv6_equal(src, seed->id)) {
kadonotakashi 0:8fdf9a60065b 526 id_len = 0;
kadonotakashi 0:8fdf9a60065b 527 }
kadonotakashi 0:8fdf9a60065b 528 return 2 + id_len + mpl_seed_bm_len(seed);
kadonotakashi 0:8fdf9a60065b 529 }
kadonotakashi 0:8fdf9a60065b 530
kadonotakashi 0:8fdf9a60065b 531 static uint8_t *mpl_write_seed_info(uint8_t *ptr, const mpl_seed_t *seed, const uint8_t *src)
kadonotakashi 0:8fdf9a60065b 532 {
kadonotakashi 0:8fdf9a60065b 533 uint8_t bm_len = mpl_seed_bm_len(seed);
kadonotakashi 0:8fdf9a60065b 534 ptr[0] = seed->min_sequence;
kadonotakashi 0:8fdf9a60065b 535 ptr[1] = bm_len << 2;
kadonotakashi 0:8fdf9a60065b 536 uint8_t id_len = seed->id_len;
kadonotakashi 0:8fdf9a60065b 537 if (id_len == 16 && src && addr_ipv6_equal(src, seed->id)) {
kadonotakashi 0:8fdf9a60065b 538 id_len = 0;
kadonotakashi 0:8fdf9a60065b 539 }
kadonotakashi 0:8fdf9a60065b 540 switch (id_len) {
kadonotakashi 0:8fdf9a60065b 541 case 0: ptr[1] |= MPL_SEED_IPV6_SRC; break;
kadonotakashi 0:8fdf9a60065b 542 case 2: ptr[1] |= MPL_SEED_16_BIT; break;
kadonotakashi 0:8fdf9a60065b 543 case 8: ptr[1] |= MPL_SEED_64_BIT; break;
kadonotakashi 0:8fdf9a60065b 544 case 16: ptr[1] |= MPL_SEED_128_BIT; break;
kadonotakashi 0:8fdf9a60065b 545 default: return ptr;
kadonotakashi 0:8fdf9a60065b 546 }
kadonotakashi 0:8fdf9a60065b 547 ptr += 2;
kadonotakashi 0:8fdf9a60065b 548 memcpy(ptr, seed->id, id_len);
kadonotakashi 0:8fdf9a60065b 549 ptr += id_len;
kadonotakashi 0:8fdf9a60065b 550 memset(ptr, 0, bm_len);
kadonotakashi 0:8fdf9a60065b 551 ns_list_foreach(mpl_buffered_message_t, buffer, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 552 uint8_t i = mpl_buffer_sequence(buffer) - seed->min_sequence;
kadonotakashi 0:8fdf9a60065b 553 bit_set(ptr, i);
kadonotakashi 0:8fdf9a60065b 554 }
kadonotakashi 0:8fdf9a60065b 555 ptr += bm_len;
kadonotakashi 0:8fdf9a60065b 556 return ptr;
kadonotakashi 0:8fdf9a60065b 557 }
kadonotakashi 0:8fdf9a60065b 558
kadonotakashi 0:8fdf9a60065b 559 /* Does MPL spec really intend this distinction between start and reset? */
kadonotakashi 0:8fdf9a60065b 560 /* (Reset sets interval to Imin, Start puts it somewhere random between Imin and Imax) */
kadonotakashi 0:8fdf9a60065b 561 static void mpl_control_reset_or_start(mpl_domain_t *domain)
kadonotakashi 0:8fdf9a60065b 562 {
kadonotakashi 0:8fdf9a60065b 563 if (trickle_running(&domain->trickle, &domain->control_trickle_params)) {
kadonotakashi 0:8fdf9a60065b 564 trickle_inconsistent_heard(&domain->trickle, &domain->control_trickle_params);
kadonotakashi 0:8fdf9a60065b 565 } else {
kadonotakashi 0:8fdf9a60065b 566 trickle_start(&domain->trickle, &domain->control_trickle_params);
kadonotakashi 0:8fdf9a60065b 567 }
kadonotakashi 0:8fdf9a60065b 568 mpl_schedule_timer();
kadonotakashi 0:8fdf9a60065b 569 }
kadonotakashi 0:8fdf9a60065b 570
kadonotakashi 0:8fdf9a60065b 571 static uint8_t mpl_seed_id_len(uint8_t seed_id_type)
kadonotakashi 0:8fdf9a60065b 572 {
kadonotakashi 0:8fdf9a60065b 573 static const uint8_t len[] = {
kadonotakashi 0:8fdf9a60065b 574 [MPL_SEED_IPV6_SRC] = 0,
kadonotakashi 0:8fdf9a60065b 575 [MPL_SEED_16_BIT] = 2,
kadonotakashi 0:8fdf9a60065b 576 [MPL_SEED_64_BIT] = 8,
kadonotakashi 0:8fdf9a60065b 577 [MPL_SEED_128_BIT] = 16
kadonotakashi 0:8fdf9a60065b 578 };
kadonotakashi 0:8fdf9a60065b 579 return len[seed_id_type];
kadonotakashi 0:8fdf9a60065b 580 }
kadonotakashi 0:8fdf9a60065b 581
kadonotakashi 0:8fdf9a60065b 582 static uint8_t mpl_seed_id_type(uint8_t seed_id_len)
kadonotakashi 0:8fdf9a60065b 583 {
kadonotakashi 0:8fdf9a60065b 584 switch (seed_id_len) {
kadonotakashi 0:8fdf9a60065b 585 default: return MPL_SEED_IPV6_SRC;
kadonotakashi 0:8fdf9a60065b 586 case 2: return MPL_SEED_16_BIT;
kadonotakashi 0:8fdf9a60065b 587 case 8: return MPL_SEED_64_BIT;
kadonotakashi 0:8fdf9a60065b 588 case 16: return MPL_SEED_128_BIT;
kadonotakashi 0:8fdf9a60065b 589 }
kadonotakashi 0:8fdf9a60065b 590 }
kadonotakashi 0:8fdf9a60065b 591
kadonotakashi 0:8fdf9a60065b 592 /*
kadonotakashi 0:8fdf9a60065b 593 * 0 1 2 3
kadonotakashi 0:8fdf9a60065b 594 * 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
kadonotakashi 0:8fdf9a60065b 595 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 596 * | min-seqno | bm-len | S | seed-id (0/2/8/16 octets) |
kadonotakashi 0:8fdf9a60065b 597 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 598 * | |
kadonotakashi 0:8fdf9a60065b 599 * . buffered-mpl-messages (variable length) .
kadonotakashi 0:8fdf9a60065b 600 * . .
kadonotakashi 0:8fdf9a60065b 601 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 602 */
kadonotakashi 0:8fdf9a60065b 603 static void mpl_send_control(mpl_domain_t *domain)
kadonotakashi 0:8fdf9a60065b 604 {
kadonotakashi 0:8fdf9a60065b 605 uint16_t size = 0;
kadonotakashi 0:8fdf9a60065b 606 const uint8_t *src = NULL;
kadonotakashi 0:8fdf9a60065b 607
kadonotakashi 0:8fdf9a60065b 608 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 609 /* If not chosen yet, pick source to match a seed id, to save 16 bytes */
kadonotakashi 0:8fdf9a60065b 610 if (!src && seed->id_len == 16 && addr_is_assigned_to_interface(domain->interface, seed->id)) {
kadonotakashi 0:8fdf9a60065b 611 src = seed->id;
kadonotakashi 0:8fdf9a60065b 612 }
kadonotakashi 0:8fdf9a60065b 613 size += mpl_seed_info_size(seed, src);
kadonotakashi 0:8fdf9a60065b 614 }
kadonotakashi 0:8fdf9a60065b 615 buffer_t *buf = buffer_get(size);
kadonotakashi 0:8fdf9a60065b 616 if (!buf) {
kadonotakashi 0:8fdf9a60065b 617 return;
kadonotakashi 0:8fdf9a60065b 618 }
kadonotakashi 0:8fdf9a60065b 619 uint8_t *ptr = buffer_data_pointer(buf);
kadonotakashi 0:8fdf9a60065b 620 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 621 ptr = mpl_write_seed_info(ptr, seed, src);
kadonotakashi 0:8fdf9a60065b 622 }
kadonotakashi 0:8fdf9a60065b 623 buffer_data_end_set(buf, ptr);
kadonotakashi 0:8fdf9a60065b 624 buf->options.type = ICMPV6_TYPE_INFO_MPL_CONTROL;
kadonotakashi 0:8fdf9a60065b 625 buf->options.code = 0;
kadonotakashi 0:8fdf9a60065b 626 buf->options.hop_limit = 255;
kadonotakashi 0:8fdf9a60065b 627 memcpy(buf->dst_sa.address, domain->address, 16);
kadonotakashi 0:8fdf9a60065b 628 buf->dst_sa.address[1] = (buf->dst_sa.address[1] & 0xf0) | IPV6_SCOPE_LINK_LOCAL;
kadonotakashi 0:8fdf9a60065b 629 buf->dst_sa.addr_type = ADDR_IPV6;
kadonotakashi 0:8fdf9a60065b 630 if (src) {
kadonotakashi 0:8fdf9a60065b 631 buf->src_sa.addr_type = ADDR_IPV6;
kadonotakashi 0:8fdf9a60065b 632 memcpy(buf->src_sa.address, src, 16);
kadonotakashi 0:8fdf9a60065b 633 }
kadonotakashi 0:8fdf9a60065b 634 buf->info = (buffer_info_t)(B_FROM_ICMP | B_TO_ICMP | B_DIR_DOWN);
kadonotakashi 0:8fdf9a60065b 635 buf->interface = domain->interface;
kadonotakashi 0:8fdf9a60065b 636 protocol_push(buf);
kadonotakashi 0:8fdf9a60065b 637 }
kadonotakashi 0:8fdf9a60065b 638
kadonotakashi 0:8fdf9a60065b 639 /*
kadonotakashi 0:8fdf9a60065b 640 * There is an edge case in control handling when the hop limit runs out. This
kadonotakashi 0:8fdf9a60065b 641 * is handled as follows:
kadonotakashi 0:8fdf9a60065b 642 *
kadonotakashi 0:8fdf9a60065b 643 * Hop Limit 2 Hop Limit 1 [Won't Forward]
kadonotakashi 0:8fdf9a60065b 644 * Seed ---------------> Forwarder -------------> Final Node -------X------> Neighbour Node
kadonotakashi 0:8fdf9a60065b 645 * In Message Set In Message Set MinSequence advanced Not In Message Set
kadonotakashi 0:8fdf9a60065b 646 *
kadonotakashi 0:8fdf9a60065b 647 * The Final Node does NOT add the message to its buffered message set, and it
kadonotakashi 0:8fdf9a60065b 648 * advances MinSequence so that doesn't have to report about the message either
kadonotakashi 0:8fdf9a60065b 649 * positively or negatively in control messages.
kadonotakashi 0:8fdf9a60065b 650 *
kadonotakashi 0:8fdf9a60065b 651 * If it reported "present" in control messages, the Neighbour Node would see a "missing"
kadonotakashi 0:8fdf9a60065b 652 * message and reset its control timer. If it reported "absent", the Forwarder would
kadonotakashi 0:8fdf9a60065b 653 * notice the inconsistency and resend. So we sidestep the issue by advancing MinSequence.
kadonotakashi 0:8fdf9a60065b 654 * This also saves RAM - we'd never retransmit the message anyway, so why buffer it?
kadonotakashi 0:8fdf9a60065b 655 *
kadonotakashi 0:8fdf9a60065b 656 * This means we drop out-of-order packets at the edge of a hop limit boundary,
kadonotakashi 0:8fdf9a60065b 657 * but this isn't a huge deal.
kadonotakashi 0:8fdf9a60065b 658 */
kadonotakashi 0:8fdf9a60065b 659 buffer_t *mpl_control_handler(buffer_t *buf, protocol_interface_info_entry_t *cur)
kadonotakashi 0:8fdf9a60065b 660 {
kadonotakashi 0:8fdf9a60065b 661 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) {
kadonotakashi 0:8fdf9a60065b 662 tr_warn("Invalid control");
kadonotakashi 0:8fdf9a60065b 663 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 664 }
kadonotakashi 0:8fdf9a60065b 665
kadonotakashi 0:8fdf9a60065b 666 /* Um, how do we distinguish between multiple domains with different scop?
kadonotakashi 0:8fdf9a60065b 667 * Control messages just have the domain address with scop 2. Currently
kadonotakashi 0:8fdf9a60065b 668 * deal with that by not letting users join two domains differing only in
kadonotakashi 0:8fdf9a60065b 669 * scop.
kadonotakashi 0:8fdf9a60065b 670 */
kadonotakashi 0:8fdf9a60065b 671
kadonotakashi 0:8fdf9a60065b 672 mpl_domain_t *domain = mpl_domain_lookup_ignoring_scop(cur, buf->dst_sa.address);
kadonotakashi 0:8fdf9a60065b 673 if (!domain) {
kadonotakashi 0:8fdf9a60065b 674 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 675 }
kadonotakashi 0:8fdf9a60065b 676
kadonotakashi 0:8fdf9a60065b 677 bool they_have_new_data = false;
kadonotakashi 0:8fdf9a60065b 678 bool we_have_new_data = false;
kadonotakashi 0:8fdf9a60065b 679 const uint8_t *ptr = buffer_data_pointer(buf);
kadonotakashi 0:8fdf9a60065b 680 const uint8_t *end = buffer_data_end(buf);
kadonotakashi 0:8fdf9a60065b 681
kadonotakashi 0:8fdf9a60065b 682 // All objects will currently have the same colour. The scan
kadonotakashi 0:8fdf9a60065b 683 // of the control message will flip the colour of every mentioned seed
kadonotakashi 0:8fdf9a60065b 684 // and data message. Then the omission of anything we have will be detected
kadonotakashi 0:8fdf9a60065b 685 // by its colour not being flipped.
kadonotakashi 0:8fdf9a60065b 686 // This is equivalent to having a "mentioned" flag, except we don't have
kadonotakashi 0:8fdf9a60065b 687 // to have a separate "reset" loop.
kadonotakashi 0:8fdf9a60065b 688 domain->colour = !domain->colour;
kadonotakashi 0:8fdf9a60065b 689 bool new_colour = domain->colour;
kadonotakashi 0:8fdf9a60065b 690
kadonotakashi 0:8fdf9a60065b 691 while (ptr < end) {
kadonotakashi 0:8fdf9a60065b 692 if (end - ptr < 2) {
kadonotakashi 0:8fdf9a60065b 693 tr_err("MPL control error");
kadonotakashi 0:8fdf9a60065b 694 break;
kadonotakashi 0:8fdf9a60065b 695 }
kadonotakashi 0:8fdf9a60065b 696 uint8_t min_seqno = ptr[0];
kadonotakashi 0:8fdf9a60065b 697 uint8_t bm_len = ptr[1] >> 2;
kadonotakashi 0:8fdf9a60065b 698 uint8_t seed_id_type = ptr[1] & 3;
kadonotakashi 0:8fdf9a60065b 699 uint8_t seed_id_len = mpl_seed_id_len(seed_id_type);
kadonotakashi 0:8fdf9a60065b 700 ptr += 2;
kadonotakashi 0:8fdf9a60065b 701 /* Sequence number is 8-bit, so bitmask should never be bigger than 32 bytes */
kadonotakashi 0:8fdf9a60065b 702 if (bm_len > 32 || end - ptr < seed_id_len + bm_len) {
kadonotakashi 0:8fdf9a60065b 703 tr_err("MPL control error");
kadonotakashi 0:8fdf9a60065b 704 break;
kadonotakashi 0:8fdf9a60065b 705 }
kadonotakashi 0:8fdf9a60065b 706 const uint8_t *seed_id;
kadonotakashi 0:8fdf9a60065b 707 if (seed_id_type == MPL_SEED_IPV6_SRC) {
kadonotakashi 0:8fdf9a60065b 708 seed_id = buf->src_sa.address;
kadonotakashi 0:8fdf9a60065b 709 seed_id_len = 16;
kadonotakashi 0:8fdf9a60065b 710 /* Thread spec says, or at least implies, that ML16/RLOC address is
kadonotakashi 0:8fdf9a60065b 711 * matched against corresponding 16-bit seed id (although
kadonotakashi 0:8fdf9a60065b 712 * Thread doesn't use control messages...) */
kadonotakashi 0:8fdf9a60065b 713 if (thread_addr_is_mesh_local_16(seed_id, cur)) {
kadonotakashi 0:8fdf9a60065b 714 seed_id += 14;
kadonotakashi 0:8fdf9a60065b 715 seed_id_len = 2;
kadonotakashi 0:8fdf9a60065b 716 }
kadonotakashi 0:8fdf9a60065b 717 } else {
kadonotakashi 0:8fdf9a60065b 718 seed_id = ptr;
kadonotakashi 0:8fdf9a60065b 719 ptr += seed_id_len;
kadonotakashi 0:8fdf9a60065b 720 }
kadonotakashi 0:8fdf9a60065b 721 mpl_seed_t *seed = mpl_seed_lookup(domain, seed_id_len, seed_id);
kadonotakashi 0:8fdf9a60065b 722 if (!seed) {
kadonotakashi 0:8fdf9a60065b 723 they_have_new_data = true;
kadonotakashi 0:8fdf9a60065b 724 ptr += bm_len;
kadonotakashi 0:8fdf9a60065b 725 continue;
kadonotakashi 0:8fdf9a60065b 726 }
kadonotakashi 0:8fdf9a60065b 727
kadonotakashi 0:8fdf9a60065b 728 seed->colour = new_colour;
kadonotakashi 0:8fdf9a60065b 729 /* They are assumed to not be interested in messages lower than their min_seqno */
kadonotakashi 0:8fdf9a60065b 730 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 731 if (common_serial_number_greater_8(min_seqno, mpl_buffer_sequence(message))) {
kadonotakashi 0:8fdf9a60065b 732 message->colour = new_colour;
kadonotakashi 0:8fdf9a60065b 733 }
kadonotakashi 0:8fdf9a60065b 734 }
kadonotakashi 0:8fdf9a60065b 735 for (uint8_t i = 0; i / 8 < bm_len; i++) {
kadonotakashi 0:8fdf9a60065b 736 if (bit_test(ptr, i)) {
kadonotakashi 0:8fdf9a60065b 737 mpl_buffered_message_t *message = mpl_buffer_lookup(seed, min_seqno + i);
kadonotakashi 0:8fdf9a60065b 738
kadonotakashi 0:8fdf9a60065b 739 if (!message && common_serial_number_greater_8(min_seqno + i, seed->min_sequence)) {
kadonotakashi 0:8fdf9a60065b 740 they_have_new_data = true;
kadonotakashi 0:8fdf9a60065b 741 } else if (message) {
kadonotakashi 0:8fdf9a60065b 742 message->colour = new_colour;
kadonotakashi 0:8fdf9a60065b 743 }
kadonotakashi 0:8fdf9a60065b 744 }
kadonotakashi 0:8fdf9a60065b 745 }
kadonotakashi 0:8fdf9a60065b 746 ptr += bm_len;
kadonotakashi 0:8fdf9a60065b 747 }
kadonotakashi 0:8fdf9a60065b 748
kadonotakashi 0:8fdf9a60065b 749 /* Search for seeds or messages they haven't mentioned */
kadonotakashi 0:8fdf9a60065b 750 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 751 if (seed->colour != new_colour) {
kadonotakashi 0:8fdf9a60065b 752 seed->colour = new_colour;
kadonotakashi 0:8fdf9a60065b 753 we_have_new_data = true;
kadonotakashi 0:8fdf9a60065b 754 }
kadonotakashi 0:8fdf9a60065b 755 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 756 if (message->colour != new_colour) {
kadonotakashi 0:8fdf9a60065b 757 message->colour = new_colour;
kadonotakashi 0:8fdf9a60065b 758 mpl_buffer_inconsistent(domain, message);
kadonotakashi 0:8fdf9a60065b 759 we_have_new_data = true;
kadonotakashi 0:8fdf9a60065b 760 }
kadonotakashi 0:8fdf9a60065b 761 }
kadonotakashi 0:8fdf9a60065b 762 }
kadonotakashi 0:8fdf9a60065b 763
kadonotakashi 0:8fdf9a60065b 764 if (they_have_new_data || we_have_new_data) {
kadonotakashi 0:8fdf9a60065b 765 if (they_have_new_data) {
kadonotakashi 0:8fdf9a60065b 766 tr_info("%s has new MPL data", trace_ipv6(buf->src_sa.address));
kadonotakashi 0:8fdf9a60065b 767 }
kadonotakashi 0:8fdf9a60065b 768 if (we_have_new_data) {
kadonotakashi 0:8fdf9a60065b 769 tr_info("We have new MPL data for %s", trace_ipv6(buf->src_sa.address));
kadonotakashi 0:8fdf9a60065b 770 }
kadonotakashi 0:8fdf9a60065b 771 mpl_domain_inconsistent(domain);
kadonotakashi 0:8fdf9a60065b 772 } else {
kadonotakashi 0:8fdf9a60065b 773 trickle_consistent_heard(&domain->trickle);
kadonotakashi 0:8fdf9a60065b 774 }
kadonotakashi 0:8fdf9a60065b 775
kadonotakashi 0:8fdf9a60065b 776
kadonotakashi 0:8fdf9a60065b 777 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 778 }
kadonotakashi 0:8fdf9a60065b 779
kadonotakashi 0:8fdf9a60065b 780 bool mpl_hbh_len_check(const uint8_t *opt_data, uint8_t opt_data_len)
kadonotakashi 0:8fdf9a60065b 781 {
kadonotakashi 0:8fdf9a60065b 782 if (opt_data_len < 2) {
kadonotakashi 0:8fdf9a60065b 783 return false;
kadonotakashi 0:8fdf9a60065b 784 }
kadonotakashi 0:8fdf9a60065b 785 if (opt_data[0] & MPL_OPT_V) {
kadonotakashi 0:8fdf9a60065b 786 return true; /* No length complaint - we let "process" drop */
kadonotakashi 0:8fdf9a60065b 787 }
kadonotakashi 0:8fdf9a60065b 788
kadonotakashi 0:8fdf9a60065b 789 uint8_t seed_id_type = (opt_data[0] & MPL_OPT_S_MASK) >> MPL_OPT_S_SHIFT;
kadonotakashi 0:8fdf9a60065b 790 /* Note that option is allowed to be longer - spec allows for extension
kadonotakashi 0:8fdf9a60065b 791 * beyond seed-id.
kadonotakashi 0:8fdf9a60065b 792 */
kadonotakashi 0:8fdf9a60065b 793 if (opt_data_len < 2 + mpl_seed_id_len(seed_id_type)) {
kadonotakashi 0:8fdf9a60065b 794 return false;
kadonotakashi 0:8fdf9a60065b 795 }
kadonotakashi 0:8fdf9a60065b 796 return true;
kadonotakashi 0:8fdf9a60065b 797 }
kadonotakashi 0:8fdf9a60065b 798
kadonotakashi 0:8fdf9a60065b 799 /* 0 1 2 3
kadonotakashi 0:8fdf9a60065b 800 * 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
kadonotakashi 0:8fdf9a60065b 801 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 802 * | Option Type | Opt Data Len |
kadonotakashi 0:8fdf9a60065b 803 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 804 * | S |M|V| rsv | sequence | seed-id (optional) |
kadonotakashi 0:8fdf9a60065b 805 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
kadonotakashi 0:8fdf9a60065b 806 */
kadonotakashi 0:8fdf9a60065b 807
kadonotakashi 0:8fdf9a60065b 808 bool mpl_process_hbh(buffer_t *buf, protocol_interface_info_entry_t *cur, uint8_t *opt_data)
kadonotakashi 0:8fdf9a60065b 809 {
kadonotakashi 0:8fdf9a60065b 810 if ((buf->options.ip_extflags & IPEXT_HBH_MPL) || buf->options.ll_security_bypass_rx) {
kadonotakashi 0:8fdf9a60065b 811 tr_warn("Bad MPL");
kadonotakashi 0:8fdf9a60065b 812 return false;
kadonotakashi 0:8fdf9a60065b 813 }
kadonotakashi 0:8fdf9a60065b 814
kadonotakashi 0:8fdf9a60065b 815 /* mpl_hbh_len_check has already returned true, so know length is okay */
kadonotakashi 0:8fdf9a60065b 816
kadonotakashi 0:8fdf9a60065b 817 /* V flag indicates incompatible new version - packets MUST be dropped */
kadonotakashi 0:8fdf9a60065b 818 if (opt_data[0] & MPL_OPT_V) {
kadonotakashi 0:8fdf9a60065b 819 tr_warn("MPL V!");
kadonotakashi 0:8fdf9a60065b 820 return false;
kadonotakashi 0:8fdf9a60065b 821 }
kadonotakashi 0:8fdf9a60065b 822
kadonotakashi 0:8fdf9a60065b 823 mpl_domain_t *domain = mpl_domain_lookup_with_realm_check(cur, buf->dst_sa.address);
kadonotakashi 0:8fdf9a60065b 824 if (!domain) {
kadonotakashi 0:8fdf9a60065b 825 tr_debug("No MPL domain");
kadonotakashi 0:8fdf9a60065b 826 return false;
kadonotakashi 0:8fdf9a60065b 827 }
kadonotakashi 0:8fdf9a60065b 828
kadonotakashi 0:8fdf9a60065b 829 buf->options.ip_extflags |= IPEXT_HBH_MPL;
kadonotakashi 0:8fdf9a60065b 830 buf->mpl_option_data_offset = opt_data - buffer_data_pointer(buf);
kadonotakashi 0:8fdf9a60065b 831
kadonotakashi 0:8fdf9a60065b 832 return true;
kadonotakashi 0:8fdf9a60065b 833 // return mpl_forwarder_process_message(buf, domain, opt_data);
kadonotakashi 0:8fdf9a60065b 834 }
kadonotakashi 0:8fdf9a60065b 835
kadonotakashi 0:8fdf9a60065b 836 /* seeding is true if this is processing an outgoing message */
kadonotakashi 0:8fdf9a60065b 837 bool mpl_forwarder_process_message(buffer_t *buf, mpl_domain_t *domain, bool seeding)
kadonotakashi 0:8fdf9a60065b 838 {
kadonotakashi 0:8fdf9a60065b 839 const uint8_t *opt_data = buffer_data_pointer(buf) + buf->mpl_option_data_offset;
kadonotakashi 0:8fdf9a60065b 840 uint8_t sequence = opt_data[1];
kadonotakashi 0:8fdf9a60065b 841 uint8_t seed_id_type = (opt_data[0] & MPL_OPT_S_MASK) >> MPL_OPT_S_SHIFT;
kadonotakashi 0:8fdf9a60065b 842 const uint8_t *seed_id = opt_data + 2;
kadonotakashi 0:8fdf9a60065b 843 uint8_t seed_id_len = mpl_seed_id_len(seed_id_type);
kadonotakashi 0:8fdf9a60065b 844
kadonotakashi 0:8fdf9a60065b 845 /* Special handling - just ignore the MPL option if receiving loopback copy.
kadonotakashi 0:8fdf9a60065b 846 * (MPL gets to process the outgoing message, and with seeding true - when
kadonotakashi 0:8fdf9a60065b 847 * looping back, we want to accept it without MPL getting in the way).
kadonotakashi 0:8fdf9a60065b 848 */
kadonotakashi 0:8fdf9a60065b 849 if (!seeding && buf->options.multicast_loop) {
kadonotakashi 0:8fdf9a60065b 850 return true;
kadonotakashi 0:8fdf9a60065b 851 }
kadonotakashi 0:8fdf9a60065b 852
kadonotakashi 0:8fdf9a60065b 853 if (!domain) {
kadonotakashi 0:8fdf9a60065b 854 domain = mpl_domain_lookup_with_realm_check(buf->interface, buf->dst_sa.address);
kadonotakashi 0:8fdf9a60065b 855 if (!domain) {
kadonotakashi 0:8fdf9a60065b 856 tr_debug("No domain %s %s", tr_ipv6(domain->address), tr_array(seed_id, seed_id_len));
kadonotakashi 0:8fdf9a60065b 857 return false;
kadonotakashi 0:8fdf9a60065b 858 }
kadonotakashi 0:8fdf9a60065b 859 }
kadonotakashi 0:8fdf9a60065b 860
kadonotakashi 0:8fdf9a60065b 861 if (seed_id_type == MPL_SEED_IPV6_SRC) {
kadonotakashi 0:8fdf9a60065b 862 seed_id = buf->src_sa.address;
kadonotakashi 0:8fdf9a60065b 863 seed_id_len = 16;
kadonotakashi 0:8fdf9a60065b 864 /* Thread spec says, or at least implies, that ML16/RLOC address is
kadonotakashi 0:8fdf9a60065b 865 * matched against corresponding 16-bit seed id */
kadonotakashi 0:8fdf9a60065b 866 if (thread_addr_is_mesh_local_16(seed_id, buf->interface)) {
kadonotakashi 0:8fdf9a60065b 867 seed_id += 14;
kadonotakashi 0:8fdf9a60065b 868 seed_id_len = 2;
kadonotakashi 0:8fdf9a60065b 869 }
kadonotakashi 0:8fdf9a60065b 870 }
kadonotakashi 0:8fdf9a60065b 871
kadonotakashi 0:8fdf9a60065b 872 tr_debug("seed %s seq %"PRIu8, tr_array(seed_id, seed_id_len), sequence);
kadonotakashi 0:8fdf9a60065b 873 mpl_seed_t *seed = mpl_seed_lookup(domain, seed_id_len, seed_id);
kadonotakashi 0:8fdf9a60065b 874 if (!seed) {
kadonotakashi 0:8fdf9a60065b 875 seed = mpl_seed_create(domain, seed_id_len, seed_id, sequence);
kadonotakashi 0:8fdf9a60065b 876 if (!seed) {
kadonotakashi 0:8fdf9a60065b 877 tr_debug("No seed %s %s", tr_ipv6(domain->address), tr_array(seed_id, seed_id_len));
kadonotakashi 0:8fdf9a60065b 878 return false;
kadonotakashi 0:8fdf9a60065b 879 }
kadonotakashi 0:8fdf9a60065b 880 }
kadonotakashi 0:8fdf9a60065b 881
kadonotakashi 0:8fdf9a60065b 882 /* If the M flag is set, we report an inconsistency against any messages with higher sequences */
kadonotakashi 0:8fdf9a60065b 883 if ((opt_data[0] & MPL_OPT_M) && !thread_info(buf->interface)) {
kadonotakashi 0:8fdf9a60065b 884 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 885 if (common_serial_number_greater_8(mpl_buffer_sequence(message), sequence)) {
kadonotakashi 0:8fdf9a60065b 886 mpl_buffer_inconsistent(domain, message);
kadonotakashi 0:8fdf9a60065b 887 }
kadonotakashi 0:8fdf9a60065b 888 }
kadonotakashi 0:8fdf9a60065b 889 }
kadonotakashi 0:8fdf9a60065b 890
kadonotakashi 0:8fdf9a60065b 891 /* Drop old messages (sequence < MinSequence) */
kadonotakashi 0:8fdf9a60065b 892 if (common_serial_number_greater_8(seed->min_sequence, sequence)) {
kadonotakashi 0:8fdf9a60065b 893 tr_debug("Old MPL message %"PRIu8" < %"PRIu8, sequence, seed->min_sequence);
kadonotakashi 0:8fdf9a60065b 894 return false;
kadonotakashi 0:8fdf9a60065b 895 }
kadonotakashi 0:8fdf9a60065b 896
kadonotakashi 0:8fdf9a60065b 897 mpl_buffered_message_t *message = mpl_buffer_lookup(seed, sequence);
kadonotakashi 0:8fdf9a60065b 898 if (message) {
kadonotakashi 0:8fdf9a60065b 899 tr_debug("Repeated MPL message %"PRIu8, sequence);
kadonotakashi 0:8fdf9a60065b 900 trickle_consistent_heard(&message->trickle);
kadonotakashi 0:8fdf9a60065b 901 return false;
kadonotakashi 0:8fdf9a60065b 902 }
kadonotakashi 0:8fdf9a60065b 903
kadonotakashi 0:8fdf9a60065b 904 seed->lifetime = domain->seed_set_entry_lifetime;
kadonotakashi 0:8fdf9a60065b 905
kadonotakashi 0:8fdf9a60065b 906 uint8_t hop_limit = buffer_data_pointer(buf)[IPV6_HDROFF_HOP_LIMIT];
kadonotakashi 0:8fdf9a60065b 907 if (!seeding && hop_limit != 0) {
kadonotakashi 0:8fdf9a60065b 908 hop_limit--;
kadonotakashi 0:8fdf9a60065b 909 }
kadonotakashi 0:8fdf9a60065b 910
kadonotakashi 0:8fdf9a60065b 911 if (domain->data_trickle_params.TimerExpirations == 0 || hop_limit == 0 ||
kadonotakashi 0:8fdf9a60065b 912 (thread_info(domain->interface) && !thread_i_am_router(domain->interface))) {
kadonotakashi 0:8fdf9a60065b 913 /* As a non-forwarder, just accept the packet and advance the
kadonotakashi 0:8fdf9a60065b 914 * min_sequence - means we will drop anything arriving out-of-order, but
kadonotakashi 0:8fdf9a60065b 915 * old implementation always did this in all cases anyway (even if
kadonotakashi 0:8fdf9a60065b 916 * being a forwarder).
kadonotakashi 0:8fdf9a60065b 917 *
kadonotakashi 0:8fdf9a60065b 918 * We also do this if hop limit is 0, so we are not going to forward.
kadonotakashi 0:8fdf9a60065b 919 * This avoids the edge case discussed in the comment above mpl_control_handler.
kadonotakashi 0:8fdf9a60065b 920 *
kadonotakashi 0:8fdf9a60065b 921 * And finally, also treat Thread non-routers like this, to avoid
kadonotakashi 0:8fdf9a60065b 922 * need to dynamically changing TimerExpirations.
kadonotakashi 0:8fdf9a60065b 923 */
kadonotakashi 0:8fdf9a60065b 924 mpl_seed_advance_min_sequence(seed, sequence + 1);
kadonotakashi 0:8fdf9a60065b 925 return true;
kadonotakashi 0:8fdf9a60065b 926 }
kadonotakashi 0:8fdf9a60065b 927
kadonotakashi 0:8fdf9a60065b 928 message = mpl_buffer_create(buf, domain, seed, sequence, hop_limit);
kadonotakashi 0:8fdf9a60065b 929
kadonotakashi 0:8fdf9a60065b 930 return true;
kadonotakashi 0:8fdf9a60065b 931 }
kadonotakashi 0:8fdf9a60065b 932
kadonotakashi 0:8fdf9a60065b 933
kadonotakashi 0:8fdf9a60065b 934 static void mpl_schedule_timer(void)
kadonotakashi 0:8fdf9a60065b 935 {
kadonotakashi 0:8fdf9a60065b 936 if (!mpl_timer_running) {
kadonotakashi 0:8fdf9a60065b 937 mpl_timer_running = true;
kadonotakashi 0:8fdf9a60065b 938 protocol_timer_start(PROTOCOL_TIMER_MULTICAST_TIM, mpl_fast_timer, MPL_TICK_MS);
kadonotakashi 0:8fdf9a60065b 939 }
kadonotakashi 0:8fdf9a60065b 940 }
kadonotakashi 0:8fdf9a60065b 941
kadonotakashi 0:8fdf9a60065b 942 static void mpl_fast_timer(uint16_t ticks)
kadonotakashi 0:8fdf9a60065b 943 {
kadonotakashi 0:8fdf9a60065b 944 bool need_timer = false;
kadonotakashi 0:8fdf9a60065b 945 mpl_timer_running = false;
kadonotakashi 0:8fdf9a60065b 946
kadonotakashi 0:8fdf9a60065b 947 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 948 if (trickle_timer(&domain->trickle, &domain->control_trickle_params, ticks)) {
kadonotakashi 0:8fdf9a60065b 949 mpl_send_control(domain);
kadonotakashi 0:8fdf9a60065b 950 }
kadonotakashi 0:8fdf9a60065b 951 ns_list_foreach(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 952 ns_list_foreach(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 953 if (trickle_timer(&message->trickle, &domain->data_trickle_params, ticks)) {
kadonotakashi 0:8fdf9a60065b 954 mpl_buffer_transmit(domain, message, ns_list_get_next(&seed->messages, message) == NULL);
kadonotakashi 0:8fdf9a60065b 955 }
kadonotakashi 0:8fdf9a60065b 956 need_timer = need_timer || trickle_running(&message->trickle, &domain->data_trickle_params);
kadonotakashi 0:8fdf9a60065b 957 }
kadonotakashi 0:8fdf9a60065b 958 }
kadonotakashi 0:8fdf9a60065b 959 need_timer = need_timer || trickle_running(&domain->trickle, &domain->control_trickle_params);
kadonotakashi 0:8fdf9a60065b 960 }
kadonotakashi 0:8fdf9a60065b 961
kadonotakashi 0:8fdf9a60065b 962 if (need_timer) {
kadonotakashi 0:8fdf9a60065b 963 mpl_schedule_timer();
kadonotakashi 0:8fdf9a60065b 964 }
kadonotakashi 0:8fdf9a60065b 965 }
kadonotakashi 0:8fdf9a60065b 966
kadonotakashi 0:8fdf9a60065b 967 void mpl_slow_timer(uint16_t seconds)
kadonotakashi 0:8fdf9a60065b 968 {
kadonotakashi 0:8fdf9a60065b 969 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 970 uint32_t message_age_limit = (domain->seed_set_entry_lifetime * UINT32_C(10)) / 4;
kadonotakashi 0:8fdf9a60065b 971 if (message_age_limit > MAX_BUFFERED_MESSAGE_LIFETIME) {
kadonotakashi 0:8fdf9a60065b 972 message_age_limit = MAX_BUFFERED_MESSAGE_LIFETIME;
kadonotakashi 0:8fdf9a60065b 973 }
kadonotakashi 0:8fdf9a60065b 974 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 975 /* Count down seed lifetime, and expire immediately when hit */
kadonotakashi 0:8fdf9a60065b 976 if (seed->lifetime > seconds) {
kadonotakashi 0:8fdf9a60065b 977 seed->lifetime -= seconds;
kadonotakashi 0:8fdf9a60065b 978 } else {
kadonotakashi 0:8fdf9a60065b 979 mpl_seed_delete(domain, seed);
kadonotakashi 0:8fdf9a60065b 980 continue;
kadonotakashi 0:8fdf9a60065b 981 }
kadonotakashi 0:8fdf9a60065b 982 /* Once data trickle timer has stopped, we MAY delete a message by
kadonotakashi 0:8fdf9a60065b 983 * advancing MinSequence. We use timestamp to control this, so we
kadonotakashi 0:8fdf9a60065b 984 * can hold beyond just the initial data transmission, permitting
kadonotakashi 0:8fdf9a60065b 985 * it to be restarted by control messages.
kadonotakashi 0:8fdf9a60065b 986 */
kadonotakashi 0:8fdf9a60065b 987 ns_list_foreach_safe(mpl_buffered_message_t, message, &seed->messages) {
kadonotakashi 0:8fdf9a60065b 988 if (!trickle_running(&message->trickle, &domain->data_trickle_params) &&
kadonotakashi 0:8fdf9a60065b 989 protocol_core_monotonic_time - message->timestamp >= message_age_limit) {
kadonotakashi 0:8fdf9a60065b 990 seed->min_sequence = mpl_buffer_sequence(message) + 1;
kadonotakashi 0:8fdf9a60065b 991 mpl_buffer_delete(seed, message);
kadonotakashi 0:8fdf9a60065b 992 } else {
kadonotakashi 0:8fdf9a60065b 993 break;
kadonotakashi 0:8fdf9a60065b 994 }
kadonotakashi 0:8fdf9a60065b 995 }
kadonotakashi 0:8fdf9a60065b 996 }
kadonotakashi 0:8fdf9a60065b 997 }
kadonotakashi 0:8fdf9a60065b 998 }
kadonotakashi 0:8fdf9a60065b 999
kadonotakashi 0:8fdf9a60065b 1000 void mpl_clear_realm_scope_seeds(protocol_interface_info_entry_t *cur)
kadonotakashi 0:8fdf9a60065b 1001 {
kadonotakashi 0:8fdf9a60065b 1002 ns_list_foreach(mpl_domain_t, domain, &mpl_domains) {
kadonotakashi 0:8fdf9a60065b 1003 if (domain->interface == cur && addr_ipv6_multicast_scope(domain->address) <= IPV6_SCOPE_REALM_LOCAL) {
kadonotakashi 0:8fdf9a60065b 1004 ns_list_foreach_safe(mpl_seed_t, seed, &domain->seeds) {
kadonotakashi 0:8fdf9a60065b 1005 mpl_seed_delete(domain, seed);
kadonotakashi 0:8fdf9a60065b 1006 }
kadonotakashi 0:8fdf9a60065b 1007 }
kadonotakashi 0:8fdf9a60065b 1008 }
kadonotakashi 0:8fdf9a60065b 1009 }
kadonotakashi 0:8fdf9a60065b 1010
kadonotakashi 0:8fdf9a60065b 1011 static buffer_t *mpl_exthdr_provider(buffer_t *buf, ipv6_exthdr_stage_t stage, int16_t *result)
kadonotakashi 0:8fdf9a60065b 1012 {
kadonotakashi 0:8fdf9a60065b 1013 mpl_domain_t *domain = mpl_domain_lookup_with_realm_check(buf->interface, buf->dst_sa.address);
kadonotakashi 0:8fdf9a60065b 1014 if (!domain) {
kadonotakashi 0:8fdf9a60065b 1015 // We need to tunnel
kadonotakashi 0:8fdf9a60065b 1016
kadonotakashi 0:8fdf9a60065b 1017 if (stage != IPV6_EXTHDR_MODIFY) {
kadonotakashi 0:8fdf9a60065b 1018 *result = 0;
kadonotakashi 0:8fdf9a60065b 1019 return buf;
kadonotakashi 0:8fdf9a60065b 1020 }
kadonotakashi 0:8fdf9a60065b 1021
kadonotakashi 0:8fdf9a60065b 1022 *result = IPV6_EXTHDR_MODIFY_TUNNEL;
kadonotakashi 0:8fdf9a60065b 1023 memcpy(buf->dst_sa.address, ADDR_ALL_MPL_FORWARDERS, 16);
kadonotakashi 0:8fdf9a60065b 1024 buf->src_sa.addr_type = ADDR_NONE; // force auto-selection
kadonotakashi 0:8fdf9a60065b 1025 return buf;
kadonotakashi 0:8fdf9a60065b 1026 }
kadonotakashi 0:8fdf9a60065b 1027
kadonotakashi 0:8fdf9a60065b 1028 const uint8_t *seed_id;
kadonotakashi 0:8fdf9a60065b 1029 uint8_t seed_id_len;
kadonotakashi 0:8fdf9a60065b 1030 uint8_t seed_id_buf[16];
kadonotakashi 0:8fdf9a60065b 1031 if (domain->seed_id_mode > 0) {
kadonotakashi 0:8fdf9a60065b 1032 seed_id_len = domain->seed_id_mode;
kadonotakashi 0:8fdf9a60065b 1033 seed_id = domain->seed_id;
kadonotakashi 0:8fdf9a60065b 1034 } else switch (domain->seed_id_mode) {
kadonotakashi 0:8fdf9a60065b 1035 case MULTICAST_MPL_SEED_ID_MAC_SHORT: {
kadonotakashi 0:8fdf9a60065b 1036 uint16_t addr = mac_helper_mac16_address_get(buf->interface);
kadonotakashi 0:8fdf9a60065b 1037 if (addr < 0xfffe) {
kadonotakashi 0:8fdf9a60065b 1038 common_write_16_bit(addr, seed_id_buf);
kadonotakashi 0:8fdf9a60065b 1039 seed_id = seed_id_buf;
kadonotakashi 0:8fdf9a60065b 1040 seed_id_len = 2;
kadonotakashi 0:8fdf9a60065b 1041 break;
kadonotakashi 0:8fdf9a60065b 1042 }
kadonotakashi 0:8fdf9a60065b 1043 // Otherwise fall through to extended
kadonotakashi 0:8fdf9a60065b 1044 case MULTICAST_MPL_SEED_ID_MAC:
kadonotakashi 0:8fdf9a60065b 1045 seed_id = buf->interface->mac;
kadonotakashi 0:8fdf9a60065b 1046 seed_id_len = 8;
kadonotakashi 0:8fdf9a60065b 1047 break;
kadonotakashi 0:8fdf9a60065b 1048
kadonotakashi 0:8fdf9a60065b 1049 case MULTICAST_MPL_SEED_ID_IID_EUI64:
kadonotakashi 0:8fdf9a60065b 1050 seed_id = buf->interface->iid_eui64;
kadonotakashi 0:8fdf9a60065b 1051 seed_id_len = 8;
kadonotakashi 0:8fdf9a60065b 1052 break;
kadonotakashi 0:8fdf9a60065b 1053
kadonotakashi 0:8fdf9a60065b 1054 case MULTICAST_MPL_SEED_ID_IID_SLAAC:
kadonotakashi 0:8fdf9a60065b 1055 seed_id = buf->interface->iid_slaac;
kadonotakashi 0:8fdf9a60065b 1056 seed_id_len = 8;
kadonotakashi 0:8fdf9a60065b 1057 break;
kadonotakashi 0:8fdf9a60065b 1058 }
kadonotakashi 0:8fdf9a60065b 1059
kadonotakashi 0:8fdf9a60065b 1060 default:
kadonotakashi 0:8fdf9a60065b 1061 case MULTICAST_MPL_SEED_ID_IPV6_SRC_FOR_DOMAIN:
kadonotakashi 0:8fdf9a60065b 1062 seed_id = addr_select_source(buf->interface, domain->address, 0);
kadonotakashi 0:8fdf9a60065b 1063 seed_id_len = 16;
kadonotakashi 0:8fdf9a60065b 1064 break;
kadonotakashi 0:8fdf9a60065b 1065 }
kadonotakashi 0:8fdf9a60065b 1066
kadonotakashi 0:8fdf9a60065b 1067 if (!seed_id) {
kadonotakashi 0:8fdf9a60065b 1068 tr_err("No MPL Seed ID");
kadonotakashi 0:8fdf9a60065b 1069 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 1070 }
kadonotakashi 0:8fdf9a60065b 1071
kadonotakashi 0:8fdf9a60065b 1072 /* "Compress" seed ID if it's the IPv6 source address */
kadonotakashi 0:8fdf9a60065b 1073 /* (For Thread, also compress if source is the 16-bit address) */
kadonotakashi 0:8fdf9a60065b 1074 if (seed_id_len == 16 && addr_ipv6_equal(seed_id, buf->src_sa.address)) {
kadonotakashi 0:8fdf9a60065b 1075 seed_id_len = 0;
kadonotakashi 0:8fdf9a60065b 1076 } else if (seed_id_len == 2 && thread_addr_is_mesh_local_16(buf->src_sa.address, buf->interface) &&
kadonotakashi 0:8fdf9a60065b 1077 seed_id[0] == buf->src_sa.address[14] && seed_id[1] == buf->src_sa.address[15]) {
kadonotakashi 0:8fdf9a60065b 1078 seed_id_len = 0;
kadonotakashi 0:8fdf9a60065b 1079 }
kadonotakashi 0:8fdf9a60065b 1080
kadonotakashi 0:8fdf9a60065b 1081 switch (stage) {
kadonotakashi 0:8fdf9a60065b 1082 case IPV6_EXTHDR_SIZE:
kadonotakashi 0:8fdf9a60065b 1083 *result = 4 + seed_id_len;
kadonotakashi 0:8fdf9a60065b 1084 return buf;
kadonotakashi 0:8fdf9a60065b 1085 case IPV6_EXTHDR_INSERT: {
kadonotakashi 0:8fdf9a60065b 1086 /* Only have 4 possible lengths/padding patterns to consider:
kadonotakashi 0:8fdf9a60065b 1087 * HbH 2 + Option header 4 + Seed 0 + Padding 2 = 8
kadonotakashi 0:8fdf9a60065b 1088 * HbH 2 + Option header 4 + Seed 2 + Padding 0 = 8
kadonotakashi 0:8fdf9a60065b 1089 * HbH 2 + Option header 4 + Seed 8 + Padding 2 = 16
kadonotakashi 0:8fdf9a60065b 1090 * HbH 2 + Option header 4 + Seed 16 + Padding 2 = 24
kadonotakashi 0:8fdf9a60065b 1091 */
kadonotakashi 0:8fdf9a60065b 1092 uint8_t extlen = (6 + seed_id_len + 7) &~ 7;
kadonotakashi 0:8fdf9a60065b 1093 buf = buffer_headroom(buf, extlen);
kadonotakashi 0:8fdf9a60065b 1094 if (!buf) {
kadonotakashi 0:8fdf9a60065b 1095 return NULL;
kadonotakashi 0:8fdf9a60065b 1096 }
kadonotakashi 0:8fdf9a60065b 1097 uint8_t *ext = buffer_data_reserve_header(buf, extlen);
kadonotakashi 0:8fdf9a60065b 1098 ext[0] = buf->options.type;
kadonotakashi 0:8fdf9a60065b 1099 buf->options.type = IPV6_NH_HOP_BY_HOP;
kadonotakashi 0:8fdf9a60065b 1100 ext[1] = (extlen / 8) - 1;
kadonotakashi 0:8fdf9a60065b 1101 ext[2] = IPV6_OPTION_MPL;
kadonotakashi 0:8fdf9a60065b 1102 ext[3] = 2 + seed_id_len;
kadonotakashi 0:8fdf9a60065b 1103 ext[4] = (mpl_seed_id_type(seed_id_len) << MPL_OPT_S_SHIFT);
kadonotakashi 0:8fdf9a60065b 1104 ext[5] = 0; // sequence placeholder
kadonotakashi 0:8fdf9a60065b 1105 memcpy(ext + 6, seed_id, seed_id_len);
kadonotakashi 0:8fdf9a60065b 1106 if (seed_id_len != 2) {
kadonotakashi 0:8fdf9a60065b 1107 ext[extlen - 2] = IPV6_OPTION_PADN;
kadonotakashi 0:8fdf9a60065b 1108 ext[extlen - 1] = 0;
kadonotakashi 0:8fdf9a60065b 1109 }
kadonotakashi 0:8fdf9a60065b 1110
kadonotakashi 0:8fdf9a60065b 1111 *result = 0;
kadonotakashi 0:8fdf9a60065b 1112 buf->options.ip_extflags |= IPEXT_HBH_MPL | IPEXT_HBH_MPL_UNFILLED;
kadonotakashi 0:8fdf9a60065b 1113 return buf;
kadonotakashi 0:8fdf9a60065b 1114 }
kadonotakashi 0:8fdf9a60065b 1115 case IPV6_EXTHDR_MODIFY:
kadonotakashi 0:8fdf9a60065b 1116 if (buf->options.ip_extflags & IPEXT_HBH_MPL_UNFILLED) {
kadonotakashi 0:8fdf9a60065b 1117 /* We assume we created this, therefore our option is in place
kadonotakashi 0:8fdf9a60065b 1118 * in the expected place. Sequence is set now, AFTER
kadonotakashi 0:8fdf9a60065b 1119 * fragmentation.
kadonotakashi 0:8fdf9a60065b 1120 */
kadonotakashi 0:8fdf9a60065b 1121 uint8_t *iphdr = buffer_data_pointer(buf);
kadonotakashi 0:8fdf9a60065b 1122 uint8_t *ext = iphdr + IPV6_HDRLEN;
kadonotakashi 0:8fdf9a60065b 1123 if (iphdr[IPV6_HDROFF_NH] != IPV6_NH_HOP_BY_HOP || ext[2] != IPV6_OPTION_MPL) {
kadonotakashi 0:8fdf9a60065b 1124 tr_err("modify");
kadonotakashi 0:8fdf9a60065b 1125 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 1126 }
kadonotakashi 0:8fdf9a60065b 1127 /* We don't bother setting the M flag on these initial packets. Setting to 0 is always acceptable. */
kadonotakashi 0:8fdf9a60065b 1128 ext[5] = domain->sequence++;
kadonotakashi 0:8fdf9a60065b 1129 buf->options.ip_extflags &=~ IPEXT_HBH_MPL_UNFILLED;
kadonotakashi 0:8fdf9a60065b 1130 buf->mpl_option_data_offset = IPV6_HDRLEN + 4;
kadonotakashi 0:8fdf9a60065b 1131 mpl_forwarder_process_message(buf, domain, true);
kadonotakashi 0:8fdf9a60065b 1132 }
kadonotakashi 0:8fdf9a60065b 1133 *result = 0;
kadonotakashi 0:8fdf9a60065b 1134 return buf;
kadonotakashi 0:8fdf9a60065b 1135 default:
kadonotakashi 0:8fdf9a60065b 1136 return buffer_free(buf);
kadonotakashi 0:8fdf9a60065b 1137 }
kadonotakashi 0:8fdf9a60065b 1138 }
kadonotakashi 0:8fdf9a60065b 1139
kadonotakashi 0:8fdf9a60065b 1140 #endif /* HAVE_MPL */
kadonotakashi 0:8fdf9a60065b 1141