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