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