1 /*
2 * Copyright (C) 2019 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * 4. Any redistribution, use, or modification is done solely for
17 * personal benefit and not for any commercial purpose or for
18 * monetary gain.
19 *
20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * Please inquire about commercial licensing options at
34 * [email protected]
35 *
36 */
37
38 #define BTSTACK_FILE__ "mesh_proxy.c"
39
40 #include "mesh/mesh_proxy.h"
41
42 #include <stdio.h>
43 #include <string.h>
44
45 #include "bluetooth_company_id.h"
46 #include "bluetooth_data_types.h"
47 #include "bluetooth_gatt.h"
48 #include "btstack_config.h"
49 #include "btstack_crypto.h"
50 #include "btstack_debug.h"
51 #include "btstack_memory.h"
52 #include "btstack_run_loop.h"
53 #include "btstack_util.h"
54
55 #include "mesh/adv_bearer.h"
56 #include "mesh/gatt_bearer.h"
57 #include "mesh/mesh_crypto.h"
58 #include "mesh/mesh_foundation.h"
59 #include "mesh/mesh_iv_index_seq_number.h"
60 #include "mesh/mesh_lower_transport.h"
61 #include "mesh/mesh_network.h"
62 #include "mesh/mesh_node.h"
63
64 static void mesh_proxy_start_advertising_with_node_id_next_subnet(void);
65 static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet);
66
67 #ifdef ENABLE_MESH_PROXY_SERVER
68
69 // we only support a single active node id advertisement. when new one is started, an active one is stopped
70
71 #define MESH_PROXY_NODE_ID_ADVERTISEMENT_TIMEOUT_MS 60000
72
73 static btstack_timer_source_t mesh_proxy_node_id_timer;
74 static btstack_crypto_random_t mesh_proxy_node_id_crypto_request_random;
75 static btstack_crypto_aes128_t mesh_proxy_node_id_crypto_request_aes128;
76 static uint8_t mesh_proxy_node_id_plaintext[16];
77 static uint8_t mesh_proxy_node_id_hash[16];
78 static uint8_t mesh_proxy_node_id_random_value[8];
79 static uint8_t mesh_proxy_node_id_all_subnets;
80
81 static uint16_t primary_element_address;
82
83 // Mesh Proxy, advertise unprovisioned device
84 static adv_bearer_connectable_advertisement_data_item_t connectable_advertisement_unprovisioned_device;
85
86 static const uint8_t adv_data_with_node_id_template[] = {
87 // Flags general discoverable, BR/EDR not supported
88 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
89 // 16-bit Service UUIDs
90 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
91 // Service Data
92 0x14, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
93 // MESH_IDENTIFICATION_NODE_IDENTIFY_TYPE
94 MESH_IDENTIFICATION_NODE_IDENTIFY_TYPE,
95 // Hash - 8 bytes
96 // Random - 8 bytes
97 };
98
99 static const uint8_t adv_data_with_network_id_template[] = {
100 // Flags general discoverable, BR/EDR not supported
101 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
102 // 16-bit Service UUIDs
103 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
104 // Service Data
105 0x0C, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
106 // MESH_IDENTIFICATION_NETWORK_ID_TYPE
107 MESH_IDENTIFICATION_NETWORK_ID_TYPE
108 };
109
110 #ifdef ENABLE_MESH_PB_GATT
111
112 // Mesh Provisioning
113 static const uint8_t adv_data_unprovisioned_template[] = {
114 // Flags general discoverable, BR/EDR not supported
115 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
116 // 16-bit Service UUIDs
117 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING >> 8,
118 // Service Data (22)
119 0x15, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING >> 8,
120 // UUID - 16 bytes
121 // OOB information - 2 bytes
122 };
123 const uint8_t adv_data_unprovisioned_template_len = sizeof(adv_data_unprovisioned_template);
124
mesh_proxy_setup_advertising_unprovisioned(adv_bearer_connectable_advertisement_data_item_t * advertisement_item,const uint8_t * device_uuid)125 static void mesh_proxy_setup_advertising_unprovisioned(adv_bearer_connectable_advertisement_data_item_t * advertisement_item, const uint8_t * device_uuid) {
126 // store in advertisement item
127 memset(advertisement_item, 0, sizeof(adv_bearer_connectable_advertisement_data_item_t));
128 advertisement_item->adv_length = adv_data_unprovisioned_template_len + 18;
129 (void)memcpy(advertisement_item->adv_data,
130 (uint8_t *)adv_data_unprovisioned_template,
131 adv_data_unprovisioned_template_len);
132 // dynamically store device uuid into adv data
133 (void)memcpy(&advertisement_item->adv_data[11], device_uuid, 16);
134 little_endian_store_16(advertisement_item->adv_data, 27, 0);
135 }
136
mesh_proxy_start_advertising_unprovisioned_device(void)137 void mesh_proxy_start_advertising_unprovisioned_device(void){
138 const uint8_t * device_uuid = mesh_node_get_device_uuid();
139 mesh_proxy_setup_advertising_unprovisioned(&connectable_advertisement_unprovisioned_device, device_uuid);
140 // setup advertisements
141 adv_bearer_advertisements_add_item(&connectable_advertisement_unprovisioned_device);
142 adv_bearer_advertisements_enable(1);
143 }
144
145 /**
146 * @brief Start Advertising Unprovisioned Device with Device ID
147 * @param device_uuid
148 */
mesh_proxy_stop_advertising_unprovisioned_device(void)149 void mesh_proxy_stop_advertising_unprovisioned_device(void){
150 adv_bearer_advertisements_remove_item(&connectable_advertisement_unprovisioned_device);
151 }
152
153 #endif
154
mesh_proxy_setup_advertising_with_network_id(uint8_t * buffer,uint8_t * network_id)155 static uint8_t mesh_proxy_setup_advertising_with_network_id(uint8_t * buffer, uint8_t * network_id){
156 (void)memcpy(&buffer[0], adv_data_with_network_id_template, 12);
157 (void)memcpy(&buffer[12], network_id, 8);
158 return 20;
159 }
160
mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet)161 static void mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
162 if (mesh_subnet->node_id_advertisement_running != 0){
163 adv_bearer_advertisements_remove_item(&mesh_subnet->advertisement_with_node_id);
164 mesh_subnet->node_id_advertisement_running = 0;
165 }
166 }
167
mesh_proxy_stop_all_advertising_with_node_id(void)168 static void mesh_proxy_stop_all_advertising_with_node_id(void){
169 mesh_subnet_iterator_t it;
170 mesh_subnet_iterator_init(&it);
171 while (mesh_subnet_iterator_has_more(&it)){
172 mesh_subnet_t * mesh_subnet = mesh_subnet_iterator_get_next(&it);
173 mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
174 }
175 btstack_run_loop_remove_timer(&mesh_proxy_node_id_timer);
176 mesh_proxy_node_id_all_subnets = 0;
177 }
178
mesh_proxy_node_id_timeout_handler(btstack_timer_source_t * ts)179 static void mesh_proxy_node_id_timeout_handler(btstack_timer_source_t * ts){
180 UNUSED(ts);
181 mesh_proxy_stop_all_advertising_with_node_id();
182 }
183
mesh_proxy_node_id_handle_get_aes128(void * arg)184 static void mesh_proxy_node_id_handle_get_aes128(void * arg){
185 mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
186
187 (void)memcpy(mesh_subnet->advertisement_with_node_id.adv_data,
188 adv_data_with_node_id_template, 12);
189 (void)memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[12],
190 &mesh_proxy_node_id_hash[8], 8);
191 (void)memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[20],
192 mesh_proxy_node_id_random_value, 8);
193 mesh_subnet->advertisement_with_node_id.adv_length = 28;
194
195 // setup advertisements
196 adv_bearer_advertisements_add_item(&mesh_subnet->advertisement_with_node_id);
197 adv_bearer_advertisements_enable(1);
198
199 // set timer
200 btstack_run_loop_set_timer_handler(&mesh_proxy_node_id_timer, mesh_proxy_node_id_timeout_handler);
201 btstack_run_loop_set_timer(&mesh_proxy_node_id_timer, MESH_PROXY_NODE_ID_ADVERTISEMENT_TIMEOUT_MS);
202 btstack_run_loop_add_timer(&mesh_proxy_node_id_timer);
203
204 // mark as active
205 mesh_subnet->node_id_advertisement_running = 1;
206
207 // next one
208 if (mesh_proxy_node_id_all_subnets == 0) return;
209 mesh_proxy_start_advertising_with_node_id_next_subnet();
210 }
211
mesh_proxy_node_id_handle_random(void * arg)212 static void mesh_proxy_node_id_handle_random(void * arg){
213 mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
214
215 // Hash = e(IdentityKey, Padding | Random | Address) mod 2^64
216 memset(mesh_proxy_node_id_plaintext, 0, sizeof(mesh_proxy_node_id_plaintext));
217 (void)memcpy(&mesh_proxy_node_id_plaintext[6],
218 mesh_proxy_node_id_random_value, 8);
219 big_endian_store_16(mesh_proxy_node_id_plaintext, 14, primary_element_address);
220 // TODO: old vs. new key
221 btstack_crypto_aes128_encrypt(&mesh_proxy_node_id_crypto_request_aes128, mesh_subnet->old_key->identity_key, mesh_proxy_node_id_plaintext, mesh_proxy_node_id_hash, mesh_proxy_node_id_handle_get_aes128, mesh_subnet);
222 }
223
mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet)224 static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
225 if (mesh_subnet->node_id_advertisement_running) return;
226 log_info("Proxy start advertising with node id, netkey index %04x", mesh_subnet->netkey_index);
227 // setup node id
228 btstack_crypto_random_generate(&mesh_proxy_node_id_crypto_request_random, mesh_proxy_node_id_random_value, sizeof(mesh_proxy_node_id_random_value), mesh_proxy_node_id_handle_random, mesh_subnet);
229 }
230
231 // Public API
232
mesh_proxy_get_advertising_with_node_id_status(uint16_t netkey_index,mesh_node_identity_state_t * out_state)233 uint8_t mesh_proxy_get_advertising_with_node_id_status(uint16_t netkey_index, mesh_node_identity_state_t * out_state ){
234 mesh_subnet_t * mesh_subnet = mesh_subnet_get_by_netkey_index(netkey_index);
235 if (mesh_subnet == NULL){
236 *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED;
237 return MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
238 }
239
240 #ifdef ENABLE_MESH_PROXY_SERVER
241 if (mesh_subnet->node_id_advertisement_running == 0){
242 *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED;
243 } else {
244 *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING;
245 }
246 #else
247 *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_NOT_SUPPORTED;
248 #endif
249
250 return MESH_FOUNDATION_STATUS_SUCCESS;
251 }
252
mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index,mesh_node_identity_state_t state)253 uint8_t mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index, mesh_node_identity_state_t state){
254 mesh_subnet_t * mesh_subnet = mesh_subnet_get_by_netkey_index(netkey_index);
255 if (mesh_subnet == NULL){
256 return MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
257 }
258
259 #ifdef ENABLE_MESH_PROXY_SERVER
260 switch (state){
261 case MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED:
262 mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
263 return MESH_FOUNDATION_STATUS_SUCCESS;
264 case MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING:
265 mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet);
266 return MESH_FOUNDATION_STATUS_SUCCESS;
267 default:
268 break;
269 }
270 #endif
271
272 return MESH_FOUNDATION_STATUS_FEATURE_NOT_SUPPORTED;
273 }
274
mesh_proxy_start_advertising_with_node_id_next_subnet(void)275 static void mesh_proxy_start_advertising_with_node_id_next_subnet(void){
276 mesh_subnet_iterator_t it;
277 mesh_subnet_iterator_init(&it);
278 while (mesh_subnet_iterator_has_more(&it)){
279 mesh_subnet_t * subnet = mesh_subnet_iterator_get_next(&it);
280 if (subnet->node_id_advertisement_running != 0) continue;
281
282 mesh_proxy_start_advertising_with_node_id_for_subnet(subnet);
283 return;
284 }
285 }
286
mesh_proxy_start_advertising_with_node_id(void)287 void mesh_proxy_start_advertising_with_node_id(void){
288 mesh_proxy_node_id_all_subnets = 1;
289 adv_bearer_advertisements_enable(1);
290
291 // start advertising on first subnet that is not already advertising with node id
292 mesh_proxy_start_advertising_with_node_id_next_subnet();
293 }
294
mesh_proxy_stop_advertising_with_node_id(void)295 void mesh_proxy_stop_advertising_with_node_id(void){
296 mesh_proxy_stop_all_advertising_with_node_id();
297 }
298
mesh_proxy_start_advertising_with_network_id(void)299 void mesh_proxy_start_advertising_with_network_id(void){
300 mesh_subnet_iterator_t it;
301 mesh_subnet_iterator_init(&it);
302 while (mesh_subnet_iterator_has_more(&it)){
303 mesh_subnet_t * subnet = mesh_subnet_iterator_get_next(&it);
304 log_info("Proxy start advertising with network id, netkey index %04x", subnet->netkey_index);
305 // setup advertisement with network id (used by proxy)
306 mesh_network_key_t * network_key = mesh_subnet_get_outgoing_network_key(subnet);
307 subnet->advertisement_with_network_id.adv_length = mesh_proxy_setup_advertising_with_network_id(subnet->advertisement_with_network_id.adv_data, network_key->network_id);
308 adv_bearer_advertisements_add_item(&subnet->advertisement_with_network_id);
309 }
310 adv_bearer_advertisements_enable(1);
311 }
312
mesh_proxy_stop_advertising_with_network_id(void)313 void mesh_proxy_stop_advertising_with_network_id(void){
314 mesh_subnet_iterator_t it;
315 mesh_subnet_iterator_init(&it);
316 while (mesh_subnet_iterator_has_more(&it)){
317 mesh_subnet_t * network_key = mesh_subnet_iterator_get_next(&it);
318 log_info("Proxy stop advertising with network id, netkey index %04x", network_key->netkey_index);
319 adv_bearer_advertisements_remove_item(&network_key->advertisement_with_network_id);
320 }
321 }
322
323 // Mesh Proxy Configuration
324
325 typedef enum {
326 MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_SET_FILTER_TYPE = 0,
327 MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_ADD_ADDRESSES,
328 MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_REMOVE_ADDRESSES,
329 MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_FILTER_STATUS
330 } mesh_proxy_configuration_message_opcode_t;
331
332 typedef enum {
333 MESH_PROXY_CONFIGURATION_FILTER_TYPE_SET_WHITE_LIST = 0,
334 MESH_PROXY_CONFIGURATION_FILTER_TYPE_BLACK_LIST
335 } mesh_proxy_configuration_filter_type_t;
336
337 static mesh_network_pdu_t * encrypted_proxy_configuration_ready_to_send;
338
339 // Used to answer configuration request
340 static uint16_t proxy_configuration_filter_list_len;
341 static mesh_proxy_configuration_filter_type_t proxy_configuration_filter_type;
342
request_can_send_now_proxy_configuration_callback_handler(mesh_network_pdu_t * network_pdu)343 static void request_can_send_now_proxy_configuration_callback_handler(mesh_network_pdu_t * network_pdu){
344 encrypted_proxy_configuration_ready_to_send = network_pdu;
345 gatt_bearer_request_can_send_now_for_mesh_proxy_configuration();
346 }
347
proxy_configuration_message_handler(mesh_network_callback_type_t callback_type,mesh_network_pdu_t * received_network_pdu)348 static void proxy_configuration_message_handler(mesh_network_callback_type_t callback_type, mesh_network_pdu_t * received_network_pdu){
349 mesh_proxy_configuration_message_opcode_t opcode;
350 uint8_t data[4];
351 mesh_network_pdu_t * network_pdu;
352 uint8_t * network_pdu_data;
353
354 switch (callback_type){
355 case MESH_NETWORK_PDU_RECEIVED:
356 printf("proxy_configuration_message_handler: MESH_PROXY_PDU_RECEIVED\n");
357 network_pdu_data = mesh_network_pdu_data(received_network_pdu);
358 opcode = network_pdu_data[0];
359 switch (opcode){
360 case MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_SET_FILTER_TYPE:{
361 switch ((mesh_proxy_configuration_filter_type_t) network_pdu_data[1]){
362 case MESH_PROXY_CONFIGURATION_FILTER_TYPE_SET_WHITE_LIST:
363 case MESH_PROXY_CONFIGURATION_FILTER_TYPE_BLACK_LIST:
364 proxy_configuration_filter_type = network_pdu_data[1];
365 break;
366 default:
367 // invalid filter type, ignore
368 btstack_memory_mesh_network_pdu_free(received_network_pdu);
369 return;
370 }
371
372 uint8_t ctl = 1;
373 uint8_t ttl = 0;
374 uint16_t src = primary_element_address;
375 uint16_t dest = 0; // unassigned address
376 uint32_t seq = mesh_sequence_number_next();
377 uint8_t nid = mesh_network_nid(received_network_pdu);
378 uint16_t netkey_index = received_network_pdu->netkey_index;
379 printf("netkey index 0x%02x\n", netkey_index);
380
381 network_pdu = btstack_memory_mesh_network_pdu_get();
382 int pos = 0;
383 data[pos++] = MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_FILTER_STATUS;
384 data[pos++] = proxy_configuration_filter_type;
385 big_endian_store_16(data, pos, proxy_configuration_filter_list_len);
386
387 mesh_network_setup_pdu(network_pdu, netkey_index, nid, ctl, ttl, seq, src, dest, data, sizeof(data));
388 mesh_network_encrypt_proxy_configuration_message(network_pdu);
389
390 // received_network_pdu is processed
391 btstack_memory_mesh_network_pdu_free(received_network_pdu);
392 break;
393 }
394 default:
395 printf("proxy config not implemented, opcode %d\n", opcode);
396 btstack_memory_mesh_network_pdu_free(received_network_pdu);
397 break;
398 }
399 break;
400 case MESH_NETWORK_CAN_SEND_NOW:
401 printf("MESH_SUBEVENT_CAN_SEND_NOW mesh_netework_gatt_bearer_handle_proxy_configuration len %d\n", encrypted_proxy_configuration_ready_to_send->len);
402 printf_hexdump(encrypted_proxy_configuration_ready_to_send->data, encrypted_proxy_configuration_ready_to_send->len);
403 gatt_bearer_send_mesh_proxy_configuration(encrypted_proxy_configuration_ready_to_send->data, encrypted_proxy_configuration_ready_to_send->len);
404 break;
405 case MESH_NETWORK_PDU_SENT:
406 // printf("test MESH_PROXY_PDU_SENT\n");
407 // mesh_lower_transport_received_mesage(MESH_NETWORK_PDU_SENT, network_pdu);
408 break;
409 case MESH_NETWORK_PDU_ENCRYPTED:
410 request_can_send_now_proxy_configuration_callback_handler(received_network_pdu);
411 break;
412 default:
413 break;
414 }
415 }
416
mesh_proxy_init(uint16_t primary_unicast_address)417 void mesh_proxy_init(uint16_t primary_unicast_address){
418 primary_element_address = primary_unicast_address;
419
420 // mesh proxy configuration
421 mesh_network_set_proxy_message_handler(proxy_configuration_message_handler);
422 }
423 #endif
424
425