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