BA
/
BaBoRo1
Embed:
(wiki syntax)
Show/hide line numbers
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 Tue Jul 12 2022 12:22:26 by
