xref: /btstack/src/mesh/mesh_configuration_server.c (revision 818629960d27e317b8115fb513912a06dbf88524)
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 MATTHIAS
24  * RINGWALD 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_configuration_server.c"
39 
40 #include <string.h>
41 #include <stdio.h>
42 
43 #include "mesh/mesh_configuration_server.h"
44 
45 #include "bluetooth_company_id.h"
46 #include "btstack_debug.h"
47 #include "btstack_memory.h"
48 #include "btstack_tlv.h"
49 #include "btstack_util.h"
50 
51 #include "mesh/beacon.h"
52 #include "mesh/gatt_bearer.h"
53 #include "mesh/mesh.h"
54 #include "mesh/mesh_access.h"
55 #include "mesh/mesh_crypto.h"
56 #include "mesh/mesh_foundation.h"
57 #include "mesh/mesh_iv_index_seq_number.h"
58 #include "mesh/mesh_keys.h"
59 #include "mesh/mesh_network.h"
60 #include "mesh/mesh_node.h"
61 #include "mesh/mesh_proxy.h"
62 #include "mesh/mesh_upper_transport.h"
63 #include "mesh/mesh_virtual_addresses.h"
64 
65 #define MESH_HEARTBEAT_FEATURES_SUPPORTED_MASK 0x000f
66 
67 // current access pdu
68 static mesh_pdu_t * access_pdu_in_process;
69 
70 // data from current pdu
71 static uint16_t                     configuration_server_element_address;
72 static uint32_t                     configuration_server_model_identifier;
73 static mesh_model_t               * configuration_server_target_model;
74 static mesh_publication_model_t     configuration_server_publication_model;
75 
76 // cmac for virtual address hash and netkey derive
77 static btstack_crypto_aes128_cmac_t configuration_server_cmac_request;
78 
79 // used to setup virtual addresses
80 static uint8_t                      configuration_server_label_uuid[16];
81 static uint16_t                     configuration_server_hash;
82 
83 // heartbeat publication and subscription state for all Configuration Server models - there is only one
84 // static mesh_heartbeat_subscription_t mesh_heartbeat_subscription;
85 
86 // for PTS testing
87 static int config_netkey_list_max = 0;
88 
89 
90 // Heartbeat (helper)
91 static uint16_t heartbeat_pwr2(uint8_t value){
92     if (value == 0 )                    return 0x0000;
93     if (value == 0xff || value == 0x11) return 0xffff;
94     return 1 << (value-1);
95 }
96 
97 static uint8_t heartbeat_count_log(uint16_t value){
98     if (value == 0)      return 0x00;
99     if (value == 0xffff) return 0xff;
100     // count leading zeros, supported by clang and gcc
101     // note: CountLog(8) == CountLog(7) = 3
102     return 33 - __builtin_clz(value - 1);
103 }
104 
105 static uint8_t heartbeat_period_log(uint16_t value){
106     if (value == 0)      return 0x00;
107     // count leading zeros, supported by clang and gcc
108     // note: PeriodLog(8) == PeriodLog(7) = 3
109     return 33 - __builtin_clz(value - 1);
110 }
111 
112 // TLV
113 
114 static int mesh_model_is_configuration_server(uint32_t model_identifier){
115     return mesh_model_is_bluetooth_sig(model_identifier) && (mesh_model_get_model_id(model_identifier) == MESH_SIG_MODEL_ID_CONFIGURATION_SERVER);
116 }
117 
118 // Configuration Model Subscriptions (helper)
119 
120 // Model to Appkey List
121 
122 static uint8_t mesh_model_add_subscription(mesh_model_t * mesh_model, uint16_t address){
123     uint16_t i;
124     for (i=0;i<MAX_NR_MESH_SUBSCRIPTION_PER_MODEL;i++){
125         if (mesh_model->subscriptions[i] == address) return MESH_FOUNDATION_STATUS_SUCCESS;
126     }
127     for (i=0;i<MAX_NR_MESH_SUBSCRIPTION_PER_MODEL;i++){
128         if (mesh_model->subscriptions[i] == MESH_ADDRESS_UNSASSIGNED) {
129             mesh_model->subscriptions[i] = address;
130             return MESH_FOUNDATION_STATUS_SUCCESS;
131         }
132     }
133     return MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
134 }
135 
136 static void mesh_model_delete_subscription(mesh_model_t * mesh_model, uint16_t address){
137     uint16_t i;
138     for (i=0;i<MAX_NR_MESH_SUBSCRIPTION_PER_MODEL;i++){
139         if (mesh_model->subscriptions[i] == address) {
140             mesh_model->subscriptions[i] = MESH_ADDRESS_UNSASSIGNED;
141         }
142     }
143 }
144 
145 static void mesh_model_delete_all_subscriptions(mesh_model_t * mesh_model){
146     uint16_t i;
147     for (i=0;i<MAX_NR_MESH_SUBSCRIPTION_PER_MODEL;i++){
148         mesh_model->subscriptions[i] = MESH_ADDRESS_UNSASSIGNED;
149     }
150 }
151 
152 static void mesh_subcription_decrease_virtual_address_ref_count(mesh_model_t *mesh_model){
153     // decrease ref counts for current virtual subscriptions
154     uint16_t i;
155     for (i = 0; i<MAX_NR_MESH_SUBSCRIPTION_PER_MODEL ; i++){
156         uint16_t src = mesh_model->subscriptions[i];
157         if (mesh_network_address_virtual(src)){
158             mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_pseudo_dst(src);
159             mesh_virtual_address_decrease_refcount(virtual_address);
160         }
161     }
162 }
163 static void mesh_configuration_server_stop_publishing_using_appkey(mesh_model_t * mesh_model, uint16_t appkey_index){
164     // stop publishing if this AppKey was used
165     if (mesh_model->publication_model != NULL){
166         mesh_publication_model_t * publication_model = mesh_model->publication_model;
167         if (publication_model->appkey_index == appkey_index){
168             publication_model->address = MESH_ADDRESS_UNSASSIGNED;
169             publication_model->appkey_index = 0;
170             publication_model->period = 0;
171             publication_model->ttl = 0;
172             publication_model->retransmit = 0;
173 
174             mesh_model_store_publication(mesh_model);
175         }
176     }
177 }
178 
179 // AppKeys Helper
180 static void mesh_configuration_server_delete_appkey(mesh_transport_key_t * transport_key){
181     uint16_t appkey_index = transport_key->appkey_index;
182 
183     // iterate over elements and models
184     mesh_element_iterator_t element_it;
185     mesh_element_iterator_init(&element_it);
186     while (mesh_element_iterator_has_next(&element_it)){
187         mesh_element_t * element = mesh_element_iterator_next(&element_it);
188         mesh_model_iterator_t model_it;
189         mesh_model_iterator_init(&model_it, element);
190         while (mesh_model_iterator_has_next(&model_it)){
191             mesh_model_t * mesh_model = mesh_model_iterator_next(&model_it);
192 
193             // remove from Model to AppKey List
194             mesh_model_unbind_appkey(mesh_model, appkey_index);
195 
196             // stop publishing if this appkey is used
197             mesh_configuration_server_stop_publishing_using_appkey(mesh_model, appkey_index);
198         }
199     }
200 
201     mesh_access_appkey_finalize(transport_key);
202 }
203 
204 // Foundatiopn Message
205 
206 const mesh_access_message_t mesh_foundation_config_beacon_status = {
207         MESH_FOUNDATION_OPERATION_BEACON_STATUS, "1"
208 };
209 const mesh_access_message_t mesh_foundation_config_default_ttl_status = {
210         MESH_FOUNDATION_OPERATION_DEFAULT_TTL_STATUS, "1"
211 };
212 const mesh_access_message_t mesh_foundation_config_friend_status = {
213         MESH_FOUNDATION_OPERATION_FRIEND_STATUS, "1"
214 };
215 const mesh_access_message_t mesh_foundation_config_gatt_proxy_status = {
216         MESH_FOUNDATION_OPERATION_GATT_PROXY_STATUS, "1"
217 };
218 const mesh_access_message_t mesh_foundation_config_relay_status = {
219         MESH_FOUNDATION_OPERATION_RELAY_STATUS, "11"
220 };
221 const mesh_access_message_t mesh_foundation_config_model_publication_status = {
222         MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_STATUS, "1222111m"
223 };
224 const mesh_access_message_t mesh_foundation_config_model_subscription_status = {
225         MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_STATUS, "122m"
226 };
227 const mesh_access_message_t mesh_foundation_config_netkey_status = {
228         MESH_FOUNDATION_OPERATION_NETKEY_STATUS, "12"
229 };
230 const mesh_access_message_t mesh_foundation_config_appkey_status = {
231         MESH_FOUNDATION_OPERATION_APPKEY_STATUS, "13"
232 };
233 const mesh_access_message_t mesh_foundation_config_model_app_status = {
234         MESH_FOUNDATION_OPERATION_MODEL_APP_STATUS, "122m"
235 };
236 const mesh_access_message_t mesh_foundation_node_reset_status = {
237         MESH_FOUNDATION_OPERATION_NODE_RESET_STATUS, ""
238 };
239 const mesh_access_message_t mesh_foundation_config_heartbeat_publication_status = {
240         MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_STATUS, "1211122"
241 };
242 const mesh_access_message_t mesh_foundation_config_network_transmit_status = {
243         MESH_FOUNDATION_OPERATION_NETWORK_TRANSMIT_STATUS, "1"
244 };
245 const mesh_access_message_t mesh_foundation_node_identity_status = {
246         MESH_FOUNDATION_OPERATION_NODE_IDENTITY_STATUS, "121"
247 };
248 const mesh_access_message_t mesh_key_refresh_phase_status = {
249         MESH_FOUNDATION_OPERATION_KEY_REFRESH_PHASE_STATUS, "121"
250 };
251 const mesh_access_message_t mesh_foundation_low_power_node_poll_timeout_status = {
252         MESH_FOUNDATION_OPERATION_LOW_POWER_NODE_POLL_TIMEOUT_STATUS, "23"
253 };
254 const mesh_access_message_t mesh_foundation_config_heartbeat_subscription_status = {
255         MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_STATUS, "1221111"
256 };
257 
258 static void config_server_send_message(uint16_t netkey_index, uint16_t dest, mesh_pdu_t *pdu){
259     // Configuration Server is on primary element and can only use DeviceKey
260     uint16_t appkey_index = MESH_DEVICE_KEY_INDEX;
261     uint16_t src          = mesh_node_get_primary_element_address();
262     uint8_t  ttl          = mesh_foundation_default_ttl_get();
263     mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0);
264     mesh_access_send_unacknowledged_pdu(pdu);
265 }
266 
267 static void config_composition_data_status(uint16_t netkey_index, uint16_t dest){
268 
269     printf("Received Config Composition Data Get -> send Config Composition Data Status\n");
270 
271     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(MESH_FOUNDATION_OPERATION_COMPOSITION_DATA_STATUS);
272     if (!transport_pdu) return;
273 
274     // page 0
275     mesh_access_transport_add_uint8(transport_pdu, 0);
276 
277     // CID
278     mesh_access_transport_add_uint16(transport_pdu, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH);
279     // PID
280     mesh_access_transport_add_uint16(transport_pdu, 0);
281     // VID
282     mesh_access_transport_add_uint16(transport_pdu, 0);
283     // CRPL - number of protection list entries
284     mesh_access_transport_add_uint16(transport_pdu, 1);
285     // Features - Relay, Proxy, Friend, Lower Power, ...
286     mesh_access_transport_add_uint16(transport_pdu, 0);
287 
288     mesh_element_iterator_t element_it;
289     mesh_element_iterator_init(&element_it);
290     while (mesh_element_iterator_has_next(&element_it)){
291         mesh_element_t * element = mesh_element_iterator_next(&element_it);
292 
293         // Loc
294         mesh_access_transport_add_uint16(transport_pdu, element->loc);
295         // NumS
296         mesh_access_transport_add_uint8( transport_pdu, element->models_count_sig);
297         // NumV
298         mesh_access_transport_add_uint8( transport_pdu, element->models_count_vendor);
299 
300         mesh_model_iterator_t model_it;
301 
302         // SIG Models
303         mesh_model_iterator_init(&model_it, element);
304         while (mesh_model_iterator_has_next(&model_it)){
305             mesh_model_t * model = mesh_model_iterator_next(&model_it);
306             if (!mesh_model_is_bluetooth_sig(model->model_identifier)) continue;
307             mesh_access_transport_add_uint16(transport_pdu, model->model_identifier);
308         }
309         // Vendor Models
310         mesh_model_iterator_init(&model_it, element);
311         while (mesh_model_iterator_has_next(&model_it)){
312             mesh_model_t * model = mesh_model_iterator_next(&model_it);
313             if (mesh_model_is_bluetooth_sig(model->model_identifier)) continue;
314             mesh_access_transport_add_uint32(transport_pdu, model->model_identifier);
315         }
316     }
317 
318     // send as segmented access pdu
319     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
320 }
321 
322 static void config_composition_data_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
323     UNUSED(mesh_model);
324     config_composition_data_status(mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
325 
326     mesh_access_message_processed(pdu);
327 }
328 
329 static void config_model_beacon_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
330     UNUSED(mesh_model);
331     // setup message
332     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_beacon_status,
333                                                                                mesh_foundation_beacon_get());
334     if (!transport_pdu) return;
335 
336     // send as segmented access pdu
337     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
338 }
339 
340 static void config_beacon_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
341     config_model_beacon_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
342 
343     mesh_access_message_processed(pdu);
344 }
345 
346 static void config_beacon_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
347     mesh_access_parser_state_t parser;
348     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
349 
350     uint8_t beacon_enabled = mesh_access_parser_get_u8(&parser);
351 
352     // beacon valid
353     if (beacon_enabled < MESH_FOUNDATION_STATE_NOT_SUPPORTED) {
354         // set and store new value
355         mesh_foundation_beacon_set(beacon_enabled);
356         mesh_foundation_state_store();
357 
358         // send status
359         config_model_beacon_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
360     }
361 
362     mesh_access_message_processed(pdu);
363 }
364 
365 static void config_model_default_ttl_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
366     UNUSED(mesh_model);
367     // setup message
368     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
369             &mesh_foundation_config_default_ttl_status, mesh_foundation_default_ttl_get());
370     if (!transport_pdu) return;
371 
372     // send as segmented access pdu
373     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
374 }
375 
376 static void config_default_ttl_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
377     config_model_default_ttl_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
378 
379     mesh_access_message_processed(pdu);
380 }
381 
382 static void config_default_ttl_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
383     mesh_access_parser_state_t parser;
384     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
385 
386     uint8_t new_ttl = mesh_access_parser_get_u8(&parser);
387 
388     // validate (0x01 and > 0x7f are prohibited)
389     if (new_ttl <= 0x7f && new_ttl != 0x01) {
390 
391         // set and store
392         mesh_foundation_default_ttl_set(new_ttl);
393         mesh_foundation_state_store();
394 
395         // send status
396         config_model_default_ttl_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
397     }
398 
399     mesh_access_message_processed(pdu);
400 }
401 
402 static void config_friend_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
403     UNUSED(mesh_model);
404 
405     // setup message
406     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
407             &mesh_foundation_config_friend_status, mesh_foundation_friend_get());
408     if (!transport_pdu) return;
409 
410     // send as segmented access pdu
411     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
412 }
413 
414 static void config_friend_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
415     config_friend_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
416 
417     mesh_access_message_processed(pdu);
418 }
419 
420 static void config_friend_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
421     mesh_access_parser_state_t parser;
422     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
423 
424     uint8_t new_friend_state = mesh_access_parser_get_u8(&parser);
425 
426     // validate
427     if (new_friend_state < MESH_FOUNDATION_STATE_NOT_SUPPORTED) {
428 
429         // set and store
430         if (mesh_foundation_friend_get() != MESH_FOUNDATION_STATE_NOT_SUPPORTED){
431             mesh_foundation_friend_set(new_friend_state);
432             mesh_foundation_state_store();
433         }
434 
435         // send status
436         config_friend_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
437     }
438 
439     mesh_access_message_processed(pdu);
440 }
441 
442 static void config_model_gatt_proxy_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
443     UNUSED(mesh_model);
444     // setup message
445     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_gatt_proxy_status, mesh_foundation_gatt_proxy_get());
446     if (!transport_pdu) return;
447 
448     // send as segmented access pdu
449     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
450 }
451 
452 static void config_gatt_proxy_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
453     config_model_gatt_proxy_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
454 
455     mesh_access_message_processed(pdu);
456 }
457 
458 static void config_gatt_proxy_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
459     mesh_access_parser_state_t parser;
460     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
461 
462     uint8_t enabled = mesh_access_parser_get_u8(&parser);
463 
464     // validate
465     if (enabled <  MESH_FOUNDATION_STATE_NOT_SUPPORTED) {
466         // set and store
467         mesh_foundation_gatt_proxy_set(enabled);
468         mesh_foundation_state_store();
469 
470         // send status
471         config_model_gatt_proxy_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
472     }
473 
474     mesh_access_message_processed(pdu);
475 
476     // trigger heartbeat emit on change
477     mesh_configuration_server_feature_changed();
478 }
479 
480 static void config_model_relay_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
481     UNUSED(mesh_model);
482 
483     // setup message
484     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_relay_status,
485                                                                                mesh_foundation_relay_get(),
486                                                                                mesh_foundation_relay_retransmit_get());
487     if (!transport_pdu) return;
488 
489     // send as segmented access pdu
490     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
491 }
492 
493 static void config_relay_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
494     config_model_relay_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
495 
496     mesh_access_message_processed(pdu);
497 }
498 
499 static void config_relay_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
500 
501     mesh_access_parser_state_t parser;
502     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
503 
504     // check if valid
505     uint8_t relay            = mesh_access_parser_get_u8(&parser);
506     uint8_t relay_retransmit = mesh_access_parser_get_u8(&parser);
507 
508     // check if valid
509     if (relay <= 1) {
510         // only update if supported
511         if (mesh_foundation_relay_get() != MESH_FOUNDATION_STATE_NOT_SUPPORTED){
512             mesh_foundation_relay_set(relay);
513             mesh_foundation_relay_retransmit_set(relay_retransmit);
514             mesh_foundation_state_store();
515         }
516 
517         // send status
518         config_model_relay_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
519     }
520 
521     mesh_access_message_processed(pdu);
522 
523     // trigger heartbeat emit on change
524     mesh_configuration_server_feature_changed();
525 }
526 
527 static void config_model_network_transmit_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest){
528     UNUSED(mesh_model);
529 
530     // setup message
531     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
532             &mesh_foundation_config_network_transmit_status, mesh_foundation_network_transmit_get());
533     if (!transport_pdu) return;
534 
535     // send as segmented access pdu
536     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
537 }
538 
539 static void config_model_network_transmit_get_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
540     config_model_network_transmit_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
541 
542     mesh_access_message_processed(pdu);
543 }
544 
545 static void config_model_network_transmit_set_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
546     mesh_access_parser_state_t parser;
547     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
548 
549     uint8_t new_ttl = mesh_access_parser_get_u8(&parser);
550 
551     // store
552     mesh_foundation_network_transmit_set(new_ttl);
553     mesh_foundation_state_store();
554 
555     //
556     config_model_network_transmit_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
557 
558     mesh_access_message_processed(pdu);
559 }
560 
561 // NetKey List
562 
563 void config_nekey_list_set_max(uint16_t max){
564     config_netkey_list_max = max;
565 }
566 
567 static void config_netkey_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, uint16_t new_netkey_index){
568     UNUSED(mesh_model);
569 
570     // setup message
571     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
572             &mesh_foundation_config_netkey_status, status, new_netkey_index);
573     if (!transport_pdu) return;
574 
575     // send as segmented access pdu
576     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
577 }
578 
579 static void config_netkey_list(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest) {
580     UNUSED(mesh_model);
581 
582     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(MESH_FOUNDATION_OPERATION_NETKEY_LIST);
583     if (!transport_pdu) return;
584 
585     // add list of netkey indexes
586     mesh_network_key_iterator_t it;
587     mesh_network_key_iterator_init(&it);
588     while (mesh_network_key_iterator_has_more(&it)){
589         mesh_network_key_t * network_key = mesh_network_key_iterator_get_next(&it);
590         mesh_access_transport_add_uint16(transport_pdu, network_key->netkey_index);
591     }
592 
593     // send as segmented access pdu
594     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
595 }
596 
597 static void config_netkey_add_derived(void * arg){
598     mesh_subnet_t * subnet = (mesh_subnet_t *) arg;
599 
600     // store network key
601     mesh_store_network_key(subnet->old_key);
602 
603     // add key to NetKey List
604     mesh_network_key_add(subnet->old_key);
605 
606     // add subnet
607     mesh_subnet_add(subnet);
608 
609 #ifdef ENABLE_MESH_PROXY_SERVER
610     mesh_proxy_start_advertising_with_network_id();
611 #endif
612 
613     config_netkey_status(mesh_model_get_configuration_server(), mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), MESH_FOUNDATION_STATUS_SUCCESS, subnet->netkey_index);
614     mesh_access_message_processed(access_pdu_in_process);
615 }
616 
617 static void config_netkey_add_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
618     mesh_access_parser_state_t parser;
619     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
620 
621     // get params
622     uint8_t new_netkey[16];
623     uint16_t new_netkey_index = mesh_access_parser_get_u16(&parser);
624     mesh_access_parser_get_key(&parser, new_netkey);
625 
626     uint8_t status;
627 
628     const mesh_subnet_t * existing_subnet = mesh_subnet_get_by_netkey_index(new_netkey_index);
629     if (existing_subnet == NULL){
630 
631         // check limit for pts
632         uint16_t internal_index = mesh_network_key_get_free_index();
633         if (internal_index == MESH_KEYS_INVALID_INDEX || (config_netkey_list_max && mesh_network_key_list_count() >= config_netkey_list_max)){
634             status = MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
635         } else {
636 
637             // allocate new key and subnet
638             mesh_network_key_t * new_network_key = btstack_memory_mesh_network_key_get();
639             mesh_subnet_t * new_subnet = btstack_memory_mesh_subnet_get();
640 
641             if (new_network_key == NULL || new_subnet == NULL){
642                 if (new_network_key != NULL){
643                     btstack_memory_mesh_network_key_free(new_network_key);
644                 }
645                 if (new_subnet != NULL){
646                     btstack_memory_mesh_subnet_free(new_subnet);
647                 }
648                 status = MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
649 
650             } else {
651 
652                 // setup key
653                 new_network_key->internal_index = internal_index;
654                 new_network_key->netkey_index   = new_netkey_index;
655                 memcpy(new_network_key->net_key, new_netkey, 16);
656 
657                 // setup subnet
658                 new_subnet->old_key = new_network_key;
659                 new_subnet->netkey_index = new_netkey_index;
660                 new_subnet->key_refresh = MESH_KEY_REFRESH_NOT_ACTIVE;
661 
662                 // derive other keys
663                 access_pdu_in_process = pdu;
664                 mesh_network_key_derive(&configuration_server_cmac_request, new_network_key, config_netkey_add_derived, new_subnet);
665                 return;
666             }
667         }
668 
669     } else {
670         // network key for netkey index already exists
671         if (memcmp(existing_subnet->old_key->net_key, new_netkey, 16) == 0){
672             // same netkey
673             status = MESH_FOUNDATION_STATUS_SUCCESS;
674         } else {
675             // different netkey
676             status = MESH_FOUNDATION_STATUS_KEY_INDEX_ALREADY_STORED;
677         }
678     }
679 
680     // report status
681     config_netkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, new_netkey_index);
682     mesh_access_message_processed(pdu);
683 }
684 
685 static void config_netkey_update_derived(void * arg){
686     mesh_subnet_t * subnet = (mesh_subnet_t *) arg;
687 
688     // store network key
689     mesh_store_network_key(subnet->new_key);
690 
691     // add key to NetKey List
692     mesh_network_key_add(subnet->new_key);
693 
694     // update subnet - Key Refresh Phase 1
695     subnet->key_refresh = MESH_KEY_REFRESH_FIRST_PHASE;
696 
697     // report status
698     config_netkey_status(mesh_model_get_configuration_server(), mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), MESH_FOUNDATION_STATUS_SUCCESS, subnet->netkey_index);
699     mesh_access_message_processed(access_pdu_in_process);
700 }
701 
702 static void config_netkey_update_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu) {
703     mesh_access_parser_state_t parser;
704     mesh_access_parser_init(&parser, (mesh_pdu_t *) pdu);
705 
706     // get params
707     uint8_t new_netkey[16];
708     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
709     mesh_access_parser_get_key(&parser, new_netkey);
710 
711     // get existing subnet
712     mesh_subnet_t * subnet = mesh_subnet_get_by_netkey_index(netkey_index);
713     if (subnet == NULL){
714         config_netkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX, netkey_index);
715         mesh_access_message_processed(access_pdu_in_process);
716         return;
717     }
718 
719     // get index for new key
720     uint16_t internal_index = mesh_network_key_get_free_index();
721     if (internal_index == MESH_KEYS_INVALID_INDEX){
722         config_netkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES, netkey_index);
723         mesh_access_message_processed(access_pdu_in_process);
724         return;
725     }
726 
727     // get new key
728     mesh_network_key_t * new_network_key = btstack_memory_mesh_network_key_get();
729     if (new_network_key == NULL){
730         config_netkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES, netkey_index);
731         mesh_access_message_processed(access_pdu_in_process);
732         return;
733     }
734 
735     // setup new key
736     new_network_key->internal_index = internal_index;
737     new_network_key->netkey_index   = netkey_index;
738     new_network_key->version        = (uint8_t)(subnet->old_key->version + 1);
739     memcpy(new_network_key->net_key, new_netkey, 16);
740 
741     // store in subnet (not active yet)
742     subnet->new_key = new_network_key;
743 
744     // derive other keys
745     access_pdu_in_process = pdu;
746     mesh_network_key_derive(&configuration_server_cmac_request, new_network_key, config_netkey_update_derived, subnet);
747 }
748 
749 static void config_netkey_delete_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu) {
750     mesh_access_parser_state_t parser;
751     mesh_access_parser_init(&parser, (mesh_pdu_t *) pdu);
752 
753     // get params
754     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
755 
756     // get existing network_key
757     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
758 
759     // remove subnet
760     mesh_subnet_t * subnet = mesh_subnet_get_by_netkey_index(netkey_index);
761     if (subnet != NULL){
762         // A NetKey shall not be deleted from the NetKey List using a message secured with this NetKey.
763         // Also prevents deleting the last network key
764         if (netkey_index != mesh_pdu_netkey_index(pdu)){
765 
766             // remove all appkeys for this netkey
767             mesh_transport_key_iterator_t it;
768             mesh_transport_key_iterator_init(&it, netkey_index);
769             while (mesh_transport_key_iterator_has_more(&it)){
770                 mesh_transport_key_t * transport_key = mesh_transport_key_iterator_get_next(&it);
771                 mesh_configuration_server_delete_appkey(transport_key);
772             }
773 
774             // delete old/current key
775             mesh_access_netkey_finalize(subnet->old_key);
776             subnet->old_key = NULL;
777 
778             // delete new key
779             if (subnet->new_key != NULL){
780                 mesh_access_netkey_finalize(subnet->new_key);
781                 subnet->new_key = NULL;
782             }
783 
784             // remove subnet
785             mesh_subnet_remove(subnet);
786             btstack_memory_mesh_subnet_free(subnet);
787 
788         } else {
789             status = MESH_FOUNDATION_STATUS_CANNOT_REMOVE;
790         }
791     }
792     config_netkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, netkey_index);
793 }
794 
795 static void config_netkey_get_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){
796     config_netkey_list(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
797     mesh_access_message_processed(pdu);
798 }
799 
800 // AppKey List
801 
802 static void config_appkey_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint32_t netkey_and_appkey_index, uint8_t status){
803     UNUSED(mesh_model);
804 
805     // setup message
806     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_appkey_status,
807                                                                                status, netkey_and_appkey_index);
808     if (!transport_pdu) return;
809 
810     // send as segmented access pdu
811     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
812 }
813 
814 static void config_appkey_list(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint32_t netkey_index_of_list){
815     UNUSED(mesh_model);
816 
817     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(MESH_FOUNDATION_OPERATION_APPKEY_LIST);
818     if (!transport_pdu) return;
819 
820     // check netkey_index is valid
821     mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index_of_list);
822     uint8_t status;
823     if (network_key == NULL){
824         status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
825     } else {
826         status = MESH_FOUNDATION_STATUS_SUCCESS;
827     }
828     mesh_access_transport_add_uint8(transport_pdu, status);
829     mesh_access_transport_add_uint16(transport_pdu, netkey_index_of_list);
830 
831     // add list of appkey indexes
832     mesh_transport_key_iterator_t it;
833     mesh_transport_key_iterator_init(&it, netkey_index_of_list);
834     while (mesh_transport_key_iterator_has_more(&it)){
835         mesh_transport_key_t * transport_key = mesh_transport_key_iterator_get_next(&it);
836         mesh_access_transport_add_uint16(transport_pdu, transport_key->appkey_index);
837     }
838 
839     // send as segmented access pdu
840     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
841 }
842 
843 static void config_appkey_add_or_update_aid(void *arg){
844     mesh_transport_key_t * transport_key = (mesh_transport_key_t *) arg;
845 
846     printf("Config Appkey Add/Update: NetKey Index 0x%04x, AppKey Index 0x%04x, AID %02x: ", transport_key->netkey_index, transport_key->appkey_index, transport_key->aid);
847     printf_hexdump(transport_key->key, 16);
848 
849     // store in TLV
850     mesh_store_app_key(transport_key);
851 
852     // add app key
853     mesh_transport_key_add(transport_key);
854 
855     uint32_t netkey_and_appkey_index = (transport_key->appkey_index << 12) | transport_key->netkey_index;
856     config_appkey_status(mesh_model_get_configuration_server(),  mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_SUCCESS);
857 
858     mesh_access_message_processed(access_pdu_in_process);
859 }
860 
861 static void config_appkey_add_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
862 
863     mesh_access_parser_state_t parser;
864     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
865 
866     // netkey and appkey index
867     uint32_t netkey_and_appkey_index = mesh_access_parser_get_u24(&parser);
868     uint16_t netkey_index = netkey_and_appkey_index & 0xfff;
869     uint16_t appkey_index = netkey_and_appkey_index >> 12;
870 
871     // actual key
872     uint8_t  appkey[16];
873     mesh_access_parser_get_key(&parser, appkey);
874 
875     // check netkey_index is valid
876     mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index);
877     if (network_key == NULL){
878         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX);
879         mesh_access_message_processed(pdu);
880         return;
881     }
882 
883     // check if appkey already exists
884     mesh_transport_key_t * transport_key = mesh_transport_key_get(appkey_index);
885     if (transport_key){
886         uint8_t status;
887         if (transport_key->netkey_index != netkey_index){
888             // already stored but with different netkey
889             status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
890         } else if (memcmp(transport_key->key, appkey, 16) == 0){
891             // key identical
892             status = MESH_FOUNDATION_STATUS_SUCCESS;
893         } else {
894             // key differs
895             status = MESH_FOUNDATION_STATUS_KEY_INDEX_ALREADY_STORED;
896         }
897         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, status);
898         mesh_access_message_processed(pdu);
899         return;
900     }
901 
902     // create app key (first get free slot in transport key table)
903     mesh_transport_key_t * app_key = NULL;
904     uint16_t internal_index = mesh_transport_key_get_free_index();
905     if (internal_index > 0){
906         app_key = btstack_memory_mesh_transport_key_get();
907     }
908     if (app_key == NULL) {
909         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES);
910         mesh_access_message_processed(pdu);
911         return;
912     }
913 
914     // store data
915     app_key->internal_index = internal_index;
916     app_key->akf = 1;
917     app_key->appkey_index = appkey_index;
918     app_key->netkey_index = netkey_index;
919     app_key->version = 0;
920     app_key->old_key = 0;
921 
922     memcpy(app_key->key, appkey, 16);
923 
924     // calculate AID
925     access_pdu_in_process = pdu;
926     mesh_transport_key_calc_aid(&configuration_server_cmac_request, app_key, config_appkey_add_or_update_aid, app_key);
927 }
928 
929 static void config_appkey_update_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
930 
931     mesh_access_parser_state_t parser;
932     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
933 
934     // netkey and appkey index
935     uint32_t netkey_and_appkey_index = mesh_access_parser_get_u24(&parser);
936     uint16_t netkey_index = netkey_and_appkey_index & 0xfff;
937     uint16_t appkey_index = netkey_and_appkey_index >> 12;
938 
939     // actual key
940     uint8_t  appkey[16];
941     mesh_access_parser_get_key(&parser, appkey);
942 
943 
944 // for PTS testing
945     // check netkey_index is valid
946     mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index);
947     if (network_key == NULL){
948         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX);
949         mesh_access_message_processed(pdu);
950         return;
951     }
952 
953     // check if appkey already exists
954     mesh_transport_key_t * existing_app_key = mesh_transport_key_get(appkey_index);
955     if (!existing_app_key) {
956         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INVALID_APPKEY_INDEX);
957         mesh_access_message_processed(pdu);
958         return;
959     }
960 
961     if (existing_app_key->netkey_index != netkey_index){
962         // already stored but with different netkey
963         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INVALID_BINDING);
964         mesh_access_message_processed(pdu);
965         return;
966     }
967 
968     if (memcmp(existing_app_key->key, appkey, 16) == 0){
969         // key identical
970         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_SUCCESS);
971         mesh_access_message_processed(pdu);
972         return;
973     }
974 
975     // create app key
976     mesh_transport_key_t * new_app_key = NULL;
977     uint16_t internal_index = mesh_transport_key_get_free_index();
978     if (internal_index > 0){
979         new_app_key = btstack_memory_mesh_transport_key_get();
980     }
981     if (new_app_key == NULL) {
982         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES);
983         mesh_access_message_processed(pdu);
984         return;
985     }
986 
987     // store data
988     new_app_key->internal_index = internal_index;
989     new_app_key->appkey_index = appkey_index;
990     new_app_key->netkey_index = netkey_index;
991     new_app_key->key_refresh  = 1;
992     new_app_key->version = (uint8_t)(existing_app_key->version + 1);
993     memcpy(new_app_key->key, appkey, 16);
994 
995     // mark old key
996     existing_app_key->old_key = 1;
997 
998     // calculate AID
999     access_pdu_in_process = pdu;
1000     mesh_transport_key_calc_aid(&configuration_server_cmac_request, new_app_key, config_appkey_add_or_update_aid, new_app_key);
1001 }
1002 
1003 static void config_appkey_delete_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1004     mesh_access_parser_state_t parser;
1005     mesh_access_parser_init(&parser, (mesh_pdu_t *) pdu);
1006 
1007     // netkey and appkey index
1008     uint32_t netkey_and_appkey_index = mesh_access_parser_get_u24(&parser);
1009     uint16_t netkey_index = netkey_and_appkey_index & 0xfff;
1010     uint16_t appkey_index = netkey_and_appkey_index >> 12;
1011 
1012     // check netkey_index is valid
1013     mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index);
1014     if (network_key == NULL){
1015         config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX);
1016         mesh_access_message_processed(pdu);
1017         return;
1018     }
1019 
1020     // check if appkey already exists
1021     mesh_transport_key_t * transport_key = mesh_transport_key_get(appkey_index);
1022     if (transport_key){
1023         mesh_configuration_server_delete_appkey(transport_key);
1024     }
1025 
1026     config_appkey_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_and_appkey_index, MESH_FOUNDATION_STATUS_SUCCESS);
1027     mesh_access_message_processed(pdu);
1028 }
1029 
1030 static void config_appkey_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1031     mesh_access_parser_state_t parser;
1032     mesh_access_parser_init(&parser, (mesh_pdu_t *) pdu);
1033     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
1034 
1035     config_appkey_list(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), netkey_index);
1036     mesh_access_message_processed(pdu);
1037 }
1038 
1039 // Configuration Model Subscriptions (messages)
1040 
1041 static void config_model_subscription_list(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, uint16_t element_address, uint32_t model_identifier, mesh_model_t * target_model){
1042     UNUSED(mesh_model);
1043 
1044     uint16_t opcode;
1045     if (mesh_model_is_bluetooth_sig(model_identifier)){
1046         opcode = MESH_FOUNDATION_OPERATION_SIG_MODEL_SUBSCRIPTION_LIST;
1047     } else {
1048         opcode = MESH_FOUNDATION_OPERATION_VENDOR_MODEL_SUBSCRIPTION_LIST;
1049     }
1050 
1051     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(opcode);
1052     if (!transport_pdu) return;
1053 
1054     // setup segmented message
1055     mesh_access_transport_add_uint8(transport_pdu, status);
1056     mesh_access_transport_add_uint16(transport_pdu, element_address);
1057     mesh_access_transport_add_model_identifier(transport_pdu, model_identifier);
1058 
1059     if (target_model != NULL){
1060         uint16_t i;
1061         for (i = 0; i < MAX_NR_MESH_SUBSCRIPTION_PER_MODEL; i++){
1062             uint16_t address = target_model->subscriptions[i];
1063             if (address == MESH_ADDRESS_UNSASSIGNED) continue;
1064             if (mesh_network_address_virtual(address)) {
1065                 mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_pseudo_dst(address);
1066                 if (virtual_address == NULL) continue;
1067                 address = virtual_address->hash;
1068             }
1069             mesh_access_transport_add_uint16(transport_pdu, address);
1070         }
1071     }
1072     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1073 }
1074 
1075 static void config_model_subscription_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
1076     mesh_access_parser_state_t parser;
1077     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1078 
1079     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1080     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1081 
1082     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1083     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1084 
1085     config_model_subscription_list(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, model_identifier, target_model);
1086     mesh_access_message_processed(pdu);
1087 }
1088 
1089 static void config_model_subscription_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, uint16_t element_address, uint16_t address, uint32_t model_identifier){
1090     UNUSED(mesh_model);
1091 
1092     // setup message
1093     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_model_subscription_status,
1094                                                                                 status, element_address, address, model_identifier);
1095     if (!transport_pdu) return;
1096     // send as segmented access pdu
1097     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1098 }
1099 
1100 static void config_model_subscription_add_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1101     mesh_access_parser_state_t parser;
1102     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1103 
1104     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1105     uint16_t address          = mesh_access_parser_get_u16(&parser);
1106     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1107 
1108     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1109     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1110 
1111     if (target_model != NULL){
1112         if (mesh_network_address_group(address) && !mesh_network_address_all_nodes(address)){
1113             status = mesh_model_add_subscription(target_model, address);
1114             if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1115                 mesh_model_store_subscriptions(target_model);
1116             }
1117         } else {
1118             status = MESH_FOUNDATION_STATUS_INVALID_ADDRESS;
1119         }
1120     }
1121 
1122     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, address, model_identifier);
1123     mesh_access_message_processed(pdu);
1124 }
1125 
1126 static void config_model_subscription_virtual_address_add_hash(void *arg){
1127     mesh_model_t * target_model = (mesh_model_t*) arg;
1128     mesh_model_t * mesh_model = mesh_model_get_configuration_server();
1129 
1130     // add if not exists
1131     mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_label_uuid(configuration_server_label_uuid);
1132     if (virtual_address == NULL){
1133         virtual_address = mesh_virtual_address_register(configuration_server_label_uuid, configuration_server_hash);
1134     }
1135 
1136     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1137     uint16_t hash_dst   = MESH_ADDRESS_UNSASSIGNED;
1138     if (virtual_address == NULL){
1139         status = MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
1140     } else {
1141         if (!mesh_model_contains_subscription(target_model,  virtual_address->pseudo_dst)){
1142             status = mesh_model_add_subscription(target_model,  virtual_address->pseudo_dst);
1143             if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1144                 hash_dst = virtual_address->hash;
1145                 mesh_virtual_address_increase_refcount(virtual_address);
1146                 mesh_model_store_subscriptions(target_model);
1147             }
1148         }
1149     }
1150 
1151     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), status, configuration_server_element_address, hash_dst, target_model->model_identifier);
1152     mesh_access_message_processed(access_pdu_in_process);
1153     return;
1154 }
1155 
1156 static void config_model_subscription_virtual_address_add_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1157     mesh_access_parser_state_t parser;
1158     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1159 
1160     // ElementAddress - Address of the element - should be us
1161     configuration_server_element_address = mesh_access_parser_get_u16(&parser);
1162 
1163     // store label uuid
1164     mesh_access_parser_get_label_uuid(&parser, configuration_server_label_uuid);
1165 
1166     // Model Identifier
1167     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1168 
1169     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1170     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(configuration_server_element_address, model_identifier, &status);
1171 
1172     if (target_model == NULL){
1173         config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, configuration_server_element_address, MESH_ADDRESS_UNSASSIGNED, model_identifier);
1174         mesh_access_message_processed(pdu);
1175         return;
1176     }
1177 
1178     access_pdu_in_process = pdu;
1179     mesh_virtual_address(&configuration_server_cmac_request, configuration_server_label_uuid, &configuration_server_hash, &config_model_subscription_virtual_address_add_hash, target_model);
1180 }
1181 
1182 static void config_model_subscription_overwrite_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
1183     mesh_access_parser_state_t parser;
1184     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1185 
1186     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1187     uint16_t address          = mesh_access_parser_get_u16(&parser);
1188     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1189 
1190     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1191     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1192 
1193     if (target_model != NULL){
1194         if (mesh_network_address_group(address) && !mesh_network_address_all_nodes(address)){
1195             mesh_subcription_decrease_virtual_address_ref_count(target_model);
1196             mesh_model_delete_all_subscriptions(target_model);
1197             mesh_model_add_subscription(target_model, address);
1198             mesh_model_store_subscriptions(target_model);
1199         } else {
1200             status = MESH_FOUNDATION_STATUS_INVALID_ADDRESS;
1201         }
1202     }
1203 
1204     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, address, model_identifier);
1205     mesh_access_message_processed(pdu);
1206 }
1207 
1208 static void config_model_subscription_virtual_address_overwrite_hash(void *arg){
1209     mesh_model_t * target_model = (mesh_model_t*) arg;
1210     mesh_model_t * mesh_model = mesh_model_get_configuration_server();
1211 
1212     // add if not exists
1213     mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_label_uuid(configuration_server_label_uuid);
1214     if (virtual_address == NULL){
1215         virtual_address = mesh_virtual_address_register(configuration_server_label_uuid, configuration_server_hash);
1216     }
1217 
1218     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1219     uint16_t address = MESH_ADDRESS_UNSASSIGNED;;
1220     if (virtual_address == NULL){
1221         status = MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
1222     } else {
1223         address = configuration_server_hash;
1224 
1225         // increase refcount first to avoid flash delete + add in a row
1226         mesh_virtual_address_increase_refcount(virtual_address);
1227 
1228         // decrease ref counts for virtual addresses in subscription
1229         mesh_subcription_decrease_virtual_address_ref_count(target_model);
1230 
1231         // clear subscriptions
1232         mesh_model_delete_all_subscriptions(target_model);
1233 
1234         // add new subscription (successfull if MAX_NR_MESH_SUBSCRIPTION_PER_MODEL > 0)
1235         mesh_model_add_subscription(target_model, virtual_address->pseudo_dst);
1236 
1237         mesh_model_store_subscriptions(target_model);
1238     }
1239 
1240     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), status, configuration_server_element_address, address, target_model->model_identifier);
1241     mesh_access_message_processed(access_pdu_in_process);
1242     return;
1243 }
1244 
1245 static void config_model_subscription_virtual_address_overwrite_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1246     mesh_access_parser_state_t parser;
1247     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1248 
1249     // ElementAddress - Address of the element - should be us
1250     configuration_server_element_address = mesh_access_parser_get_u16(&parser);
1251 
1252     // store label uuid
1253     mesh_access_parser_get_label_uuid(&parser, configuration_server_label_uuid);
1254 
1255     // Model Identifier
1256     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1257 
1258     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1259     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(configuration_server_element_address, model_identifier, &status);
1260 
1261     if (target_model == NULL){
1262         config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, configuration_server_element_address, MESH_ADDRESS_UNSASSIGNED, model_identifier);
1263         mesh_access_message_processed(pdu);
1264         return;
1265     }
1266     access_pdu_in_process = pdu;
1267     mesh_virtual_address(&configuration_server_cmac_request, configuration_server_label_uuid, &configuration_server_hash, &config_model_subscription_virtual_address_overwrite_hash, target_model);
1268 }
1269 
1270 static void config_model_subscription_delete_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
1271     mesh_access_parser_state_t parser;
1272     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1273 
1274     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1275     uint16_t address          = mesh_access_parser_get_u16(&parser);
1276     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1277 
1278     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1279     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1280 
1281     if (target_model != NULL){
1282         if (mesh_network_address_group(address) && !mesh_network_address_all_nodes(address)){
1283             mesh_model_delete_subscription(target_model, address);
1284             mesh_model_store_subscriptions(target_model);
1285         } else {
1286             status = MESH_FOUNDATION_STATUS_INVALID_ADDRESS;
1287         }
1288     }
1289 
1290     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, address, model_identifier);
1291     mesh_access_message_processed(pdu);
1292 }
1293 
1294 static void config_model_subscription_virtual_address_delete_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
1295     mesh_access_parser_state_t parser;
1296     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1297 
1298     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1299     mesh_access_parser_get_label_uuid(&parser, configuration_server_label_uuid);
1300     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1301 
1302     mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_label_uuid(configuration_server_label_uuid);
1303     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1304     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1305     uint16_t address = MESH_ADDRESS_UNSASSIGNED;
1306 
1307     if ((target_model != NULL) && (virtual_address != NULL) && mesh_model_contains_subscription(target_model, virtual_address->pseudo_dst)){
1308         address = virtual_address->hash;
1309         mesh_model_delete_subscription(target_model, virtual_address->pseudo_dst);
1310         mesh_model_store_subscriptions(target_model);
1311         mesh_virtual_address_decrease_refcount(virtual_address);
1312      }
1313 
1314     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address,address, model_identifier);
1315     mesh_access_message_processed(pdu);
1316 }
1317 
1318 static void config_model_subscription_delete_all_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
1319     mesh_access_parser_state_t parser;
1320     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1321 
1322     uint16_t element_address  = mesh_access_parser_get_u16(&parser);
1323     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1324 
1325     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1326     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1327 
1328     if (target_model != NULL){
1329         mesh_subcription_decrease_virtual_address_ref_count(target_model);
1330         mesh_model_delete_all_subscriptions(target_model);
1331         mesh_model_store_subscriptions(target_model);
1332     }
1333 
1334     config_model_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, MESH_ADDRESS_UNSASSIGNED, model_identifier);
1335     mesh_access_message_processed(pdu);
1336 }
1337 
1338 // Configuration Model to AppKey List
1339 
1340 static void config_model_app_status(mesh_model_t * mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, uint16_t element_address, uint16_t appkey_index, uint32_t model_identifier){
1341     UNUSED(mesh_model);
1342 
1343     // setup message
1344     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_config_model_app_status,
1345                                                             status, element_address, appkey_index, model_identifier);
1346     if (!transport_pdu) return;
1347 
1348     // send as segmented access pdu
1349     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1350 }
1351 
1352 static void config_model_app_list(mesh_model_t * config_server_model, uint16_t netkey_index, uint16_t dest, uint8_t status, uint16_t element_address, uint32_t model_identifier, mesh_model_t * mesh_model){
1353     UNUSED(config_server_model);
1354 
1355     uint16_t opcode;
1356     if (mesh_model_is_bluetooth_sig(model_identifier)){
1357         opcode = MESH_FOUNDATION_OPERATION_SIG_MODEL_APP_LIST;
1358     } else {
1359         opcode = MESH_FOUNDATION_OPERATION_VENDOR_MODEL_APP_LIST;
1360     }
1361     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(opcode);
1362     if (!transport_pdu) return;
1363 
1364     mesh_access_transport_add_uint8(transport_pdu, status);
1365     mesh_access_transport_add_uint16(transport_pdu, element_address);
1366     if (mesh_model_is_bluetooth_sig(model_identifier)) {
1367         mesh_access_transport_add_uint16(transport_pdu, mesh_model_get_model_id(model_identifier));
1368     } else {
1369         mesh_access_transport_add_uint32(transport_pdu, model_identifier);
1370     }
1371 
1372     // add list of appkey indexes
1373     if (mesh_model){
1374         uint16_t i;
1375         for (i=0;i<MAX_NR_MESH_APPKEYS_PER_MODEL;i++){
1376             uint16_t appkey_index = mesh_model->appkey_indices[i];
1377             if (appkey_index == MESH_APPKEY_INVALID) continue;
1378             mesh_access_transport_add_uint16(transport_pdu, appkey_index);
1379         }
1380     }
1381 
1382     // send as segmented access pdu
1383     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1384 }
1385 
1386 static void config_model_app_bind_handler(mesh_model_t *config_server_model, mesh_pdu_t * pdu) {
1387     mesh_access_parser_state_t parser;
1388     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1389 
1390     uint16_t element_address = mesh_access_parser_get_u16(&parser);
1391     uint16_t appkey_index    = mesh_access_parser_get_u16(&parser);
1392     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1393 
1394     uint8_t status;
1395     do {
1396         // validate address and look up model
1397         mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1398         if (target_model == NULL) break;
1399 
1400         // validate app key exists
1401         mesh_transport_key_t * app_key = mesh_transport_key_get(appkey_index);
1402         if (!app_key){
1403             status = MESH_FOUNDATION_STATUS_INVALID_APPKEY_INDEX;
1404             break;
1405         }
1406 
1407         // Configuration Server only allows device keys
1408         if (mesh_model_is_configuration_server(model_identifier)){
1409             status = MESH_FOUNDATION_STATUS_CANNOT_BIND;
1410             break;
1411         }
1412         status = mesh_model_bind_appkey(target_model, appkey_index);
1413 
1414     } while (0);
1415 
1416     config_model_app_status(config_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, appkey_index, model_identifier);
1417     mesh_access_message_processed(pdu);
1418 }
1419 
1420 static void config_model_app_unbind_handler(mesh_model_t *config_server_model, mesh_pdu_t * pdu) {
1421     mesh_access_parser_state_t parser;
1422     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1423 
1424     uint16_t element_address = mesh_access_parser_get_u16(&parser);
1425     uint16_t appkey_index    = mesh_access_parser_get_u16(&parser);
1426     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1427 
1428     uint8_t status;
1429     do {
1430         // validate address and look up model
1431         mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1432         if (target_model == NULL) break;
1433 
1434         // validate app key exists
1435         mesh_transport_key_t * app_key = mesh_transport_key_get(appkey_index);
1436         if (!app_key){
1437             status = MESH_FOUNDATION_STATUS_INVALID_APPKEY_INDEX;
1438             break;
1439         }
1440 
1441         // stop publishing
1442         mesh_configuration_server_stop_publishing_using_appkey(target_model, appkey_index);
1443 
1444         // unbind appkey
1445         mesh_model_unbind_appkey(target_model, appkey_index);
1446 
1447         status = MESH_FOUNDATION_STATUS_SUCCESS;
1448     } while (0);
1449 
1450     config_model_app_status(config_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, appkey_index, model_identifier);
1451     mesh_access_message_processed(pdu);
1452 }
1453 
1454 static void config_model_app_get_handler(mesh_model_t *config_server_model, mesh_pdu_t * pdu){
1455     mesh_access_parser_state_t parser;
1456     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1457 
1458     uint16_t element_address = mesh_access_parser_get_u16(&parser);
1459     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1460 
1461     uint8_t status;
1462     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1463     config_model_app_list(config_server_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, model_identifier, target_model);
1464     mesh_access_message_processed(pdu);
1465 }
1466 
1467 // Model Publication
1468 static void config_model_publication_changed(mesh_model_t *mesh_model, mesh_publication_model_t * new_publication_model){
1469 
1470     // stop publication
1471     mesh_model_publication_stop(mesh_model);
1472 
1473     if (new_publication_model->address == MESH_ADDRESS_UNSASSIGNED) {
1474         // "When the PublishAddress is set to the unassigned address, the values of the AppKeyIndex, CredentialFlag, PublishTTL, PublishPeriod, PublishRetransmitCount, and PublishRetransmitIntervalSteps fields shall be set to 0x00.
1475         mesh_model->publication_model->address = MESH_ADDRESS_UNSASSIGNED;
1476         mesh_model->publication_model->appkey_index = 0;
1477         mesh_model->publication_model->friendship_credential_flag = 0;
1478         mesh_model->publication_model->period = 0;
1479         mesh_model->publication_model->ttl = 0;
1480         mesh_model->publication_model->retransmit = 0;
1481     } else {
1482         // update model publication state
1483         mesh_model->publication_model->address = new_publication_model->address;
1484         mesh_model->publication_model->appkey_index = new_publication_model->appkey_index;
1485         mesh_model->publication_model->friendship_credential_flag = new_publication_model->friendship_credential_flag;
1486         mesh_model->publication_model->period = new_publication_model->period;
1487         mesh_model->publication_model->ttl = new_publication_model->ttl;
1488         mesh_model->publication_model->retransmit = new_publication_model->retransmit;
1489     }
1490 
1491     // store
1492     mesh_model_store_publication(mesh_model);
1493 
1494     // start publication if address is set (nothing happens if period = 0 and retransmit = 0)
1495     if (new_publication_model->address == MESH_ADDRESS_UNSASSIGNED) return;
1496 
1497     // start to publish
1498     mesh_model_publication_start(mesh_model);
1499 }
1500 
1501 
1502 static void
1503 config_model_publication_status(mesh_model_t *mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status,
1504                                     uint16_t element_address, uint32_t model_identifier, mesh_publication_model_t *publication_model) {
1505     UNUSED(mesh_model);
1506 
1507     // setup message
1508     uint16_t app_key_index_and_credential_flag = 0;
1509     uint16_t publish_address = 0;
1510     uint8_t ttl = 0;
1511     uint8_t period = 0;
1512     uint8_t retransmit = 0;
1513     if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1514         app_key_index_and_credential_flag = (publication_model->friendship_credential_flag << 12) | publication_model->appkey_index;
1515         ttl = publication_model->ttl;
1516         period = publication_model->period;
1517         retransmit = publication_model->retransmit;
1518 
1519         publish_address = publication_model->address;
1520         if (mesh_network_address_virtual(publish_address)){
1521             mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_pseudo_dst(publish_address);
1522             if (virtual_address){
1523                 publish_address = virtual_address->hash;
1524             }
1525         }
1526     }
1527 
1528     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
1529             &mesh_foundation_config_model_publication_status, status, element_address, publish_address,
1530             app_key_index_and_credential_flag, ttl, period, retransmit, model_identifier);
1531     if (!transport_pdu) return;
1532 
1533     // send as segmented access pdu
1534     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1535 }
1536 
1537 static void
1538 config_model_publication_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1539 
1540     // set defaults
1541     memset(&configuration_server_publication_model, 0, sizeof(mesh_publication_model_t));
1542 
1543     mesh_access_parser_state_t parser;
1544     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1545 
1546     // ElementAddress
1547     uint16_t element_address = mesh_access_parser_get_u16(&parser);
1548 
1549     // PublishAddress, 16 bit
1550     configuration_server_publication_model.address = mesh_access_parser_get_u16(&parser);
1551 
1552     // AppKeyIndex (12), CredentialFlag (1), RFU (3)
1553     uint16_t temp = mesh_access_parser_get_u16(&parser);
1554     configuration_server_publication_model.appkey_index = temp & 0x0fff;
1555     configuration_server_publication_model.friendship_credential_flag = (temp >> 12) & 1;
1556 
1557     // TTL
1558     configuration_server_publication_model.ttl        = mesh_access_parser_get_u8(&parser);
1559 
1560     // Period
1561     configuration_server_publication_model.period     = mesh_access_parser_get_u8(&parser);
1562 
1563     // Retransmit
1564     configuration_server_publication_model.retransmit = mesh_access_parser_get_u8(&parser);
1565 
1566     // Model Identifier
1567     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1568 
1569     // Get Model for Element + Model Identifier
1570     uint8_t status;
1571     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1572 
1573     // Check if publicatation model struct provided
1574     if (status == MESH_FOUNDATION_STATUS_SUCCESS) {
1575         if (target_model->publication_model == NULL){
1576             status = MESH_FOUNDATION_STATUS_CANNOT_SET;
1577         }
1578     }
1579 
1580     // Check AppKey
1581     if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1582         // check if appkey already exists
1583         mesh_transport_key_t * app_key = mesh_transport_key_get(configuration_server_publication_model.appkey_index);
1584         if (app_key == NULL) {
1585             status = MESH_FOUNDATION_STATUS_INVALID_APPKEY_INDEX;
1586         }
1587     }
1588 
1589     // Accept set
1590     if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1591         // decrease ref count if old virtual address
1592         if (mesh_network_address_virtual(configuration_server_publication_model.address)) {
1593             mesh_virtual_address_t * current_virtual_address = mesh_virtual_address_for_pseudo_dst(configuration_server_publication_model.address);
1594             mesh_virtual_address_decrease_refcount(current_virtual_address);
1595         }
1596 
1597         // restart publication
1598         config_model_publication_changed(target_model, &configuration_server_publication_model);
1599     }
1600 
1601     // send status
1602     config_model_publication_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, model_identifier, &configuration_server_publication_model);
1603     mesh_access_message_processed(pdu);
1604 }
1605 
1606 static void config_model_publication_virtual_address_set_hash(void *arg){
1607     mesh_model_t *mesh_model = (mesh_model_t*) arg;
1608 
1609     // add if not exist
1610     mesh_virtual_address_t * virtual_address = mesh_virtual_address_for_label_uuid(configuration_server_label_uuid);
1611     if (virtual_address == NULL){
1612         virtual_address = mesh_virtual_address_register(configuration_server_label_uuid, configuration_server_hash);
1613     }
1614 
1615     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1616     if (virtual_address == NULL){
1617         status = MESH_FOUNDATION_STATUS_INSUFFICIENT_RESOURCES;
1618     } else {
1619 
1620         // increase ref count if new virtual address
1621         mesh_virtual_address_increase_refcount(virtual_address);
1622 
1623         // decrease ref count if old virtual address
1624         if (mesh_network_address_virtual(configuration_server_publication_model.address)) {
1625             mesh_virtual_address_t * current_virtual_address = mesh_virtual_address_for_pseudo_dst(configuration_server_publication_model.address);
1626             if (current_virtual_address){
1627                 mesh_virtual_address_decrease_refcount(current_virtual_address);
1628             }
1629         }
1630 
1631         configuration_server_publication_model.address = virtual_address->pseudo_dst;
1632         mesh_virtual_address_increase_refcount(virtual_address);
1633 
1634         // restart publication
1635         config_model_publication_changed(configuration_server_target_model, &configuration_server_publication_model);
1636     }
1637 
1638     // send status
1639     config_model_publication_status(mesh_model, mesh_pdu_netkey_index(access_pdu_in_process), mesh_pdu_src(access_pdu_in_process), status, configuration_server_element_address, configuration_server_model_identifier, &configuration_server_publication_model);
1640 
1641     mesh_access_message_processed(access_pdu_in_process);
1642 }
1643 
1644 static void
1645 config_model_publication_virtual_address_set_handler(mesh_model_t *mesh_model,
1646                                                      mesh_pdu_t * pdu) {
1647 
1648     // set defaults
1649     memset(&configuration_server_publication_model, 0, sizeof(mesh_publication_model_t));
1650 
1651     mesh_access_parser_state_t parser;
1652     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1653 
1654     // ElementAddress - Address of the element
1655     configuration_server_element_address = mesh_access_parser_get_u16(&parser);
1656 
1657     // store label uuid
1658     mesh_access_parser_get_label_uuid(&parser, configuration_server_label_uuid);
1659 
1660     // AppKeyIndex (12), CredentialFlag (1), RFU (3)
1661     uint16_t temp = mesh_access_parser_get_u16(&parser);
1662     configuration_server_publication_model.appkey_index = temp & 0x0fff;
1663     configuration_server_publication_model.friendship_credential_flag = (temp >> 12) & 1;
1664     configuration_server_publication_model.ttl        = mesh_access_parser_get_u8(&parser);
1665     configuration_server_publication_model.period     = mesh_access_parser_get_u8(&parser);
1666     configuration_server_publication_model.retransmit = mesh_access_parser_get_u8(&parser);
1667 
1668     // Model Identifier
1669     configuration_server_model_identifier = mesh_access_parser_get_model_identifier(&parser);
1670 
1671     // Get Model for Element + Model Identifier
1672     uint8_t status;
1673     configuration_server_target_model = mesh_access_model_for_address_and_model_identifier(configuration_server_element_address, configuration_server_model_identifier, &status);
1674 
1675      // Check if publicatation model struct provided
1676      if (status == MESH_FOUNDATION_STATUS_SUCCESS) {
1677         if (configuration_server_target_model->publication_model == NULL){
1678             status = MESH_FOUNDATION_STATUS_CANNOT_SET;
1679         }
1680      }
1681 
1682      // Check AppKey
1683      if (status == MESH_FOUNDATION_STATUS_SUCCESS){
1684          // check if appkey already exists
1685          mesh_transport_key_t * app_key = mesh_transport_key_get(configuration_server_publication_model.appkey_index);
1686          if (app_key == NULL) {
1687              status = MESH_FOUNDATION_STATUS_INVALID_APPKEY_INDEX;
1688          }
1689      }
1690 
1691     // on error, no need to calculate virtual address hash
1692     if (status != MESH_FOUNDATION_STATUS_SUCCESS){
1693         config_model_publication_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, configuration_server_element_address, configuration_server_model_identifier, &configuration_server_publication_model);
1694         mesh_access_message_processed(pdu);
1695         return;
1696     }
1697 
1698     access_pdu_in_process = pdu;
1699     mesh_virtual_address(&configuration_server_cmac_request, configuration_server_label_uuid, &configuration_server_hash, &config_model_publication_virtual_address_set_hash, mesh_model);
1700 }
1701 
1702 static void
1703 config_model_publication_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1704 
1705 
1706     mesh_access_parser_state_t parser;
1707     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1708 
1709     // ElementAddress - Address of the element - should be us
1710     uint16_t element_address = mesh_access_parser_get_u16(&parser);
1711 
1712     // Model Identifier
1713     uint32_t model_identifier = mesh_access_parser_get_model_identifier(&parser);
1714 
1715     // Get Model for Element + Model Identifier
1716     uint8_t status;
1717     mesh_model_t * target_model = mesh_access_model_for_address_and_model_identifier(element_address, model_identifier, &status);
1718 
1719     mesh_publication_model_t * publication_model;
1720     if (target_model == NULL){
1721         // use defaults
1722         memset(&configuration_server_publication_model, 0, sizeof(mesh_publication_model_t));
1723         publication_model = &configuration_server_publication_model;
1724     } else {
1725         publication_model = target_model->publication_model;
1726     }
1727 
1728     config_model_publication_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, element_address, model_identifier, publication_model);
1729     mesh_access_message_processed(pdu);
1730 }
1731 
1732 // Heartbeat Publication
1733 
1734 static void config_heartbeat_publication_emit(mesh_heartbeat_publication_t * mesh_heartbeat_publication){
1735 
1736     printf("CONFIG_SERVER_HEARTBEAT: Emit (dest %04x, count %u, period %u ms)\n",
1737         mesh_heartbeat_publication->destination,
1738         mesh_heartbeat_publication->count,
1739         mesh_heartbeat_publication->period_ms);
1740 
1741     // active features
1742     mesh_heartbeat_publication->active_features = mesh_foundation_get_features();
1743 
1744     mesh_network_pdu_t * network_pdu = mesh_network_pdu_get();
1745     if (network_pdu){
1746         uint8_t data[3];
1747         data[0] = mesh_heartbeat_publication->ttl;
1748         big_endian_store_16(data, 1, mesh_heartbeat_publication->active_features);
1749         uint8_t status = mesh_upper_transport_setup_control_pdu((mesh_pdu_t *) network_pdu, mesh_heartbeat_publication->netkey_index,
1750                 mesh_heartbeat_publication->ttl, mesh_node_get_primary_element_address(), mesh_heartbeat_publication->destination,
1751                 MESH_TRANSPORT_OPCODE_HEARTBEAT, data, sizeof(data));
1752         if (status){
1753             // stop periodic emit on error (netkey got invalid)
1754             mesh_heartbeat_publication->period_ms = 0;
1755             mesh_network_pdu_free(network_pdu);
1756         } else {
1757             mesh_upper_transport_send_control_pdu((mesh_pdu_t *) network_pdu);
1758         }
1759     }
1760 
1761     // forever
1762     if (mesh_heartbeat_publication->count > 0 && mesh_heartbeat_publication->count < 0xffffu) {
1763         mesh_heartbeat_publication->count--;
1764     }
1765 }
1766 void mesh_configuration_server_feature_changed(void){
1767     mesh_model_t * mesh_model = mesh_model_get_configuration_server();
1768     mesh_heartbeat_publication_t * mesh_heartbeat_publication = &((mesh_configuration_server_model_context_t*) mesh_model->model_data)->heartbeat_publication;
1769 
1770     // filter features by observed features for heartbeats
1771     uint16_t current_features  = mesh_foundation_get_features()              & mesh_heartbeat_publication->features;
1772     uint16_t previous_features = mesh_heartbeat_publication->active_features & mesh_heartbeat_publication->features;
1773 
1774     // changes?
1775     if (current_features == previous_features) return;
1776 
1777     config_heartbeat_publication_emit(mesh_heartbeat_publication);
1778 }
1779 
1780 static void config_heartbeat_publication_timeout_handler(btstack_timer_source_t * ts){
1781 
1782     mesh_heartbeat_publication_t * mesh_heartbeat_publication = (mesh_heartbeat_publication_t*) ts;
1783     mesh_heartbeat_publication->timer_active = 0;
1784 
1785     // emit beat
1786     config_heartbeat_publication_emit(mesh_heartbeat_publication);
1787 
1788     // all sent?
1789     if (mesh_heartbeat_publication->count == 0) return;
1790 
1791     // periodic publication?
1792     if (mesh_heartbeat_publication->period_ms == 0) return;
1793 
1794     btstack_run_loop_set_timer(ts, mesh_heartbeat_publication->period_ms);
1795     btstack_run_loop_add_timer(ts);
1796     mesh_heartbeat_publication->timer_active = 1;
1797 }
1798 
1799 static void config_heartbeat_publication_status(mesh_model_t *mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, mesh_heartbeat_publication_t * mesh_heartbeat_publication){
1800     UNUSED(mesh_model);
1801 
1802     // setup message
1803     uint8_t count_log = heartbeat_count_log(mesh_heartbeat_publication->count);
1804     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
1805             &mesh_foundation_config_heartbeat_publication_status,
1806             status,
1807             mesh_heartbeat_publication->destination,
1808             count_log,
1809             mesh_heartbeat_publication->period_log,
1810             mesh_heartbeat_publication->ttl,
1811             mesh_heartbeat_publication->features,
1812             mesh_heartbeat_publication->netkey_index);
1813     if (!transport_pdu) return;
1814 
1815     printf("MESH config_heartbeat_publication_status count = %u => count_log = %u\n", mesh_heartbeat_publication->count, count_log);
1816 
1817     // send as segmented access pdu
1818     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1819 }
1820 
1821 static void config_heartbeat_publication_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1822 
1823     mesh_heartbeat_publication_t requested_publication;
1824     memset(&requested_publication, 0, sizeof(requested_publication));
1825 
1826     mesh_access_parser_state_t parser;
1827     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1828 
1829     // Destination address for Heartbeat messages
1830     requested_publication.destination = mesh_access_parser_get_u16(&parser);
1831     // Number of Heartbeat messages to be sent
1832     requested_publication.count = heartbeat_pwr2(mesh_access_parser_get_u8(&parser));
1833     //  Period for sending Heartbeat messages
1834     requested_publication.period_log = mesh_access_parser_get_u8(&parser);
1835     //  TTL to be used when sending Heartbeat messages
1836     requested_publication.ttl = mesh_access_parser_get_u8(&parser);
1837     // Bit field indicating features that trigger Heartbeat messages when changed
1838     requested_publication.features = mesh_access_parser_get_u16(&parser) & MESH_HEARTBEAT_FEATURES_SUPPORTED_MASK;
1839     // NetKey Index
1840     requested_publication.netkey_index = mesh_access_parser_get_u16(&parser);
1841 
1842     // store period as ms
1843     requested_publication.period_ms = heartbeat_pwr2(requested_publication.period_log) * 1000;
1844 
1845     // store current features
1846     requested_publication.active_features = mesh_foundation_get_features();
1847 
1848     mesh_heartbeat_publication_t * mesh_heartbeat_publication = &((mesh_configuration_server_model_context_t*) mesh_model->model_data)->heartbeat_publication;
1849 
1850     // validate fields
1851     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1852     mesh_network_key_t * network_key = mesh_network_key_list_get(requested_publication.netkey_index);
1853     if (network_key == NULL){
1854         status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
1855     } else {
1856         printf("MESH config_heartbeat_publication_set, destination %x, count = %x, period = %u s\n",
1857             requested_publication.destination, requested_publication.count, requested_publication.period_ms);
1858 
1859         // stop timer if active
1860         // note: accept update below using memcpy overwwrite timer_active flag
1861         if (mesh_heartbeat_publication->timer_active){
1862             btstack_run_loop_remove_timer(&mesh_heartbeat_publication->timer);
1863             mesh_heartbeat_publication->timer_active = 0;
1864         }
1865 
1866         // accept update
1867         memcpy(mesh_heartbeat_publication, &requested_publication, sizeof(mesh_heartbeat_publication_t));
1868     }
1869 
1870     config_heartbeat_publication_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, mesh_heartbeat_publication);
1871 
1872     mesh_access_message_processed(pdu);
1873 
1874     if (status != MESH_FOUNDATION_STATUS_SUCCESS) return;
1875 
1876     // check if heartbeats should be disabled
1877     if (mesh_heartbeat_publication->destination == MESH_ADDRESS_UNSASSIGNED || mesh_heartbeat_publication->period_log == 0) {
1878         return;
1879     }
1880 
1881     // initial heartbeat in 100 ms
1882     btstack_run_loop_set_timer_handler(&mesh_heartbeat_publication->timer, config_heartbeat_publication_timeout_handler);
1883     btstack_run_loop_set_timer_context(&mesh_heartbeat_publication->timer, mesh_heartbeat_publication);
1884     btstack_run_loop_set_timer(&mesh_heartbeat_publication->timer, 100);
1885     btstack_run_loop_add_timer(&mesh_heartbeat_publication->timer);
1886     mesh_heartbeat_publication->timer_active = 1;
1887 }
1888 
1889 static void config_heartbeat_publication_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1890     mesh_heartbeat_publication_t * mesh_heartbeat_publication = &((mesh_configuration_server_model_context_t*) mesh_model->model_data)->heartbeat_publication;
1891     config_heartbeat_publication_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_SUCCESS, mesh_heartbeat_publication);
1892     mesh_access_message_processed(pdu);
1893 }
1894 
1895 // Heartbeat Subscription
1896 
1897 static void config_heartbeat_subscription_status(mesh_model_t *mesh_model, uint16_t netkey_index, uint16_t dest, uint8_t status, mesh_heartbeat_subscription_t * mesh_heartbeat_subscription){
1898     UNUSED(mesh_model);
1899 
1900     // setup message
1901     uint8_t count_log = heartbeat_count_log(mesh_heartbeat_subscription->count);
1902     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
1903             &mesh_foundation_config_heartbeat_subscription_status,
1904             status,
1905             mesh_heartbeat_subscription->source,
1906             mesh_heartbeat_subscription->destination,
1907             mesh_heartbeat_subscription->period_log,
1908             count_log,
1909             mesh_heartbeat_subscription->min_hops,
1910             mesh_heartbeat_subscription->max_hops);
1911     if (!transport_pdu) return;
1912     printf("MESH config_heartbeat_subscription_status count = %u => count_log = %u\n", mesh_heartbeat_subscription->count, count_log);
1913 
1914     // send as segmented access pdu
1915     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
1916 }
1917 static int config_heartbeat_subscription_enabled(mesh_heartbeat_subscription_t * mesh_heartbeat_subscription){
1918     return mesh_network_address_unicast(mesh_heartbeat_subscription->source) && mesh_heartbeat_subscription->period_log > 0 &&
1919         (mesh_network_address_unicast(mesh_heartbeat_subscription->destination) || mesh_network_address_group(mesh_heartbeat_subscription->destination));
1920 }
1921 
1922 static void config_heartbeat_subscription_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
1923     mesh_access_parser_state_t parser;
1924     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
1925 
1926     mesh_heartbeat_subscription_t requested_subscription;
1927 
1928     // Destination address for Heartbeat messages
1929     requested_subscription.source = mesh_access_parser_get_u16(&parser);
1930     // Destination address for Heartbeat messages
1931     requested_subscription.destination = mesh_access_parser_get_u16(&parser);
1932     //  Period for sending Heartbeat messages
1933     requested_subscription.period_log = mesh_access_parser_get_u8(&parser);
1934 
1935 
1936     // Hearbeat Subscription Soure must be unassigned or a unicast address
1937     int source_address_valid =
1938          (requested_subscription.source == MESH_ADDRESS_UNSASSIGNED)    ||
1939          (mesh_network_address_unicast(requested_subscription.source));
1940 
1941     // Heartbeat Subscription Destination must be unassigned, unicast (== our primary address), or a group address)
1942     int destination_address_valid =
1943          (requested_subscription.destination == MESH_ADDRESS_UNSASSIGNED)  ||
1944          (mesh_network_address_unicast(requested_subscription.destination) && requested_subscription.destination == mesh_node_get_primary_element_address()) ||
1945          (mesh_network_address_group(requested_subscription.destination));
1946 
1947     if (!source_address_valid || !destination_address_valid){
1948         printf("MESH config_heartbeat_subscription_set, source %x or destination %x invalid\n", requested_subscription.source, requested_subscription.destination);
1949         mesh_access_message_processed(pdu);
1950         return;
1951     }
1952 
1953     int subscription_enabled = config_heartbeat_subscription_enabled(&requested_subscription);
1954     printf("MESH config_heartbeat_subscription_set, source %x destination %x, period = %u s => enabled %u \n", requested_subscription.source,
1955             requested_subscription.destination, heartbeat_pwr2(requested_subscription.period_log), subscription_enabled);
1956 
1957     // ignore messages
1958     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
1959     if (requested_subscription.period_log > 0x11u){
1960         status = MESH_FOUNDATION_STATUS_CANNOT_SET;
1961     } else if ((requested_subscription.destination != MESH_ADDRESS_UNSASSIGNED)  &&
1962                !mesh_network_address_unicast(requested_subscription.destination) &&
1963                !mesh_network_address_group(requested_subscription.destination)){
1964         status = MESH_FOUNDATION_STATUS_INVALID_ADDRESS;
1965     }
1966 
1967     if (status != MESH_FOUNDATION_STATUS_SUCCESS){
1968         config_heartbeat_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, &requested_subscription);
1969         mesh_access_message_processed(pdu);
1970         return;
1971     }
1972 
1973     mesh_heartbeat_subscription_t * mesh_heartbeat_subscription = &((mesh_configuration_server_model_context_t*) mesh_model->model_data)->heartbeat_subscription;
1974 
1975     if (config_heartbeat_subscription_enabled(&requested_subscription)){
1976         mesh_heartbeat_subscription->source          = requested_subscription.source;
1977         mesh_heartbeat_subscription->destination     = requested_subscription.destination;
1978         mesh_heartbeat_subscription->period_log      = requested_subscription.period_log;
1979         mesh_heartbeat_subscription->count           = 0;
1980         mesh_heartbeat_subscription->min_hops        = 0x7Fu;
1981         mesh_heartbeat_subscription->max_hops        = 0u;
1982         mesh_heartbeat_subscription->period_start_ms = btstack_run_loop_get_time_ms();
1983     } else {
1984 #if 0
1985         // code according to Mesh Spec v1.0.1
1986         // "When an element receives a Config Heartbeat Subscription Set message, it shall ... respond with a Config Heartbeat Subscription Status message, setting ...
1987         //  If the Source or the Destination field is set to the unassigned address, or the PeriodLog field is set to 0x00, [then]
1988         //  - the processing of received Heartbeat messages shall be disabled,
1989         //  - the Heartbeat Subscription Source state shall be set to the unassigned address,
1990         //  - the Heartbeat Subscription Destination state shall be set to the unassigned address,
1991         //  - the Heartbeat Subscription MinHops state shall be unchanged,
1992         //  - the Heartbeat Subscription MaxHops state shall be unchanged,
1993         //  - and the Heartbeat Subscription Count state shall be unchanged."
1994         // If period_log == 0, then set src + dest to unassigned. If src or dest are unsigned, get triggers status mit count_log == 0
1995         mesh_heartbeat_subscription->source          = MESH_ADDRESS_UNSASSIGNED;
1996         mesh_heartbeat_subscription->destination     = MESH_ADDRESS_UNSASSIGNED;
1997 #else
1998         // code to satisfy MESH/NODE/CFG/HBS/BV-02-C from PTS 7.4.1 / Mesh TS 1.0.2
1999         if (requested_subscription.source == MESH_ADDRESS_UNSASSIGNED || requested_subscription.destination == MESH_ADDRESS_UNSASSIGNED){
2000             mesh_heartbeat_subscription->source          = MESH_ADDRESS_UNSASSIGNED;
2001             mesh_heartbeat_subscription->destination     = MESH_ADDRESS_UNSASSIGNED;
2002         }
2003 #endif
2004         mesh_heartbeat_subscription->period_log      = 0u;
2005     }
2006 
2007     config_heartbeat_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, mesh_heartbeat_subscription);
2008     mesh_access_message_processed(pdu);
2009 }
2010 
2011 static uint32_t config_heartbeat_subscription_get_period_remaining_s(mesh_heartbeat_subscription_t * heartbeat_subscription){
2012     // calculate period_log
2013     int32_t time_since_start_s = btstack_time_delta(btstack_run_loop_get_time_ms(), heartbeat_subscription->period_start_ms) / 1000;
2014     int32_t period_s = heartbeat_pwr2(heartbeat_subscription->period_log);
2015     uint32_t period_remaining_s = 0;
2016     if (time_since_start_s < period_s){
2017         period_remaining_s = period_s - time_since_start_s;
2018     }
2019     printf("Heartbeat: time since start %d s, period %u s, period remaining %u s\n", time_since_start_s, period_s, period_remaining_s);
2020     return period_remaining_s;
2021 }
2022 
2023 static void config_heartbeat_subscription_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu) {
2024     mesh_heartbeat_subscription_t * mesh_heartbeat_subscription = &((mesh_configuration_server_model_context_t*) mesh_model->model_data)->heartbeat_subscription;
2025     mesh_heartbeat_subscription_t subscription;
2026     memcpy(&subscription, mesh_heartbeat_subscription, sizeof(subscription));
2027     if (mesh_heartbeat_subscription->source == MESH_ADDRESS_UNSASSIGNED || mesh_heartbeat_subscription->destination == MESH_ADDRESS_UNSASSIGNED){
2028         memset(&subscription, 0, sizeof(subscription));
2029     } else {
2030         // calculate period_log
2031         uint32_t period_remaining_s = config_heartbeat_subscription_get_period_remaining_s(&subscription);
2032         subscription.period_log = heartbeat_period_log(period_remaining_s);
2033     }
2034     config_heartbeat_subscription_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_SUCCESS, &subscription);
2035     mesh_access_message_processed(pdu);
2036 }
2037 
2038 // KeyRefresh Phase
2039 
2040 static void config_key_refresh_phase_status(mesh_model_t *mesh_model, uint16_t netkey_index_dest, uint16_t dest, uint8_t status, uint16_t netkey_index,
2041     mesh_key_refresh_state_t key_refresh_state){
2042     UNUSED(mesh_model);
2043 
2044     // setup message
2045     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
2046         &mesh_key_refresh_phase_status,
2047         status,
2048         netkey_index,
2049         key_refresh_state);
2050     if (!transport_pdu) return;
2051 
2052     // send as segmented access pdu
2053     config_server_send_message(netkey_index_dest, dest, (mesh_pdu_t *) transport_pdu);
2054 }
2055 
2056 static void config_key_refresh_phase_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2057     mesh_access_parser_state_t parser;
2058     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
2059     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
2060     mesh_subnet_t * subnet = mesh_subnet_get_by_netkey_index(netkey_index);
2061 
2062     uint8_t status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
2063     mesh_key_refresh_state_t key_refresh_state = MESH_KEY_REFRESH_NOT_ACTIVE;
2064 
2065     if (subnet != NULL){
2066         status = MESH_FOUNDATION_STATUS_SUCCESS;
2067         key_refresh_state = subnet->key_refresh;
2068     }
2069 
2070     config_key_refresh_phase_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, netkey_index, key_refresh_state);
2071     mesh_access_message_processed(pdu);
2072 }
2073 
2074 static void config_key_refresh_phase_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2075     mesh_access_parser_state_t parser;
2076     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
2077     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
2078     uint8_t  key_refresh_phase_transition = mesh_access_parser_get_u8(&parser);
2079     mesh_subnet_t * subnet = mesh_subnet_get_by_netkey_index(netkey_index);
2080 
2081     uint8_t status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
2082 
2083     if (subnet != NULL){
2084         status = MESH_FOUNDATION_STATUS_SUCCESS;
2085 
2086         switch (key_refresh_phase_transition){
2087             case 0x02:
2088                 switch (subnet->key_refresh){
2089                     case MESH_KEY_REFRESH_FIRST_PHASE:
2090                     case MESH_KEY_REFRESH_SECOND_PHASE:
2091                         subnet->key_refresh = MESH_KEY_REFRESH_SECOND_PHASE;
2092                         break;
2093                     default:
2094                         break;
2095                 }
2096                 break;
2097             case 0x03:
2098                 switch (subnet->key_refresh){
2099                     case MESH_KEY_REFRESH_FIRST_PHASE:
2100                     case MESH_KEY_REFRESH_SECOND_PHASE:
2101                         // key refresh phase 3 entered
2102                         mesh_access_key_refresh_revoke_keys(subnet);
2103                         subnet->key_refresh = MESH_KEY_REFRESH_NOT_ACTIVE;
2104                         break;
2105                     default:
2106                         break;
2107                 }
2108                 break;
2109             default:
2110                 status = MESH_FOUNDATION_STATUS_CANNOT_SET;
2111                 break;
2112         }
2113     }
2114 
2115     config_key_refresh_phase_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, netkey_index, subnet->key_refresh);
2116     mesh_access_message_processed(pdu);
2117 }
2118 
2119 
2120 static void config_node_reset_status(mesh_model_t *mesh_model, uint16_t netkey_index, uint16_t dest){
2121     UNUSED(mesh_model);
2122 
2123     // setup message
2124     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_node_reset_status);
2125     if (!transport_pdu) return;
2126 
2127     // send as segmented access pdu
2128     config_server_send_message(netkey_index, dest, (mesh_pdu_t *) transport_pdu);
2129 }
2130 
2131 static void config_node_reset_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2132     mesh_node_reset();
2133     config_node_reset_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu));
2134     mesh_access_message_processed(pdu);
2135 }
2136 
2137 static void low_power_node_poll_timeout_status(mesh_model_t *mesh_model, uint16_t netkey_index_dest, uint16_t dest, uint8_t status){
2138     UNUSED(mesh_model);
2139 
2140     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
2141         &mesh_foundation_low_power_node_poll_timeout_status,
2142         status,
2143         0,  // The unicast address of the Low Power node
2144         0); // The current value of the PollTimeout timer of the Low Power node
2145     if (!transport_pdu) return;
2146     printf("TODO: send unicast address of the Low Power node and the current value of the PollTimeout timer, instead of 0s\n");
2147     // send as segmented access pdu
2148     config_server_send_message(netkey_index_dest, dest, (mesh_pdu_t *) transport_pdu);
2149 }
2150 
2151 static void config_low_power_node_poll_timeout_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2152     mesh_access_parser_state_t parser;
2153     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
2154     printf("TODO: implement get the current value of PollTimeout timer of the Low Power node within a Friend node\n");
2155     low_power_node_poll_timeout_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), MESH_FOUNDATION_STATUS_SUCCESS);
2156 
2157     mesh_access_message_processed(pdu);
2158 }
2159 
2160 static void config_node_identity_status(mesh_model_t *mesh_model, uint16_t netkey_index_dest, uint16_t dest, uint8_t status, uint16_t netkey_index,
2161     mesh_node_identity_state_t node_identity_state){
2162     UNUSED(mesh_model);
2163 
2164     // setup message
2165     mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(
2166         &mesh_foundation_node_identity_status,
2167         status,
2168         netkey_index,
2169         node_identity_state);
2170     if (!transport_pdu) return;
2171 
2172     // send as segmented access pdu
2173     config_server_send_message(netkey_index_dest, dest, (mesh_pdu_t *) transport_pdu);
2174 }
2175 
2176 static void config_node_identity_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2177     mesh_access_parser_state_t parser;
2178     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
2179     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
2180 
2181     mesh_node_identity_state_t node_identity_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_NOT_SUPPORTED;
2182     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
2183 #ifdef ENABLE_MESH_PROXY_SERVER
2184     status = mesh_proxy_get_advertising_with_node_id_status(netkey_index, &node_identity_state);
2185 #endif
2186     config_node_identity_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, netkey_index, node_identity_state);
2187 
2188     mesh_access_message_processed(pdu);
2189 }
2190 
2191 static void config_node_identity_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){
2192     mesh_access_parser_state_t parser;
2193     mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu);
2194     uint16_t netkey_index = mesh_access_parser_get_u16(&parser);
2195     mesh_node_identity_state_t node_identity_state = (mesh_node_identity_state_t) mesh_access_parser_get_u8(&parser);
2196 
2197     // ignore invalid state
2198     if (node_identity_state >= MESH_NODE_IDENTITY_STATE_ADVERTISING_NOT_SUPPORTED) {
2199         mesh_access_message_processed(pdu);
2200         return;
2201     }
2202 
2203     uint8_t status = MESH_FOUNDATION_STATUS_SUCCESS;
2204 #ifdef ENABLE_MESH_PROXY_SERVER
2205     status = mesh_proxy_set_advertising_with_node_id(netkey_index, node_identity_state);
2206 #else
2207     mesh_subnet_t * network_key = mesh_subnet_get_by_netkey_index(netkey_index);
2208     if (network_key == NULL){
2209         status = MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
2210     }
2211 #endif
2212 
2213     config_node_identity_status(mesh_model, mesh_pdu_netkey_index(pdu), mesh_pdu_src(pdu), status, netkey_index, node_identity_state);
2214 
2215     mesh_access_message_processed(pdu);
2216 }
2217 
2218 //
2219 
2220 const static mesh_operation_t mesh_configuration_server_model_operations[] = {
2221     { MESH_FOUNDATION_OPERATION_APPKEY_ADD,                                  19, config_appkey_add_handler },
2222     { MESH_FOUNDATION_OPERATION_APPKEY_DELETE,                                3, config_appkey_delete_handler },
2223     { MESH_FOUNDATION_OPERATION_APPKEY_GET,                                   2, config_appkey_get_handler },
2224     { MESH_FOUNDATION_OPERATION_APPKEY_UPDATE,                               19, config_appkey_update_handler },
2225     { MESH_FOUNDATION_OPERATION_NETKEY_ADD,                                  18, config_netkey_add_handler },
2226     { MESH_FOUNDATION_OPERATION_NETKEY_UPDATE,                               18, config_netkey_update_handler },
2227     { MESH_FOUNDATION_OPERATION_NETKEY_DELETE,                                2, config_netkey_delete_handler },
2228     { MESH_FOUNDATION_OPERATION_NETKEY_GET,                                   0, config_netkey_get_handler },
2229     { MESH_FOUNDATION_OPERATION_COMPOSITION_DATA_GET,                         1, config_composition_data_get_handler },
2230     { MESH_FOUNDATION_OPERATION_BEACON_GET,                                   0, config_beacon_get_handler },
2231     { MESH_FOUNDATION_OPERATION_BEACON_SET,                                   1, config_beacon_set_handler },
2232     { MESH_FOUNDATION_OPERATION_DEFAULT_TTL_GET,                              0, config_default_ttl_get_handler },
2233     { MESH_FOUNDATION_OPERATION_DEFAULT_TTL_SET,                              1, config_default_ttl_set_handler },
2234     { MESH_FOUNDATION_OPERATION_FRIEND_GET,                                   0, config_friend_get_handler },
2235     { MESH_FOUNDATION_OPERATION_FRIEND_SET,                                   1, config_friend_set_handler },
2236     { MESH_FOUNDATION_OPERATION_NETWORK_TRANSMIT_GET,                         0, config_model_network_transmit_get_handler },
2237     { MESH_FOUNDATION_OPERATION_NETWORK_TRANSMIT_SET,                         1, config_model_network_transmit_set_handler },
2238     { MESH_FOUNDATION_OPERATION_GATT_PROXY_GET,                               0, config_gatt_proxy_get_handler },
2239     { MESH_FOUNDATION_OPERATION_GATT_PROXY_SET,                               1, config_gatt_proxy_set_handler },
2240     { MESH_FOUNDATION_OPERATION_RELAY_GET,                                    0, config_relay_get_handler },
2241     { MESH_FOUNDATION_OPERATION_RELAY_SET,                                    1, config_relay_set_handler },
2242     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_ADD,                       6, config_model_subscription_add_handler },
2243     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_VIRTUAL_ADDRESS_ADD,      20, config_model_subscription_virtual_address_add_handler },
2244     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_DELETE,                    6, config_model_subscription_delete_handler },
2245     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_VIRTUAL_ADDRESS_DELETE,   20, config_model_subscription_virtual_address_delete_handler },
2246     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_OVERWRITE,                 6, config_model_subscription_overwrite_handler },
2247     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_VIRTUAL_ADDRESS_OVERWRITE,20, config_model_subscription_virtual_address_overwrite_handler },
2248     { MESH_FOUNDATION_OPERATION_MODEL_SUBSCRIPTION_DELETE_ALL,                4, config_model_subscription_delete_all_handler },
2249     { MESH_FOUNDATION_OPERATION_SIG_MODEL_SUBSCRIPTION_GET,                   4, config_model_subscription_get_handler },
2250     { MESH_FOUNDATION_OPERATION_VENDOR_MODEL_SUBSCRIPTION_GET,                6, config_model_subscription_get_handler },
2251     { MESH_FOUNDATION_OPERATION_SIG_MODEL_APP_GET,                            4, config_model_app_get_handler },
2252     { MESH_FOUNDATION_OPERATION_VENDOR_MODEL_APP_GET,                         6, config_model_app_get_handler },
2253     { MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_SET,                       11, config_model_publication_set_handler },
2254     { MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_VIRTUAL_ADDRESS_SET,       25, config_model_publication_virtual_address_set_handler },
2255     { MESH_FOUNDATION_OPERATION_MODEL_PUBLICATION_GET,                        4, config_model_publication_get_handler },
2256     { MESH_FOUNDATION_OPERATION_MODEL_APP_BIND,                               6, config_model_app_bind_handler },
2257     { MESH_FOUNDATION_OPERATION_MODEL_APP_UNBIND,                             6, config_model_app_unbind_handler },
2258     { MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_GET,                    0, config_heartbeat_publication_get_handler },
2259     { MESH_FOUNDATION_OPERATION_HEARTBEAT_PUBLICATION_SET,                    9, config_heartbeat_publication_set_handler },
2260     { MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_GET,                   0, config_heartbeat_subscription_get_handler},
2261     { MESH_FOUNDATION_OPERATION_HEARTBEAT_SUBSCRIPTION_SET,                   5, config_heartbeat_subscription_set_handler},
2262     { MESH_FOUNDATION_OPERATION_KEY_REFRESH_PHASE_GET,                        2, config_key_refresh_phase_get_handler },
2263     { MESH_FOUNDATION_OPERATION_KEY_REFRESH_PHASE_SET,                        3, config_key_refresh_phase_set_handler },
2264     { MESH_FOUNDATION_OPERATION_NODE_RESET,                                   0, config_node_reset_handler },
2265     { MESH_FOUNDATION_OPERATION_LOW_POWER_NODE_POLL_TIMEOUT_GET,              2, config_low_power_node_poll_timeout_get_handler },
2266     { MESH_FOUNDATION_OPERATION_NODE_IDENTITY_GET,                            2, config_node_identity_get_handler },
2267     { MESH_FOUNDATION_OPERATION_NODE_IDENTITY_SET,                            3, config_node_identity_set_handler },
2268     { 0, 0, NULL }
2269 };
2270 
2271 const mesh_operation_t * mesh_configuration_server_get_operations(void){
2272     return mesh_configuration_server_model_operations;
2273 }
2274 
2275 void mesh_configuration_server_process_heartbeat(mesh_model_t * configuration_server_model, uint16_t src, uint16_t dest, uint8_t hops, uint16_t features){
2276     UNUSED(features);
2277     mesh_heartbeat_subscription_t * mesh_heartbeat_subscription = &((mesh_configuration_server_model_context_t*) configuration_server_model->model_data)->heartbeat_subscription;
2278     if (config_heartbeat_subscription_get_period_remaining_s(mesh_heartbeat_subscription) == 0) return;
2279     if (mesh_heartbeat_subscription->source != src) return;
2280     if (mesh_heartbeat_subscription->destination != dest) return;
2281     // update count
2282     if (mesh_heartbeat_subscription->count != 0xffff){
2283         mesh_heartbeat_subscription->count++;
2284     }
2285     // update min/max hops
2286     mesh_heartbeat_subscription->min_hops = (uint8_t) btstack_min(mesh_heartbeat_subscription->min_hops, hops);
2287     mesh_heartbeat_subscription->max_hops = (uint8_t) btstack_max(mesh_heartbeat_subscription->max_hops, hops);
2288 
2289     printf("HEARTBEAT, count %u, min %u, max %u hops\n", mesh_heartbeat_subscription->count, mesh_heartbeat_subscription->min_hops, mesh_heartbeat_subscription->max_hops);
2290 }
2291