Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers thread_resolution_client.c Source File

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