Add thread_network_destroy API with unittest
[platform/core/api/thread.git] / src / thread-network.c
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <errno.h>
18 #include <tizen.h>
19 #include <dlog.h>
20
21 #include <glib.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <arpa/inet.h>
28
29 #include "thread.h"
30 #include "thread-log.h"
31 #include "thread-private.h"
32 #include "thread-dbus-handler.h"
33 #include "thread-socket-handler.h"
34
35 #define THREAD_NETWORK_DEFAULT_NAME ""
36 #define THREAD_NETWORK_DEFAULT_KEY ""
37 #define THREAD_NETWORK_DEFAULT_PSKC ""
38 #define THREAD_NETWORK_DEFAULT_CHANNEL 0
39 #define THREAD_NETWORK_DEFAULT_EXTENDED_PANID 0
40 #define THREAD_NETWORK_DEFAULT_PANID 0
41
42 /* LCOV_EXCL_START */
43 void _thread_network_free(thread_network_h network)
44 {
45         FUNC_ENTRY;
46         if (!network)
47                 return;
48
49         g_free(network);
50
51         FUNC_EXIT;
52 }
53
54 int thread_network_create(thread_network_h *network)
55 {
56         FUNC_ENTRY;
57         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
58         THREAD_CHECK_INIT_STATUS();
59         THREAD_VALIDATE_INPUT_PARAMETER(network);
60
61         thread_network_s *new_network = NULL;
62         new_network = g_malloc0(sizeof(thread_network_s));
63         if (!new_network) {
64                 /* LCOV_EXCL_START */
65                 THREAD_ERR("g_malloc0 failed");
66                 FUNC_EXIT;
67                 return THREAD_ERROR_OUT_OF_MEMORY;
68                 /* LCOV_EXCL_STOP */
69         }
70
71         new_network->is_network_active = FALSE;
72         (void)g_strlcpy(new_network->name,
73                 THREAD_NETWORK_DEFAULT_NAME, THREAD_NETWORK_NAME_MAX + 1);
74         (void)g_strlcpy(new_network->key,
75                 THREAD_NETWORK_DEFAULT_KEY, THREAD_NETWORK_KEY_STRING_MAX + 1);
76         (void)g_strlcpy(new_network->pskc,
77                 THREAD_NETWORK_DEFAULT_PSKC, THREAD_NETWORK_PSKC_STRING_MAX + 1);
78         new_network->channel = THREAD_NETWORK_DEFAULT_CHANNEL;
79         new_network->extended_panid = THREAD_NETWORK_DEFAULT_EXTENDED_PANID;
80         new_network->panid = THREAD_NETWORK_DEFAULT_PANID;
81         *network = (thread_network_h)new_network;
82
83         FUNC_EXIT;
84         return THREAD_ERROR_NONE;
85 }
86
87 int thread_network_destroy(thread_network_h network)
88 {
89         FUNC_ENTRY;
90         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
91         THREAD_CHECK_INIT_STATUS();
92         THREAD_VALIDATE_INPUT_PARAMETER(network);
93
94         thread_network_s *current_network = network;
95
96         if (current_network->is_network_active) {
97                 THREAD_DBG("Thread network active, can't be destroyed:: \
98                                                 First Reset the Network");
99                 FUNC_EXIT;
100                 return THREAD_ERROR_OPERATION_FAILED;
101         }
102
103         _thread_network_free(current_network);
104         current_network = NULL;
105
106         FUNC_EXIT;
107         return THREAD_ERROR_NONE;
108 }
109
110 /* Network leader/Creator */
111 int thread_network_create_operational_network(thread_instance_h instance,
112         const char *name, const char *key, const char *pskc, uint32_t channel,
113         uint64_t extended_panid, uint16_t panid, thread_network_h *network)
114 {
115         FUNC_ENTRY;
116         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
117         THREAD_CHECK_INIT_STATUS();
118         THREAD_VALIDATE_INPUT_PARAMETER(instance);
119         THREAD_VALIDATE_INPUT_PARAMETER(name);
120         THREAD_VALIDATE_INPUT_PARAMETER(key);
121         THREAD_VALIDATE_INPUT_PARAMETER(pskc);
122         THREAD_VALIDATE_INPUT_PARAMETER(network);
123
124         retv_if(strlen(name) > THREAD_NETWORK_NAME_MAX,
125                                 THREAD_ERROR_INVALID_PARAMETER);
126         retv_if(strlen(key) > THREAD_NETWORK_KEY_STRING_MAX,
127                                 THREAD_ERROR_INVALID_PARAMETER);
128         retv_if(strlen(pskc) > THREAD_NETWORK_PSKC_STRING_MAX,
129                                 THREAD_ERROR_INVALID_PARAMETER);
130
131         THREAD_DBG("Network Name: %s", name);
132         THREAD_DBG("Network key: %s", key);
133         THREAD_DBG("Network pskc: %s", pskc);
134         THREAD_DBG("Network channel: 0x%8.8x", channel);
135         THREAD_DBG("Network extended_panid: %zu", (size_t)extended_panid);
136         THREAD_DBG("Network panid: %u", panid);
137
138         /* Free existing current network */
139         thread_instance_s *current_instance = instance;
140         _thread_network_free(current_instance->network);
141         current_instance->network = NULL;
142
143         /* Create New Network */
144         thread_network_s *new_network = NULL;
145         new_network = g_malloc0(sizeof(thread_network_s));
146         if (!new_network) {
147                 /* LCOV_EXCL_START */
148                 THREAD_ERR("g_malloc0 failed");
149                 return THREAD_ERROR_OUT_OF_MEMORY;
150                 /* LCOV_EXCL_STOP */
151         }
152
153         new_network->is_network_active = FALSE;
154         (void)g_strlcpy(new_network->name, name, THREAD_NETWORK_NAME_MAX + 1);
155         (void)g_strlcpy(new_network->key, key, THREAD_NETWORK_KEY_STRING_MAX + 1);
156         (void)g_strlcpy(new_network->pskc, pskc, THREAD_NETWORK_PSKC_STRING_MAX + 1);
157         new_network->channel = channel;
158         new_network->extended_panid = extended_panid;
159         new_network->panid = panid;
160         *network = (thread_network_h)new_network;
161
162         current_instance->network = *network;
163
164         FUNC_EXIT;
165         return THREAD_ERROR_NONE;
166 }
167
168 int thread_network_destroy_operational_network(thread_instance_h instance,
169         thread_network_h network)
170 {
171         FUNC_ENTRY;
172         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
173         THREAD_CHECK_INIT_STATUS();
174         THREAD_VALIDATE_INPUT_PARAMETER(instance);
175         THREAD_VALIDATE_INPUT_PARAMETER(network);
176
177         thread_instance_s *current_instance = instance;
178         thread_network_s *current_network = network;
179
180         if (current_network->is_network_active) {
181                 THREAD_DBG("Thread network active, can't be destroyed:: \
182                                                 First Reset the Network");
183                 return THREAD_ERROR_OPERATION_FAILED;
184         }
185
186         _thread_network_free(current_network);
187         current_network = NULL;
188         current_instance->network = NULL;
189
190         FUNC_EXIT;
191         return THREAD_ERROR_NONE;
192 }
193
194 int thread_network_set_active_dataset_tlvs(thread_instance_h instance,
195         const uint8_t *tlvs_buffer, int buf_length)
196 {
197         FUNC_ENTRY;
198         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
199         THREAD_CHECK_INIT_STATUS();
200         THREAD_VALIDATE_INPUT_PARAMETER(instance);
201         THREAD_VALIDATE_INPUT_PARAMETER(tlvs_buffer);
202
203         int ret = THREAD_ERROR_NONE;
204         GBytes *bytes = NULL;
205         GVariant *value = NULL;
206
207         /* Print input data */
208         char buf[THREAD_MAX_BUFFER_SIZE];
209         for (int i = 0; i < buf_length; i++)
210                 snprintf(buf + i*2, 3, "%2.2x", tlvs_buffer[i]);
211         THREAD_DBG("Active dataset tlvs size: %d :: %s", buf_length, buf);
212
213         bytes = g_bytes_new(tlvs_buffer, buf_length);
214         if (bytes == NULL) {
215                 ret = THREAD_ERROR_OPERATION_FAILED;
216                 goto exit;
217         }
218
219         value = g_variant_new_from_bytes(G_VARIANT_TYPE_BYTESTRING, bytes, true);
220         if (value == NULL) {
221                 ret = THREAD_ERROR_OPERATION_FAILED;
222                 goto exit;
223         }
224
225         /* set "ActiveDatasetTlvs" dbus property */
226         ret = _thread_dbus_set_property(
227                 THREAD_DBUS_PROPERTY_ACTIVE_DATASET_TLVS, value);
228         if (ret != THREAD_ERROR_NONE) {
229                 ret = THREAD_ERROR_OPERATION_FAILED;
230                 goto exit;
231         }
232         THREAD_DBG("Thread set active dataset tlvs successful");
233
234 exit:
235         if (value)
236                 g_variant_unref(value);
237         if (bytes)
238                 g_bytes_unref(bytes);
239
240         FUNC_EXIT;
241         return ret;
242 }
243
244 int thread_network_get_active_dataset_tlvs(thread_instance_h instance,
245         uint8_t **tlvs_buffer, int *buf_len)
246 {
247         FUNC_ENTRY;
248         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
249         THREAD_CHECK_INIT_STATUS();
250         THREAD_VALIDATE_INPUT_PARAMETER(instance);
251         THREAD_VALIDATE_INPUT_PARAMETER(tlvs_buffer);
252         THREAD_VALIDATE_INPUT_PARAMETER(buf_len);
253
254         /* get "ActiveDatasetTlvs" */
255         int ret = THREAD_ERROR_NONE;
256         char buffer[THREAD_MAX_BUFFER_SIZE];
257
258         int session_fd = _thread_get_socket_fd();
259         char cmd_buffer[THREAD_NETWORK_BUFFER_MAX];
260         int index;
261
262         snprintf(cmd_buffer, THREAD_NETWORK_BUFFER_MAX, "dataset active -x");
263
264         THREAD_DBG("DEBUG: NETWORK MESSAGE -> [%s]", cmd_buffer);
265
266         ret = _thread_socket_client_write(session_fd, cmd_buffer, strlen(cmd_buffer));
267         if (ret != THREAD_ERROR_NONE) {
268                 THREAD_DBG("Failed to execute command %s", cmd_buffer);
269                 return ret;
270         }
271         THREAD_DBG("Executed command '%s' with size %zu", cmd_buffer, strlen(cmd_buffer));
272
273         /* Check response */
274         ret = _thread_socket_client_read(session_fd, buffer);
275         if (ret != THREAD_ERROR_NONE && ret != THREAD_ERROR_ALREADY_DONE) {
276                 THREAD_DBG("Socket response failed..");
277                 return ret;
278         }
279
280         *tlvs_buffer = g_malloc0(THREAD_MAX_BUFFER_SIZE*sizeof(uint8_t));
281         if (!(*tlvs_buffer)) {
282                 /* LCOV_EXCL_START */
283                 THREAD_ERR("g_malloc0 failed");
284                 return THREAD_ERROR_OUT_OF_MEMORY;
285                 /* LCOV_EXCL_STOP */
286         }
287         index = 0;
288         while (index < THREAD_MAX_BUFFER_SIZE) {
289                 if (buffer[index] == 'D')
290                         break;
291
292                 (*tlvs_buffer)[index] = (uint8_t)buffer[index];
293                 index++;
294         }
295         *buf_len = index;
296
297         FUNC_EXIT;
298         return ret;
299 }
300
301 int thread_network_get_panid(thread_instance_h instance, uint16_t *panid)
302 {
303         FUNC_ENTRY;
304         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
305         THREAD_CHECK_INIT_STATUS();
306         THREAD_VALIDATE_INPUT_PARAMETER(instance);
307         THREAD_VALIDATE_INPUT_PARAMETER(panid);
308
309         int ret = THREAD_ERROR_NONE;
310         GVariant *out = NULL;
311
312         /* get "PanId" dbus property */
313         ret = _thread_dbus_get_property(
314                 THREAD_DBUS_PROPERTY_PANID, &out);
315         retv_if(ret != THREAD_ERROR_NONE, ret);
316
317         g_variant_get(out, "q", panid);
318         THREAD_DBG("Thread PanId: %u", (size_t)*panid);
319         g_variant_unref(out);
320
321         FUNC_EXIT;
322         return THREAD_ERROR_NONE;
323 }
324
325 static int __thread_attach_active_network()
326 {
327         FUNC_ENTRY;
328         int ret = THREAD_ERROR_NONE;
329
330         THREAD_DBG("Attach current active network dataset");
331         ret = _thread_dbus_sync_method_call(THREAD_DBUS_ATTACH_METHOD,
332                                                 g_variant_new("()"));
333         if (ret != THREAD_ERROR_NONE)
334                 THREAD_ERR("Thread Attach failed");
335         else
336                 THREAD_DBG("Thread Attach successful");
337
338         return ret;
339
340         FUNC_EXIT;
341 }
342
343 int thread_network_attach(thread_instance_h instance)
344 {
345         FUNC_ENTRY;
346         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
347         THREAD_CHECK_INIT_STATUS();
348         THREAD_VALIDATE_INPUT_PARAMETER(instance);
349         int ret = THREAD_ERROR_NONE;
350
351         thread_instance_s *current_instance = instance;
352         thread_network_s *network = current_instance->network;
353
354         if (!network) {
355                 ret = __thread_attach_active_network();
356                 goto done;
357         }
358
359         /* Network Info */
360         THREAD_DBG("Network Name: %s", network->name);
361         THREAD_DBG("Network key: %s", network->key);
362         THREAD_DBG("Network pskc: %s", network->pskc);
363         THREAD_DBG("Network channel: 0x%8.8x", network->channel);
364
365         THREAD_DBG("Network extended_panid: %zu", (size_t)network->extended_panid);
366         if (network->extended_panid == UINT64_MAX)
367                 THREAD_DBG("extended_panid is UINT64_MAX, "\
368                         "Random extended_panid will be used");
369
370         THREAD_DBG("Network panid: %u", network->panid);
371         if (network->panid == UINT16_MAX)
372                 THREAD_DBG("panid is UINT16_MAX, Random panid will be used");
373
374         THREAD_DBG("Network is_active: %s",
375                         network->is_network_active ? "Active" : "Not Active");
376
377         if (network->is_network_active) {
378                 ret = __thread_attach_active_network();
379                 goto done;
380         }
381
382         THREAD_DBG("Attach the current device to Thread Network");
383         /* Network key builder */
384         GVariantBuilder *key_builder;
385         key_builder = g_variant_builder_new(G_VARIANT_TYPE("ay"));
386         THREAD_DBG("key str length: %zu", strlen(network->key));
387         for (int i = 0; i < strlen(network->key); i++) {
388                 g_variant_builder_add(key_builder, "y",
389                                 (unsigned char)network->key[i]);
390         }
391
392         /* pskc builder */
393         GVariantBuilder *pskc_builder;
394         pskc_builder = g_variant_builder_new(G_VARIANT_TYPE("ay"));
395         THREAD_DBG("pskc str length: %zu", strlen(network->pskc));
396         for (int i = 0; i < strlen(network->pskc); i++) {
397                 g_variant_builder_add(pskc_builder, "y",
398                                 (unsigned char)network->pskc[i]);
399         }
400
401         THREAD_DBG("Thread dbus sync call...");
402         ret = _thread_dbus_sync_method_call(THREAD_DBUS_ATTACH_METHOD,
403                 g_variant_new("(ayqstayu)", key_builder, network->panid,
404                 network->name, network->extended_panid, pskc_builder, network->channel));
405
406         g_variant_builder_unref(key_builder);
407         g_variant_builder_unref(pskc_builder);
408
409         if (ret != THREAD_ERROR_NONE) {
410                 THREAD_ERR("Thread Attach failed");
411                 goto done;
412         }
413         network->is_network_active = TRUE;
414
415         THREAD_DBG("Thread Attach successful");
416
417 done:
418         FUNC_EXIT;
419         return ret;
420 }
421
422 static int __thread_detach_active_network()
423 {
424         FUNC_ENTRY;
425         int ret = THREAD_ERROR_NONE;
426
427         THREAD_DBG("Detach current active network dataset");
428         ret = _thread_dbus_sync_method_call(THREAD_DBUS_DETACH_METHOD,
429                                                 g_variant_new("()"));
430         if (ret != THREAD_ERROR_NONE)
431                 THREAD_ERR("Thread Detach failed");
432         else
433                 THREAD_DBG("Thread Detach successful");
434
435         FUNC_EXIT;
436         return ret;
437 }
438
439 int thread_network_detach(thread_instance_h instance)
440 {
441         FUNC_ENTRY;
442         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
443         THREAD_CHECK_INIT_STATUS();
444         THREAD_VALIDATE_INPUT_PARAMETER(instance);
445         int ret = THREAD_ERROR_NONE;
446
447         thread_instance_s *current_instance = instance;
448         thread_network_s *network = current_instance->network;
449
450         if (!network)
451                 return THREAD_ERROR_INVALID_PARAMETER;
452
453         if (network->is_network_active == TRUE)
454                 ret = __thread_detach_active_network();
455
456         if (ret != THREAD_ERROR_NONE) {
457                 THREAD_ERR("Thread Detach failed");
458                 goto done;
459         }
460         network->is_network_active = FALSE;
461         THREAD_DBG("Thread Detach successful");
462
463 done:
464         FUNC_EXIT;
465         return ret;
466 }
467
468 int thread_get_ipaddr(thread_instance_h instance, thread_ipaddr_foreach_cb callback,
469                                 thread_ipaddr_type_e ipaddr_type, void *user_data)
470 {
471         FUNC_ENTRY;
472         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
473         THREAD_CHECK_INIT_STATUS();
474         THREAD_VALIDATE_INPUT_PARAMETER(instance);
475
476         int ret = THREAD_ERROR_NONE;
477         char buffer[THREAD_MAX_BUFFER_SIZE];
478         int index = 0;
479         int pos = 0;
480         int count = 0;
481         char ipaddr[THREAD_IPV6_ADDRESS_LEN];
482
483         int session_fd = _thread_get_socket_fd();
484         char cmd_buffer[THREAD_NETWORK_BUFFER_MAX];
485
486         switch (ipaddr_type) {
487         case THREAD_IPADDR_TYPE_LINK_LOCAL:
488                 snprintf(cmd_buffer, THREAD_NETWORK_BUFFER_MAX, "ipaddr linklocal");
489                 break;
490         case THREAD_IPADDR_TYPE_RLOC:
491                 snprintf(cmd_buffer, THREAD_NETWORK_BUFFER_MAX, "ipaddr rloc");
492                 break;
493         case THREAD_IPADDR_TYPE_MLEID:
494                 snprintf(cmd_buffer, THREAD_NETWORK_BUFFER_MAX, "ipaddr mleid");
495                 break;
496         default:
497                 snprintf(cmd_buffer, THREAD_NETWORK_BUFFER_MAX, "ipaddr");
498         }
499
500         size_t buf_size = strlen(cmd_buffer);
501
502         THREAD_DBG("DEBUG: NETWORK MESSAGE -> [%s]", cmd_buffer);
503
504         ret = _thread_socket_client_write(session_fd, cmd_buffer, buf_size);
505         if (ret != THREAD_ERROR_NONE) {
506                 THREAD_DBG("Failed to execute command %s", cmd_buffer);
507                 return ret;
508         }
509         THREAD_DBG("Executed command '%s' with size %zu", cmd_buffer, buf_size);
510
511         /* Check response */
512         ret = _thread_socket_client_read(session_fd, buffer);
513         if (ret != THREAD_ERROR_NONE && ret != THREAD_ERROR_ALREADY_DONE) {
514                 THREAD_DBG("Socket response failed..");
515                 return ret;
516         }
517
518         while (true) {
519                 if ((buffer[index] >= 'a' && buffer[index] <= 'f') || buffer[index] == ':'
520                                 || (buffer[index] >= '0' && buffer[index] <= '9')) {
521                         ipaddr[pos++] = buffer[index];
522                 } else if (buffer[index] == 'D') {
523                          THREAD_DBG("Socket read response buffer: Done");
524                          break;
525                 } else {
526                         ipaddr[pos] = '\0';
527                         if (pos > 0) {
528                                 THREAD_DBG("IP address: %s, length: %zu", ipaddr,
529                                                                 strlen(ipaddr));
530                                 callback(++count, ipaddr, ipaddr_type, user_data);
531                         }
532                         pos = 0;
533                 }
534                 index++;
535         }
536
537         FUNC_EXIT;
538         return ret;
539 }
540
541 static bool __is_valid_ipv6(const uint8_t* ipv6_address)
542 {
543         FUNC_ENTRY;
544
545         int index = 0;
546         while (index < THREAD_IPV6_ADDRESS_SIZE) {
547                 char buffer[THREAD_NETWORK_BUFFER_MAX];
548                 snprintf(buffer, THREAD_NETWORK_BUFFER_MAX, "%02x", ipv6_address[index]);
549                 if ((buffer[0] < '0' || buffer[0] > '9') &&
550                                 (buffer[0] < 'a' ||  buffer[0] > 'f'))
551                         return FALSE;
552                 if ((buffer[1] < '0' || buffer[1] > '9') &&
553                                 (buffer[1] < 'a' ||  buffer[1] > 'f'))
554                         return FALSE;
555
556                 index++;
557         }
558         THREAD_DBG("DEBUG: NETWORK MESSAGE -> return true");
559
560         FUNC_EXIT;
561         return TRUE;
562 }
563
564 int thread_add_ipaddr(thread_instance_h instance, const uint8_t *ipv6_address)
565 {
566         FUNC_ENTRY;
567         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
568         THREAD_CHECK_INIT_STATUS();
569         THREAD_VALIDATE_INPUT_PARAMETER(instance);
570         THREAD_VALIDATE_INPUT_PARAMETER(ipv6_address);
571
572         int ret = THREAD_ERROR_NONE;
573         char msg[THREAD_NETWORK_BUFFER_MAX];
574
575         retv_if(!__is_valid_ipv6(ipv6_address), THREAD_ERROR_INVALID_PARAMETER);
576
577         snprintf(msg, THREAD_NETWORK_BUFFER_MAX,
578                 "ipaddr add %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
579                 ipv6_address[0], ipv6_address[1], ipv6_address[2], ipv6_address[3],
580                 ipv6_address[4], ipv6_address[5], ipv6_address[6], ipv6_address[7],
581                 ipv6_address[8], ipv6_address[9], ipv6_address[10], ipv6_address[11],
582                 ipv6_address[12], ipv6_address[13], ipv6_address[14], ipv6_address[15]);
583
584         ret = _thread_socket_client_execute(_thread_get_socket_fd(), msg, strlen(msg));
585         retv_if(ret != THREAD_ERROR_NONE, ret);
586         THREAD_DBG("Successfully added address");
587
588         FUNC_EXIT;
589         return ret;
590 }
591
592 int thread_remove_ipaddr(thread_instance_h instance, const uint8_t *ipv6_address)
593 {
594         FUNC_ENTRY;
595         THREAD_CHECK_SUPPORTED_FEATURE(THREAD_FEATURE_COMMON);
596         THREAD_CHECK_INIT_STATUS();
597         THREAD_VALIDATE_INPUT_PARAMETER(instance);
598         THREAD_VALIDATE_INPUT_PARAMETER(ipv6_address);
599
600         int ret = THREAD_ERROR_NONE;
601         char msg[THREAD_NETWORK_BUFFER_MAX];
602
603         retv_if(!__is_valid_ipv6(ipv6_address), THREAD_ERROR_INVALID_PARAMETER);
604
605         snprintf(msg, THREAD_BORDER_ROUTER_BUFFER_MAX,
606                 "ipaddr del %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
607                 ipv6_address[0], ipv6_address[1], ipv6_address[2], ipv6_address[3],
608                 ipv6_address[4], ipv6_address[5], ipv6_address[6], ipv6_address[7],
609                 ipv6_address[8], ipv6_address[9], ipv6_address[10], ipv6_address[11],
610                 ipv6_address[12], ipv6_address[13], ipv6_address[14], ipv6_address[15]);
611
612         ret = _thread_socket_client_execute(_thread_get_socket_fd(), msg, strlen(msg));
613         retv_if(ret != THREAD_ERROR_NONE, ret);
614         THREAD_DBG("Successfully removed address");
615
616         FUNC_EXIT;
617         return ret;
618 }
619