From 047f254657d86a6bcf7f3517a4968193e7659793 Mon Sep 17 00:00:00 2001
From: Matthias Ringwald <matthias@ringwald.ch>
Date: Fri, 16 Aug 2019 16:02:33 +0200
Subject: [PATCH] mesh: support advertising with node identity on all subnets

---
 src/mesh/mesh_network.h |   4 ++
 src/mesh/mesh_proxy.c   | 103 +++++++++++++++++++++++++---------------
 src/mesh/mesh_proxy.h   |  10 ++++
 test/mesh/mesh_pts.c    |  10 ++++
 4 files changed, 89 insertions(+), 38 deletions(-)

diff --git a/src/mesh/mesh_network.h b/src/mesh/mesh_network.h
index af71b937a..2f272b0ca 100644
--- a/src/mesh/mesh_network.h
+++ b/src/mesh/mesh_network.h
@@ -173,9 +173,13 @@ typedef struct {
     // advertisement using node id active
     uint8_t node_id_advertisement_running;
 
+
     // advertisement using network id (used by proxy)
     adv_bearer_connectable_advertisement_data_item_t advertisement_with_network_id;
 
+    // advertising using node id (used by proxy)
+    adv_bearer_connectable_advertisement_data_item_t advertisement_with_node_id;
+
     // secure network beacons
     mesh_secure_network_beacon_state_t beacon_state;
     uint32_t                           beacon_interval_ms;
diff --git a/src/mesh/mesh_proxy.c b/src/mesh/mesh_proxy.c
index ebb48129b..36a376bcf 100644
--- a/src/mesh/mesh_proxy.c
+++ b/src/mesh/mesh_proxy.c
@@ -60,6 +60,9 @@
 #include "mesh/mesh_network.h"
 #include "mesh/mesh_node.h"
 
+static void mesh_proxy_start_advertising_with_node_id_next_subnet(void);
+static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet);
+
 #ifdef ENABLE_MESH_PROXY_SERVER
 
 // we only support a single active node id advertisement. when new one is started, an active one is stopped
@@ -72,13 +75,14 @@ static btstack_crypto_aes128_t                          mesh_proxy_node_id_crypt
 static uint8_t                                          mesh_proxy_node_id_plaintext[16];
 static uint8_t                                          mesh_proxy_node_id_hash[16];
 static uint8_t                                          mesh_proxy_node_id_random_value[8];
+static uint8_t                                          mesh_proxy_node_id_all_subnets;
 
 static uint16_t primary_element_address;
 
-// Mesh Proxy, advertise with node id
-static adv_bearer_connectable_advertisement_data_item_t connectable_advertisement;
+// Mesh Proxy, advertise unprovisioned device
+static adv_bearer_connectable_advertisement_data_item_t connectable_advertisement_unprovisioned_device;
 
-static const uint8_t adv_data_with_node_identity_template[] = {
+static const uint8_t adv_data_with_node_id_template[] = {
     // Flags general discoverable, BR/EDR not supported
     0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, 
     // 16-bit Service UUIDs
@@ -129,9 +133,9 @@ static void mesh_proxy_setup_advertising_unprovisioned(adv_bearer_connectable_ad
 
 void mesh_proxy_start_advertising_unprovisioned_device(void){
     const uint8_t * device_uuid = mesh_node_get_device_uuid();
-    mesh_proxy_setup_advertising_unprovisioned(&connectable_advertisement, device_uuid);
+    mesh_proxy_setup_advertising_unprovisioned(&connectable_advertisement_unprovisioned_device, device_uuid);
     // setup advertisements
-    adv_bearer_advertisements_add_item(&connectable_advertisement);
+    adv_bearer_advertisements_add_item(&connectable_advertisement_unprovisioned_device);
     adv_bearer_advertisements_enable(1);
 }
 
@@ -140,7 +144,7 @@ void mesh_proxy_start_advertising_unprovisioned_device(void){
  * @param device_uuid
  */
 void mesh_proxy_stop_advertising_unprovisioned_device(void){
-    adv_bearer_advertisements_remove_item(&connectable_advertisement);
+    adv_bearer_advertisements_remove_item(&connectable_advertisement_unprovisioned_device);
 }
 
 #endif
@@ -151,17 +155,22 @@ static uint8_t mesh_proxy_setup_advertising_with_network_id(uint8_t * buffer, ui
     return 20;
 }
 
+static void mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
+    if (mesh_subnet->node_id_advertisement_running != 0){
+        adv_bearer_advertisements_remove_item(&mesh_subnet->advertisement_with_node_id);
+        mesh_subnet->node_id_advertisement_running = 0;
+    }
+}
+
 static void mesh_proxy_stop_all_advertising_with_node_id(void){
-    adv_bearer_advertisements_remove_item(&connectable_advertisement);
     mesh_subnet_iterator_t it;
     mesh_subnet_iterator_init(&it);
     while (mesh_subnet_iterator_has_more(&it)){
-        mesh_subnet_t * network_key = mesh_subnet_iterator_get_next(&it);
-        if (network_key->node_id_advertisement_running != 0){
-            btstack_run_loop_remove_timer(&mesh_proxy_node_id_timer);
-            network_key->node_id_advertisement_running = 0;
-        }
+        mesh_subnet_t * mesh_subnet = mesh_subnet_iterator_get_next(&it);
+        mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
     }
+    btstack_run_loop_remove_timer(&mesh_proxy_node_id_timer);
+    mesh_proxy_node_id_all_subnets = 0;
 }
 
 static void mesh_proxy_node_id_timeout_handler(btstack_timer_source_t * ts){
@@ -170,15 +179,15 @@ static void mesh_proxy_node_id_timeout_handler(btstack_timer_source_t * ts){
 }
 
 static void mesh_proxy_node_id_handle_get_aes128(void * arg){
-    mesh_subnet_t * network_key = (mesh_subnet_t *) arg;
+    mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
 
-    memcpy(connectable_advertisement.adv_data, adv_data_with_node_identity_template, 12);
-    memcpy(&connectable_advertisement.adv_data[12], &mesh_proxy_node_id_hash[8], 8);
-    memcpy(&connectable_advertisement.adv_data[20], mesh_proxy_node_id_random_value, 8);
-    connectable_advertisement.adv_length = 28;
+    memcpy(mesh_subnet->advertisement_with_node_id.adv_data, adv_data_with_node_id_template, 12);
+    memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[12], &mesh_proxy_node_id_hash[8], 8);
+    memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[20], mesh_proxy_node_id_random_value, 8);
+    mesh_subnet->advertisement_with_node_id.adv_length = 28;
     
     // setup advertisements
-    adv_bearer_advertisements_add_item(&connectable_advertisement);
+    adv_bearer_advertisements_add_item(&mesh_subnet->advertisement_with_node_id);
     adv_bearer_advertisements_enable(1);
 
     // set timer
@@ -187,34 +196,29 @@ static void mesh_proxy_node_id_handle_get_aes128(void * arg){
     btstack_run_loop_add_timer(&mesh_proxy_node_id_timer);
 
     // mark as active
-    network_key->node_id_advertisement_running = 1;
+    mesh_subnet->node_id_advertisement_running = 1;
+
+    // next one
+    if (mesh_proxy_node_id_all_subnets == 0) return;
+    mesh_proxy_start_advertising_with_node_id_next_subnet();
 }
 
 static void mesh_proxy_node_id_handle_random(void * arg){
-    mesh_subnet_t * network_key = (mesh_subnet_t *) arg;
+    mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
 
     // Hash = e(IdentityKey, Padding | Random | Address) mod 2^64
     memset(mesh_proxy_node_id_plaintext, 0, sizeof(mesh_proxy_node_id_plaintext));
     memcpy(&mesh_proxy_node_id_plaintext[6] , mesh_proxy_node_id_random_value, 8);
     big_endian_store_16(mesh_proxy_node_id_plaintext, 14, primary_element_address);
     // TODO: old vs. new key
-    btstack_crypto_aes128_encrypt(&mesh_proxy_node_id_crypto_request_aes128, network_key->old_key->identity_key, mesh_proxy_node_id_plaintext, mesh_proxy_node_id_hash, mesh_proxy_node_id_handle_get_aes128, network_key);
+    btstack_crypto_aes128_encrypt(&mesh_proxy_node_id_crypto_request_aes128, mesh_subnet->old_key->identity_key, mesh_proxy_node_id_plaintext, mesh_proxy_node_id_hash, mesh_proxy_node_id_handle_get_aes128, mesh_subnet);
 }
 
-static void mesh_proxy_start_advertising_with_node_id(uint16_t netkey_index){
-    mesh_proxy_stop_all_advertising_with_node_id();
-    // get network key
-    mesh_subnet_t * network_key = mesh_subnet_get_by_netkey_index(netkey_index);
-    if (network_key == NULL) return;
-    log_info("Proxy start advertising with node id, netkey index %04x", netkey_index);
+static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
+    if (mesh_subnet->node_id_advertisement_running) return;
+    log_info("Proxy start advertising with node id, netkey index %04x", mesh_subnet->netkey_index);
     // setup node id
-    btstack_crypto_random_generate(&mesh_proxy_node_id_crypto_request_random, mesh_proxy_node_id_random_value, sizeof(mesh_proxy_node_id_random_value), mesh_proxy_node_id_handle_random, network_key);
-}
-
-static void mesh_proxy_stop_advertising_with_node_id(uint16_t netkey_index){
-    UNUSED(netkey_index);
-    log_info("Proxy stop advertising with node id, netkey index %04x", netkey_index);
-    mesh_proxy_stop_all_advertising_with_node_id();
+    btstack_crypto_random_generate(&mesh_proxy_node_id_crypto_request_random, mesh_proxy_node_id_random_value, sizeof(mesh_proxy_node_id_random_value), mesh_proxy_node_id_handle_random, mesh_subnet);
 }
 
 // Public API
@@ -240,18 +244,18 @@ uint8_t mesh_proxy_get_advertising_with_node_id_status(uint16_t netkey_index, me
 }
 
 uint8_t mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index, mesh_node_identity_state_t state){
-    mesh_subnet_t * network_key = mesh_subnet_get_by_netkey_index(netkey_index);
-    if (network_key == NULL){
+    mesh_subnet_t * mesh_subnet = mesh_subnet_get_by_netkey_index(netkey_index);
+    if (mesh_subnet == NULL){
         return  MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
     }
 
 #ifdef ENABLE_MESH_PROXY_SERVER
     switch (state){
         case MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED:
-            mesh_proxy_stop_advertising_with_node_id(netkey_index);
+            mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
             return MESH_FOUNDATION_STATUS_SUCCESS;
         case MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING:
-            mesh_proxy_start_advertising_with_node_id(netkey_index);
+            mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet);
             return MESH_FOUNDATION_STATUS_SUCCESS;
         default:
             break;
@@ -261,6 +265,29 @@ uint8_t mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index, mesh_node
     return MESH_FOUNDATION_STATUS_FEATURE_NOT_SUPPORTED;
 }
 
+static void mesh_proxy_start_advertising_with_node_id_next_subnet(void){
+    mesh_subnet_iterator_t it;
+    mesh_subnet_iterator_init(&it);
+    while (mesh_subnet_iterator_has_more(&it)){
+        mesh_subnet_t * subnet = mesh_subnet_iterator_get_next(&it);
+        if (subnet->node_id_advertisement_running != 0) continue;
+
+        mesh_proxy_start_advertising_with_node_id_for_subnet(subnet);
+        return;
+    }
+}
+
+void mesh_proxy_start_advertising_with_node_id(void){
+    mesh_proxy_node_id_all_subnets = 1;
+    adv_bearer_advertisements_enable(1);
+    
+    // start advertising on first subnet that is not already advertising with node id    
+    mesh_proxy_start_advertising_with_node_id_next_subnet();
+}
+
+void mesh_proxy_stop_advertising_with_node_id(void){
+    mesh_proxy_stop_all_advertising_with_node_id();
+}
 
 void mesh_proxy_start_advertising_with_network_id(void){
     mesh_subnet_iterator_t it;
diff --git a/src/mesh/mesh_proxy.h b/src/mesh/mesh_proxy.h
index 3d82397fa..347379cb2 100644
--- a/src/mesh/mesh_proxy.h
+++ b/src/mesh/mesh_proxy.h
@@ -86,6 +86,16 @@ uint8_t mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index, mesh_node
  */
 uint8_t mesh_proxy_get_advertising_with_node_id_status(uint16_t netkey_index, mesh_node_identity_state_t * out_state );
 
+/**
+ * @brief Start Advertising with Node Identity (on all subnets)
+ */
+void mesh_proxy_start_advertising_with_node_id(void);
+
+/**
+ * @brief Stop Advertising with Node Identity (on all subnets)
+ */
+void mesh_proxy_stop_advertising_with_node_id(void);
+
 /**
  * @brief Start Advertising with Network ID (on all subnets)
  */
diff --git a/test/mesh/mesh_pts.c b/test/mesh/mesh_pts.c
index 1643511dc..58f2ee5cf 100644
--- a/test/mesh/mesh_pts.c
+++ b/test/mesh/mesh_pts.c
@@ -466,6 +466,8 @@ static void show_usage(void){
     printf("i      - Input  Output OOB \n");
     printf("s      - Static Output OOB \n");
     printf("b      - Set Secure Network Beacon %s\n", mesh_foundation_beacon_get() ? "Off" : "On");
+    printf("n      - Start Advertising with Node Identity\n");
+    printf("N      - Stop Advertising with Node Identity\n");
     printf("g      - Generic ON/OFF Server Toggle Value\n");
     printf("\n");
 }
@@ -544,6 +546,14 @@ static void stdin_process(char cmd){
             mesh_foundation_beacon_set(1 - mesh_foundation_beacon_get());
             mesh_foundation_state_store();
             break;
+        case 'n':
+            printf("Start Advertising with Node ID\n");
+            mesh_proxy_start_advertising_with_node_id();
+            break;
+        case 'N':
+            printf("Stop Advertising with Node ID\n");
+            mesh_proxy_start_advertising_with_node_id();
+            break;
         case ' ':
             show_usage();
             break;