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: TYBLE16_simple_data_logger TYBLE16_MP3_Air
thread_resolution_client.c
00001 /* 00002 * Copyright (c) 2014-2019, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: BSD-3-Clause 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holder nor the 00014 * names of its contributors may be used to endorse or promote products 00015 * derived from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00018 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00020 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00021 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00022 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00023 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00024 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00025 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00026 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00027 * POSSIBILITY OF SUCH DAMAGE. 00028 */ 00029 00030 #include "nsconfig.h" 00031 #ifdef HAVE_THREAD_NEIGHBOR_DISCOVERY 00032 #include <ns_types.h> 00033 #include "ns_list.h" 00034 #include "ns_trace.h" 00035 #include "nsdynmemLIB.h" 00036 #include "Core/include/ns_address_internal.h" 00037 #include "thread_tmfcop_lib.h" 00038 00039 #include "coap_service_api.h" 00040 00041 #include "thread_config.h" 00042 #include "thread_management_server.h" 00043 #include "thread_resolution_client.h" 00044 00045 #define TRACE_GROUP TRACE_GROUP_THREAD_RESOLUTION_CLIENT 00046 00047 #define ADDRESS_QUERY_TIMEOUT 3 00048 #define ADDRESS_QUERY_INITIAL_RETRY_DELAY 15 00049 #define ADDRESS_QUERY_MAX_RETRY_DELAY (8*60*60) // 8 hours = 28800 seconds 00050 00051 #define ADDRESS_QUERY_SET_SIZE 8 00052 00053 /* Thread says queries go to this "all-routers" address - routers respond for 00054 * end nodes. 00055 */ 00056 static const uint8_t ADDR_MESH_LOCAL_ALL_ROUTERS[16] = { 0xff, 0x03, [15] = 0x02 }; // ff03::2 00057 00058 typedef struct address_query { 00059 /* Outgoing query information */ 00060 uint8_t eid[16]; // IPv6 address 00061 uint16_t aq_timeout; 00062 uint16_t aq_retry_timeout; 00063 uint8_t aq_failures; 00064 /* Incoming notification information */ 00065 uint8_t an_ml_eid[8]; 00066 bool an_received; 00067 bool an_duplicate; 00068 uint16_t an_rloc; 00069 uint32_t an_last_transaction_time; 00070 ns_list_link_t link; 00071 } address_query_t; 00072 00073 typedef struct thread_resolution_client { 00074 thread_resolution_client_response_cb *response_cb_ptr; 00075 thread_resolution_client_notification_cb *notification_cb_ptr; 00076 thread_resolution_client_error_cb *error_cb_ptr; 00077 int8_t interface_id; 00078 int8_t coap_service_id; 00079 NS_LIST_HEAD (address_query_t, link) queries; 00080 ns_list_link_t link; 00081 } thread_resolution_client_t; 00082 00083 static NS_LIST_DEFINE(instance_list, thread_resolution_client_t, link); 00084 00085 static thread_resolution_client_t *thread_resolution_client_find(int8_t interface_id) 00086 { 00087 ns_list_foreach(thread_resolution_client_t, cur_ptr, &instance_list) { 00088 if (cur_ptr->interface_id == interface_id) { 00089 return cur_ptr; 00090 } 00091 } 00092 return NULL; 00093 } 00094 00095 static thread_resolution_client_t *thread_resolution_client_find_by_service(int8_t service_id) 00096 { 00097 ns_list_foreach(thread_resolution_client_t, cur_ptr, &instance_list) { 00098 if (cur_ptr->coap_service_id == service_id) { 00099 return cur_ptr; 00100 } 00101 } 00102 return NULL; 00103 } 00104 00105 static address_query_t *address_query_find(thread_resolution_client_t *this, const uint8_t eid[static 16]) 00106 { 00107 ns_list_foreach(address_query_t, query, &this->queries) { 00108 if (addr_ipv6_equal(query->eid, eid)) { 00109 return query; 00110 } 00111 } 00112 return NULL; 00113 } 00114 00115 static void address_query_delete(thread_resolution_client_t *this, address_query_t *query) 00116 { 00117 ns_list_remove(&this->queries, query); 00118 ns_dyn_mem_free(query); 00119 } 00120 00121 static void address_query_timeout(thread_resolution_client_t *this, address_query_t *query) 00122 { 00123 if (!query->an_received) { 00124 query->aq_retry_timeout = ADDRESS_QUERY_INITIAL_RETRY_DELAY; 00125 /* Spec says initial * 2^Failures, _after_ increment, but that's silly */ 00126 for (uint8_t i = query->aq_failures; i; i--) { 00127 if (query->aq_retry_timeout < ADDRESS_QUERY_MAX_RETRY_DELAY / 2) { 00128 query->aq_retry_timeout *= 2; 00129 } else { 00130 query->aq_retry_timeout = ADDRESS_QUERY_MAX_RETRY_DELAY; 00131 break; 00132 } 00133 } 00134 query->aq_retry_timeout++; // +1 for timer vagaries 00135 00136 if (query->aq_failures < UINT8_MAX) { 00137 query->aq_failures++; 00138 } 00139 tr_info("AQ failed (%d)", query->aq_failures); 00140 this->response_cb_ptr(this->interface_id, -1, query->eid, 0xFFFE, 0, NULL); 00141 return; 00142 } 00143 00144 if (query->an_duplicate) { 00145 /* Note that error message contains ML-EID that we are *accepting*. All 00146 * *other* routers should invalidate, meaning it still makes sense 00147 * to process the result. 00148 */ 00149 tr_warn("Duplicate address during resolution"); 00150 thread_resolution_client_address_error(this->interface_id, ADDR_MESH_LOCAL_ALL_ROUTERS, query->eid, query->an_ml_eid); 00151 } 00152 00153 /* No need to make the callback here - we did it immediately */ 00154 //tr_info("Resolved %s %s -> %04x", trace_ipv6(query->eid), trace_array(query->an_ml_eid, 8), query->an_rloc); 00155 //this->response_cb_ptr(this->interface_id, 0, query->eid, query->an_rloc, query->an_last_transaction_time, query->an_ml_eid); 00156 address_query_delete(this, query); 00157 } 00158 00159 /* Handle incoming "notification" POSTs */ 00160 static int thread_resolution_client_notification_post_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr) 00161 { 00162 thread_resolution_client_t *this = thread_resolution_client_find_by_service(service_id); 00163 uint8_t *target_ip_addr = NULL; 00164 uint8_t *mleid = NULL; 00165 uint16_t target_rloc = 0xffff; 00166 uint32_t last_transaction_time = 0; 00167 sn_coap_msg_code_e coap_code; 00168 (void) source_address; 00169 (void) source_port; 00170 00171 tr_debug("Thread resolution notification cb"); 00172 if (!this) { 00173 coap_code = COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR; 00174 goto done; 00175 } 00176 00177 /* Target EID, Locator and MLE-ID are compulsory */ 00178 if (16 > thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &target_ip_addr) || 00179 2 > thread_tmfcop_tlv_data_get_uint16(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_RLOC16, &target_rloc) || 00180 8 > thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ML_EID, &mleid)) { 00181 coap_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; 00182 goto done; 00183 } 00184 00185 /* Last transaction is optional - I think defaulting to 0 will give the desired result when it's absent */ 00186 thread_tmfcop_tlv_data_get_uint32(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_LAST_TRANSACTION_TIME, &last_transaction_time); 00187 00188 address_query_t *query = address_query_find(this, target_ip_addr); 00189 if (query && query->aq_timeout > 0) { 00190 tr_info("a/an(solicited); target=%s mleid=%s rloc=%04x, ltt=%"PRIu32, trace_ipv6(target_ip_addr), trace_array(mleid, 8), target_rloc, last_transaction_time); 00191 /* A notification for an ongoing query */ 00192 if (query->an_received && memcmp(query->an_ml_eid, mleid, 8)) { 00193 query->an_duplicate = true; 00194 tr_warn("DUP!"); 00195 } 00196 00197 if (!query->an_received || last_transaction_time <= query->an_last_transaction_time) { 00198 query->an_received = true; 00199 memcpy(query->an_ml_eid, mleid, 8); 00200 query->an_rloc = target_rloc; 00201 query->an_last_transaction_time = last_transaction_time; 00202 /* If we get "old then new" responses, we call the response twice. This 00203 * will likely mean the first transmission goes to the old address, then subsequent 00204 * will go to the new address. But better than waiting the full 3 seconds 00205 * to make the call. 00206 */ 00207 tr_info("Resolved %s %s -> %04x", trace_ipv6(query->eid), trace_array(query->an_ml_eid, 8), query->an_rloc); 00208 this->response_cb_ptr(this->interface_id, 0, query->eid, query->an_rloc, query->an_last_transaction_time, query->an_ml_eid); 00209 tr_info("Accepted"); 00210 } else { 00211 tr_info("Rejected"); 00212 } 00213 } else { 00214 /* Not for an non-ongoing query, but delete any stale ones - no longer "failed" */ 00215 if (query) { 00216 ns_list_remove(&this->queries, query); 00217 ns_dyn_mem_free(query); 00218 } 00219 tr_info("a/an(unsolicited); target=%s mleid=%s rloc=%04x", trace_ipv6(target_ip_addr), trace_array(mleid, 8), target_rloc); 00220 /* And tell the core */ 00221 if (this->notification_cb_ptr) { 00222 this->notification_cb_ptr(this->interface_id, target_ip_addr, target_rloc, mleid); 00223 } else { 00224 coap_code = COAP_MSG_CODE_RESPONSE_FORBIDDEN; 00225 goto done; 00226 } 00227 } 00228 00229 coap_code = COAP_MSG_CODE_RESPONSE_CHANGED; 00230 00231 done: 00232 coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_ADDRESS_SHORT, request_ptr, coap_code, COAP_CT_NONE, NULL, 0); 00233 return 0; 00234 } 00235 00236 /* Handle incoming "error" POSTs */ 00237 static int thread_resolution_client_error_post_cb(int8_t service_id, uint8_t source_address[static 16], uint16_t source_port, sn_coap_hdr_s *request_ptr) 00238 { 00239 thread_resolution_client_t *this = thread_resolution_client_find_by_service(service_id); 00240 uint8_t *target_ip_addr = NULL; 00241 uint8_t *mleid = NULL; 00242 sn_coap_msg_code_e coap_code; 00243 (void) source_address; 00244 (void) source_port; 00245 00246 tr_debug("Thread resolution error cb"); 00247 if (!this) { 00248 coap_code = COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR; 00249 goto done; 00250 } 00251 00252 /* Target EID and ML-EID are compulsory */ 00253 if (16 > thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_TARGET_EID, &target_ip_addr) || 00254 8 > thread_tmfcop_tlv_find(request_ptr->payload_ptr, request_ptr->payload_len, TMFCOP_TLV_ML_EID, &mleid)) { 00255 coap_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; 00256 goto done; 00257 } 00258 00259 if (this->error_cb_ptr) { 00260 this->error_cb_ptr(this->interface_id, target_ip_addr, mleid); 00261 } else { 00262 coap_code = COAP_MSG_CODE_RESPONSE_FORBIDDEN; 00263 goto done; 00264 } 00265 00266 coap_code = COAP_MSG_CODE_RESPONSE_CHANGED; 00267 00268 done: 00269 /* If the CoAP message was sent as confirmable, send response */ 00270 if (request_ptr->msg_type == COAP_MSG_TYPE_CONFIRMABLE) { 00271 coap_service_response_send(service_id, COAP_REQUEST_OPTIONS_ADDRESS_SHORT, request_ptr, coap_code, COAP_CT_NONE, NULL, 0); 00272 return 0; 00273 } 00274 return -1; 00275 } 00276 00277 00278 /** 00279 * Public Api functions 00280 */ 00281 void thread_resolution_client_init(int8_t interface_id) 00282 { 00283 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00284 if (this) { 00285 return; 00286 } 00287 00288 this = ns_dyn_mem_alloc(sizeof(thread_resolution_client_t)); 00289 if (!this) { 00290 return; 00291 } 00292 00293 this->interface_id = interface_id; 00294 this->notification_cb_ptr = NULL; 00295 this->error_cb_ptr = NULL; 00296 //TODO: Check if to use ephemeral port here 00297 this->coap_service_id = thread_management_server_service_id_get(interface_id); 00298 if (this->coap_service_id < 0) { 00299 tr_err("Thread resolution client init failed"); 00300 ns_dyn_mem_free(this); 00301 return; 00302 } 00303 ns_list_init(&this->queries); 00304 ns_list_add_to_start(&instance_list, this); 00305 00306 coap_service_register_uri(this->coap_service_id, THREAD_URI_ADDRESS_NOTIFICATION, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_resolution_client_notification_post_cb); 00307 coap_service_register_uri(this->coap_service_id, THREAD_URI_ADDRESS_ERROR, COAP_SERVICE_ACCESS_POST_ALLOWED, thread_resolution_client_error_post_cb); 00308 } 00309 00310 void thread_resolution_client_delete(int8_t interface_id) 00311 { 00312 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00313 if (!this) { 00314 return; 00315 } 00316 00317 coap_service_unregister_uri(this->coap_service_id, THREAD_URI_ADDRESS_NOTIFICATION); 00318 coap_service_unregister_uri(this->coap_service_id, THREAD_URI_ADDRESS_ERROR); 00319 ns_list_foreach_safe(address_query_t, query, &this->queries) { 00320 ns_list_remove(&this->queries, query); 00321 ns_dyn_mem_free(query); 00322 } 00323 ns_list_remove(&instance_list, this); 00324 ns_dyn_mem_free(this); 00325 } 00326 00327 void thread_resolution_client_set_notification_cb(int8_t interface_id, thread_resolution_client_notification_cb notification_cb) 00328 { 00329 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00330 if (!this) { 00331 return; 00332 } 00333 00334 this->notification_cb_ptr = notification_cb; 00335 } 00336 00337 void thread_resolution_client_set_error_cb(int8_t interface_id, thread_resolution_client_error_cb error_cb) 00338 { 00339 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00340 if (!this) { 00341 return; 00342 } 00343 00344 this->error_cb_ptr = error_cb; 00345 } 00346 00347 int thread_resolution_client_address_error(int8_t interface_id, const uint8_t dest_ip_addr[16], const uint8_t target_ip_addr[16], const uint8_t ml_eid[8]) 00348 { 00349 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00350 sn_coap_msg_type_e msg_type = COAP_MSG_TYPE_CONFIRMABLE; 00351 uint8_t payload[2 + 16 + 2 + 8]; 00352 uint8_t *ptr; 00353 uint8_t options; 00354 00355 if (!this || !target_ip_addr || !dest_ip_addr || !ml_eid) { 00356 return -1; 00357 } 00358 00359 ptr = payload; 00360 ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, target_ip_addr); 00361 ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_ML_EID, 8, ml_eid); 00362 00363 options = COAP_REQUEST_OPTIONS_ADDRESS_SHORT; 00364 if (addr_is_ipv6_multicast(dest_ip_addr)) { 00365 options |= COAP_REQUEST_OPTIONS_MULTICAST; 00366 msg_type = COAP_MSG_TYPE_NON_CONFIRMABLE; 00367 } 00368 00369 tr_debug("TX thread address error: target %s, mle %s, dest %s", trace_ipv6(target_ip_addr), trace_array(ml_eid, 8), trace_ipv6(dest_ip_addr)); 00370 00371 /* We don't expect a response to this POST, so we don't specify a callback. */ 00372 coap_service_request_send(this->coap_service_id, options, 00373 dest_ip_addr, THREAD_MANAGEMENT_PORT, 00374 msg_type, COAP_MSG_CODE_REQUEST_POST, 00375 THREAD_URI_ADDRESS_ERROR, COAP_CT_OCTET_STREAM, 00376 payload, ptr - payload, NULL); 00377 00378 return 0; 00379 00380 } 00381 00382 int thread_resolution_client_resolve(int8_t interface_id, uint8_t ip_addr[16], thread_resolution_client_response_cb *resp_cb) 00383 { 00384 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00385 uint8_t payload[2 + 16]; 00386 uint8_t *ptr; 00387 00388 if (!this || !ip_addr || !resp_cb) { 00389 return -1; 00390 } 00391 00392 address_query_t *query = address_query_find(this, ip_addr); 00393 if (query) { 00394 if (query->aq_timeout != 0) { 00395 /* Already in progress */ 00396 return 0; 00397 } 00398 if (query->aq_retry_timeout != 0) { 00399 /* Will not query - this will make the packet get dropped (with Address Unreachable) */ 00400 return -1; 00401 } 00402 /* Otherwise both timeouts zero - fine to proceed */ 00403 /* Remove so it can be readded to start of list */ 00404 ns_list_remove(&this->queries, query); 00405 } else { 00406 /* Get a new set entry - periodic timer will clear up if we go above limit */ 00407 query = ns_dyn_mem_alloc(sizeof * query); 00408 if (!query) { 00409 return -1; 00410 } 00411 memset(query, 0, sizeof * query); 00412 memcpy(query->eid, ip_addr, 16); 00413 } 00414 ns_list_add_to_start(&this->queries, query); 00415 00416 query->aq_timeout = ADDRESS_QUERY_TIMEOUT + 1; // +1 to allow for timer vagaries 00417 00418 this->response_cb_ptr = resp_cb; 00419 ptr = payload; 00420 ptr = thread_tmfcop_tlv_data_write(ptr, TMFCOP_TLV_TARGET_EID, 16, ip_addr); 00421 00422 tr_debug("thread address query: target %s", trace_ipv6(ip_addr)); 00423 00424 /* We don't expect a response to this POST, so we don't specify a callback. 00425 * Instead, this POST may trigger an inbound POST, which will go to our 00426 * ADDRESS_QUERY_RESPONSE POST handler. 00427 */ 00428 coap_service_request_send(this->coap_service_id, COAP_REQUEST_OPTIONS_MULTICAST | COAP_REQUEST_OPTIONS_ADDRESS_SHORT, 00429 ADDR_MESH_LOCAL_ALL_ROUTERS, THREAD_MANAGEMENT_PORT, 00430 COAP_MSG_TYPE_NON_CONFIRMABLE, COAP_MSG_CODE_REQUEST_POST, 00431 THREAD_URI_ADDRESS_QUERY_REQUEST, COAP_CT_OCTET_STREAM, 00432 payload, ptr - payload, NULL); 00433 00434 return 0; 00435 } 00436 00437 void thread_resolution_client_timer(int8_t interface_id, uint16_t seconds) 00438 { 00439 thread_resolution_client_t *this = thread_resolution_client_find(interface_id); 00440 if (!this) { 00441 return; 00442 } 00443 unsigned count = 0; 00444 /* Run timers on set entries */ 00445 ns_list_foreach_safe(address_query_t, query, &this->queries) { 00446 ++count; 00447 if (query->aq_retry_timeout != 0) { 00448 if (query->aq_retry_timeout <= seconds) { 00449 query->aq_retry_timeout = 0; 00450 } else { 00451 query->aq_retry_timeout -= seconds; 00452 } 00453 } 00454 if (query->aq_timeout != 0) { 00455 if (query->aq_timeout <= seconds) { 00456 query->aq_timeout = 0; 00457 address_query_timeout(this, query); 00458 } else { 00459 query->aq_timeout -= seconds; 00460 } 00461 } 00462 } 00463 /* Remove oldest excess entries that are not ongoing */ 00464 if (count > ADDRESS_QUERY_SET_SIZE) { 00465 ns_list_foreach_reverse_safe(address_query_t, query, &this->queries) { 00466 if (query->aq_timeout == 0) { 00467 address_query_delete(this, query); 00468 if (--count <= ADDRESS_QUERY_SET_SIZE) { 00469 break; 00470 } 00471 } 00472 } 00473 } 00474 } 00475 00476 #endif // HAVE_THREAD_NEIGHBOR_DISCOVERY 00477
Generated on Tue Jul 12 2022 13:54:59 by
1.7.2