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