From patchwork Mon Jan 23 19:48:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112867 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BAC6C38142 for ; Mon, 23 Jan 2023 19:49:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232171AbjAWTtG (ORCPT ); Mon, 23 Jan 2023 14:49:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232242AbjAWTs6 (ORCPT ); Mon, 23 Jan 2023 14:48:58 -0500 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9028C3668B for ; Mon, 23 Jan 2023 11:48:44 -0800 (PST) Received: by mail-pj1-x1033.google.com with SMTP id h5-20020a17090a9c0500b0022bb85eb35dso7593477pjp.3 for ; Mon, 23 Jan 2023 11:48:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9tI5BOYIiJ/ZNujPsyZPCPXDEbjxHMzubZ8HJ7es34c=; b=GoN+PijKLw9Kd+Gd6Lay4RJfQIjmZ4nQhjKycCwYuM61Z5aLFIPr215f0gXEjxYTRe +IGPn9xuVF5KNKNHtm8pRFulyeprNj9RbZr4Z3BAiTp5Blupt/aMmQ+YmP6rnMbnCEO9 rBYy6ObB+1qqgs/VUBXF7NrxqFiZbyVG/MUMzXTum6ZDznSjvP0Rl235k2Zwmz1mwrFF e+3M2Bw6XJfXbceFLGIiYhI1yCX1LHUYQY0gFF1AM3n3ihib7f1laTM3uVD8cW27RC0W Dxwx7zOr52LIIj7FF7tcGEIaFN2fScUbAcbW1ksl44khVxZio2+zHe2w4CXKrAchuLvc pVug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9tI5BOYIiJ/ZNujPsyZPCPXDEbjxHMzubZ8HJ7es34c=; b=AWsRXY7qfowMXszEDQH3Fylq95rLgM9U3BnfA5P6EBK+XEyjUyhNiUB419BUylbDkG QIMKOM3MV2wywI7kIi1hp32RHIEsSYmp0+FmoNMF3y45Adk2P55GzOTu6HHyIcyccAi6 NNtwCqHsDjoD/ruytpOxP+unYLerFte4pHCDdrcU0JuPC68Lz/brdyISHF673bQHaxfz 3kJyOlvCtw4FdKT7TQTsX5hqR022QTLm0l0ByPsTbTd35JqTo37G/YruuAhcCrc/+tML g6mj4yISZjxov+KFmF0pew7S7i3POzggu6HhNxdWrtIzZjdOBR/8oxX97mpMyT8t/vbN 0y8w== X-Gm-Message-State: AFqh2kqcm43QkwiVmawEVXN462BBS8Ed8rPn4RdUHK/PSF0VmnShJ5Uk JIktxnhpE/nQpjJ3C0LVu39pWLpKkMfHuQ== X-Google-Smtp-Source: AMrXdXs5xw8uwj/qUbCVkwxdNb97+ERbT7WWY1BeB+N9ykuk9sVuPUhkot6c1sPZ1EzaWJ2dRtY4aQ== X-Received: by 2002:a17:902:ea10:b0:195:e5a0:9acb with SMTP id s16-20020a170902ea1000b00195e5a09acbmr17980023plg.6.1674503323364; Mon, 23 Jan 2023 11:48:43 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:42 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 01/11] doc/mesh: Add Remote Provisioning DBus APIs Date: Mon, 23 Jan 2023 11:48:14 -0800 Message-Id: <20230123194824.257351-2-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix Remote Provisioning (introduced in Mesh Profile Specification v1.1) * Allows Provisioners to use a remote server to scan for and provision devices. * Allows Config managers to reprovision existing nodes to: * Refresh Device Keys * Reassign Node Addresses * Refresh Node Composition --- doc/mesh-api.txt | 140 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 6 deletions(-) diff --git a/doc/mesh-api.txt b/doc/mesh-api.txt index 85de6705e..393ae566f 100644 --- a/doc/mesh-api.txt +++ b/doc/mesh-api.txt @@ -484,7 +484,28 @@ Methods: Specifies number of seconds for scanning to be active. If set to 0 or if this key is not present, then the scanning will continue until UnprovisionedScanCancel() - or AddNode() methods are called. + or AddNode() methods are called. If not present, and a + remote server is specified, the default timeout will be + 60 seconds. + + uint16 Server + Specifies a remote server on which to perform scanning. + If not present, scanning will be local. If present, + the timeout must be between 1 and 60 seconds. + + uint16 Subnet + Specifies a subnet for the remote server. If not + present, primary subnet will be used. If Server not + present, the Subnet will be ignored. + + array{byte}[16] Filter + Specifies a specific UUID to search for. If not + present, all found UUIDs will be returned. + + uint8 array Extended + Specifies variable number of Bluetooth AD types to + return with scan result. Only valid if a Filter has been + specified. Each time a unique unprovisioned beacon is heard, the ScanResult() method on the app will be called with the result. @@ -514,8 +535,48 @@ Methods: of the unprovisioned device to be added to the network. The options parameter is a dictionary that may contain - additional configuration info (currently an empty placeholder - for forward compatibility). + additional optional configuration info: + + uint16 Server + Specifies a remote server to perform provisioning on. If + not present, provisioning will be done locally. + + uint16 Subnet + Specifies a subnet for the remote server. If not + present, primary subnet will be used. If Server not + present Subnet will be ignored. + + PossibleErrors: + org.bluez.mesh.Error.InvalidArguments + org.bluez.mesh.Error.NotAuthorized + + void Reprovision(uint16 unicast, dict options) + + This method is used by the application that supports + org.bluez.mesh.Provisioner1 interface to perform one of the + Node Provisioning Protocol Interface procedures with a remote + node to refresh its device key, unicast address, and + composition. Remote node being reprovisioned must have the + Remote Provisioning Server model. + + The unicast parameter is the 16-bit primary node address of + the remote node being reprovisioned. + + The options parameter is a dictionary that may contain + additional optional configuration info: + + uint8 NPPI + Specifies the Node Provisioning Protocol Interface + behavior, as defined in the Mesh Profile Specification: + 0 - Device Key Refresh Only + 1 - Node Address Refresh + 2 - Node Composition Refresh + + If not present, behavior 0 will be used. + + uint16 Subnet + Specifies the subnet remote node is on. If not + present, primary subnet will be tried. PossibleErrors: org.bluez.mesh.Error.InvalidArguments @@ -1055,11 +1116,19 @@ Object path freely definable byte remote device UUID (always), a 16 bit mask of OOB authentication flags (optional), and a 32 bit URI hash (if URI bit set in OOB mask). Whether these fields exist or not is a - decision of the remote device. + decision of the unprovisioned device. The options parameter is a dictionary that may contain - additional scan result info (currently an empty placeholder for - forward compatibility). + additional optional configuration info: + + uint16 Server + Specifies the remote server that received the + Unprovisioned beacon. If not present, beacon was + received locally. + + uint8 array ExtendedData + If Extended data was requested during scanning, any + received data will be returned here. If a beacon with a UUID that has already been reported is recieved by the daemon, it will be silently discarded unless it @@ -1082,6 +1151,26 @@ Object path freely definable PossibleErrors: org.bluez.mesh.Error.Abort + uint16 unicast RequestReprovData(uint16 original, uint8 count) + + This method is implemented by a Provisioner capable application + and is called when the remote node being reprovisioned has been + fully authenticated and confirmed. This method will only be + called if the NPPI-1 procedure (Node Address Refresh) is being + performed. + + The original parameter is the current unicast address of the + node being reprovisioned. + + The count parameter is the number of consecutive unicast + addresses the remote node is requesting. + + Return Parameter: + unicast - Primary Unicast address of the new node + + PossibleErrors: + org.bluez.mesh.Error.Abort + void AddNodeComplete(array{byte}[16] uuid, uint16 unicast, uint8 count) This method is called when the node provisioning initiated @@ -1096,6 +1185,33 @@ Object path freely definable The new node may now be sent messages using the credentials supplied by the RequestProvData method. + void ReprovComplete(uint16 original, uint8 nppi, uint16 unicast, + uint8 count) + + This method is called when the node Reprovisioning initiated + by a Reprovision() method call successfully completed. + + The original parameter is the former primary address of the + node that has been reprovisioned. + + The nppi parameter indicates which NPPI behavior was performed. + If behavior 1 or 2 was performed, the node is materially + different than it was before reprovisioning, and Composition, + Bindings, Publication and Subscription settings should be + refreshed. + + The unicast parameter is the new primary address that has been + assigned to the node, If NPPI behavior 1 was performed this + value may be different from the original. If behavior 0 or 2 + was performed, the original and new primary address should be + the same. + + The count parameter is the number of unicast addresses assigned + to the node. + + The node may now be sent messages using the credentials + supplied by the RequestReprovData method. + void AddNodeFailed(array{byte}[16] uuid, string reason) This method is called when the node provisioning initiated by @@ -1109,6 +1225,18 @@ Object path freely definable "decryption-error", "unexpected-error", "cannot-assign-addresses". + void ReprovFailed(uint16 unicast, string reason) + + This method is called when node reprovisioning initiated by + Reprovision() has failed. If reprovisioning has failed, the + prior credentials of the remote node may still be valid. + + The reason parameter identifies the reason for provisioning + failure. The defined values are: "aborted", "timeout", + "bad-pdu", "confirmation-failed", "out-of-resources", + "decryption-error", "unexpected-error", + "cannot-assign-addresses". + Provisioning Agent Hierarchy ============================ Service unique name From patchwork Mon Jan 23 19:48:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112871 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4F6FEC38142 for ; Mon, 23 Jan 2023 19:49:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231871AbjAWTtI (ORCPT ); Mon, 23 Jan 2023 14:49:08 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53724 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232960AbjAWTs7 (ORCPT ); Mon, 23 Jan 2023 14:48:59 -0500 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B2A2135275 for ; Mon, 23 Jan 2023 11:48:46 -0800 (PST) Received: by mail-pj1-x1032.google.com with SMTP id x2-20020a17090a46c200b002295ca9855aso16306904pjg.2 for ; Mon, 23 Jan 2023 11:48:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=jKBQN5+r4aDuBivQIq990k9W+Swa8Q2JDIJh8UHZK2Q=; b=qrV0GK1/htMUOz1cAyT+tOaUxa6LeWm9+6LNU4JZSxGecw7vw7kwDh/Ps4ThNAhjQL tZmHSQQ0n5LXPtASEkbtcPlc3zaASVly1bfLorE1hERKquii1IK7r36gkTa1beIcVn0j fY2GX7ggWi4wXiX1sLfhshq+XRzU47P/dAnurrx0jEQk8zwFYs5AoiGC+mRMgK18LtAx 3Uh2hRlIFp+CBgZb1AZoOI1s317Z/SwEQlIHBUlHG5M6s7HENtuOM1b+FKKTCx6j+v66 tnWksXuN/rX58Cj2ZbneNnrLiqeCmv+3wrUdcAF5cedUixi0fRMEri4nf+htc+KleCki +DwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=jKBQN5+r4aDuBivQIq990k9W+Swa8Q2JDIJh8UHZK2Q=; b=sWHHMkHD25KYVEY9RS6ma2h5ZBpS54P6GB5/WHYhQDwdpDigYZXy4F4oxa4gt3ZFuf 3iVY3spbJlMlrguxgbV3SsAFURj8AORNNd1MsSvjH8gnhGTSi+4Apdz5h773Axu9WXIS CslSestso+5CY6lAXpNZWv23z/crvOjjXbJQ5zqj+sQyYfMfU2OMvS1GS1x9D+IyEkEg yhK8pnTT4Bgif+bHh/lghxGZlps6wuVjal9SyrW4pBe6alHXHYCr8PoV0C/hmE5Zdhsw sYvwoA/Hisgs36jcx996ijayfEorpYxy+2lCbsDJ3+uSqq57k/J1IRo2EsCHJsJhD+VF VsGA== X-Gm-Message-State: AFqh2kol8NXTG8ekAUELcq9RkC3TmOYS7ofUQ/1LFL/RczhEw3xqDmmz Ot1I34aQ7dF/+X8dhz/5FRSwbv05R4PFcQ== X-Google-Smtp-Source: AMrXdXvx1dIbi/nQvPZbwPJsrRxfhhSr+7+X5Mm3Epu7WlT4n86c0279dtEIH3rr5Mq+TBzEiW1r/Q== X-Received: by 2002:a17:902:ecc2:b0:194:df47:2b42 with SMTP id a2-20020a170902ecc200b00194df472b42mr18685876plh.48.1674503324514; Mon, 23 Jan 2023 11:48:44 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:43 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 02/11] mesh: Add Remote Provisioning Date: Mon, 23 Jan 2023 11:48:15 -0800 Message-Id: <20230123194824.257351-3-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix Add Remote Provisioning Server Add Remote Provisioning Client Remove local scanning/provisioning Add delete-all dev key function Add NPPI procedures --- Makefile.mesh | 1 + mesh/cfgmod-server.c | 2 +- mesh/keyring.c | 29 +- mesh/keyring.h | 1 + mesh/manager.c | 533 ++++++++++++++++++----- mesh/mesh-config-json.c | 380 +++++++++++------ mesh/mesh-config.h | 6 +- mesh/model.c | 27 +- mesh/node.c | 255 +++++++++-- mesh/node.h | 3 + mesh/pb-adv.c | 4 +- mesh/pb-adv.h | 2 +- mesh/prov-acceptor.c | 87 ++-- mesh/prov-initiator.c | 269 +++++++++++- mesh/prov.h | 4 +- mesh/provision.h | 23 +- mesh/remprv-server.c | 907 ++++++++++++++++++++++++++++++++++++++++ mesh/remprv.h | 78 ++++ 18 files changed, 2245 insertions(+), 366 deletions(-) create mode 100644 mesh/remprv-server.c create mode 100644 mesh/remprv.h diff --git a/Makefile.mesh b/Makefile.mesh index 3047f362b..e18a169eb 100644 --- a/Makefile.mesh +++ b/Makefile.mesh @@ -26,6 +26,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \ mesh/provision.h mesh/prov.h \ mesh/model.h mesh/model.c \ mesh/cfgmod.h mesh/cfgmod-server.c \ + mesh/remprv.h mesh/remprv-server.c \ mesh/mesh-config.h mesh/mesh-config-json.c \ mesh/util.h mesh/util.c \ mesh/dbus.h mesh/dbus.c \ diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c index be90ef8c5..3d7efc44b 100644 --- a/mesh/cfgmod-server.c +++ b/mesh/cfgmod-server.c @@ -30,8 +30,8 @@ (SET_ID(SIG_VENDOR, l_get_le16(pkt)))) /* Supported composition pages, sorted high to low */ -/* Only page 0 is currently supported */ static const uint8_t supported_pages[] = { + 128, 0 }; diff --git a/mesh/keyring.c b/mesh/keyring.c index 995a4b88f..894fb14fa 100644 --- a/mesh/keyring.c +++ b/mesh/keyring.c @@ -30,9 +30,9 @@ #include "mesh/node.h" #include "mesh/keyring.h" -const char *dev_key_dir = "/dev_keys"; -const char *app_key_dir = "/app_keys"; -const char *net_key_dir = "/net_keys"; +static const char *dev_key_dir = "/dev_keys"; +static const char *app_key_dir = "/app_keys"; +static const char *net_key_dir = "/net_keys"; static int open_key_file(struct mesh_node *node, const char *key_dir, uint16_t idx, int flags) @@ -295,6 +295,7 @@ bool keyring_get_remote_dev_key(struct mesh_node *node, uint16_t unicast, close(fd); } + return result; } @@ -371,6 +372,28 @@ bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast, return true; } +bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast) +{ + uint8_t dev_key[16]; + uint8_t test_key[16]; + uint8_t cnt = 1; + + if (!keyring_get_remote_dev_key(node, unicast, dev_key)) + return false; + + while (keyring_get_remote_dev_key(node, unicast + cnt, test_key)) { + if (memcmp(dev_key, test_key, sizeof(dev_key))) + break; + + cnt++; + } + + if (cnt > 1) + return keyring_del_remote_dev_key(node, unicast + 1, cnt - 1); + + return true; +} + static DIR *open_key_dir(const char *node_path, const char *key_dir_name) { char dir_path[PATH_MAX]; diff --git a/mesh/keyring.h b/mesh/keyring.h index ecf62cbc1..efc499ac2 100644 --- a/mesh/keyring.h +++ b/mesh/keyring.h @@ -39,5 +39,6 @@ bool keyring_put_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count, uint8_t dev_key[16]); bool keyring_del_remote_dev_key(struct mesh_node *node, uint16_t unicast, uint8_t count); +bool keyring_del_remote_dev_key_all(struct mesh_node *node, uint16_t unicast); bool keyring_build_export_keys_reply(struct mesh_node *node, struct l_dbus_message_builder *builder); diff --git a/mesh/manager.c b/mesh/manager.c index e66b1a45b..0730fa266 100644 --- a/mesh/manager.c +++ b/mesh/manager.c @@ -21,75 +21,137 @@ #include "mesh/mesh.h" #include "mesh/mesh-io.h" #include "mesh/node.h" +#include "mesh/model.h" #include "mesh/net.h" #include "mesh/keyring.h" #include "mesh/agent.h" #include "mesh/provision.h" +#include "mesh/prov.h" +#include "mesh/remprv.h" #include "mesh/manager.h" -struct add_data{ +struct prov_remote_data { struct l_dbus_message *msg; struct mesh_agent *agent; struct mesh_node *node; uint32_t disc_watch; + uint16_t original; uint16_t primary; uint16_t net_idx; + uint8_t transport; uint8_t num_ele; uint8_t uuid[16]; }; -static int8_t scan_rssi; -static uint8_t scan_uuid[16]; -static struct mesh_node *scan_node; -static struct l_timeout *scan_timeout; -static struct add_data *add_pending; +struct scan_req { + struct mesh_node *node; + struct l_timeout *timeout; + uint16_t server; + uint16_t net_idx; + uint8_t uuid[16]; + int8_t rssi; + bool ext; +}; + +static struct l_queue *scans; +static struct prov_remote_data *prov_pending; static const uint8_t prvb[2] = {MESH_AD_TYPE_BEACON, 0x00}; +static bool by_scan(const void *a, const void *b) +{ + return a == b; +} + +static bool by_node(const void *a, const void *b) +{ + const struct scan_req *req = a; + const struct mesh_node *node = b; + + return req->node == node; +} + +static bool by_node_svr(const void *a, const void *b) +{ + const struct scan_req *req = a; + const struct scan_req *test = b; + + return req->node == test->node && req->server == test->server; +} + static void scan_cancel(struct l_timeout *timeout, void *user_data) { - struct mesh_node *node = user_data; + struct scan_req *req = user_data; struct mesh_io *io; struct mesh_net *net; + uint8_t msg[4]; + int n; l_debug(""); - if (scan_timeout) - l_timeout_remove(scan_timeout); + req = l_queue_remove_if(scans, by_scan, req); + + if (!req) + return; + + l_timeout_remove(req->timeout); + + if (req->server) { + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STOP, msg); + mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE, + req->net_idx, DEFAULT_TTL, + true, n, msg); + } else { + net = node_get_net(req->node); + io = mesh_net_get_io(net); + mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb)); + } - net = node_get_net(node); - io = mesh_net_get_io(net); - mesh_io_deregister_recv_cb(io, prvb, sizeof(prvb)); - scan_node = NULL; - scan_timeout = NULL; + initiator_scan_unreg(req->node); + l_free(req); } static void free_pending_add_call() { - if (!add_pending) + if (!prov_pending) return; - if (add_pending->disc_watch) + if (prov_pending->disc_watch) l_dbus_remove_watch(dbus_get_bus(), - add_pending->disc_watch); + prov_pending->disc_watch); - if (add_pending->msg) - l_dbus_message_unref(add_pending->msg); + if (prov_pending->msg) + l_dbus_message_unref(prov_pending->msg); - l_free(add_pending); - add_pending = NULL; + l_free(prov_pending); + prov_pending = NULL; } static void prov_disc_cb(struct l_dbus *bus, void *user_data) { - if (!add_pending) + if (!prov_pending) return; - initiator_cancel(add_pending); - add_pending->disc_watch = 0; + initiator_cancel(prov_pending); + prov_pending->disc_watch = 0; free_pending_add_call(); } +static void append_dict_entry_basic(struct l_dbus_message_builder *builder, + const char *key, const char *signature, + const void *data) +{ + if (!builder) + return; + + l_dbus_message_builder_enter_dict(builder, "sv"); + l_dbus_message_builder_append_basic(builder, 's', key); + l_dbus_message_builder_enter_variant(builder, signature); + l_dbus_message_builder_append_basic(builder, signature[0], data); + l_dbus_message_builder_leave_variant(builder); + l_dbus_message_builder_leave_dict(builder); +} + static void send_add_failed(const char *owner, const char *path, uint8_t status) { @@ -102,7 +164,7 @@ static void send_add_failed(const char *owner, const char *path, "AddNodeFailed"); builder = l_dbus_message_builder_new(msg); - dbus_append_byte_array(builder, add_pending->uuid, 16); + dbus_append_byte_array(builder, prov_pending->uuid, 16); l_dbus_message_builder_append_basic(builder, 's', mesh_prov_status_str(status)); l_dbus_message_builder_finalize(builder); @@ -115,14 +177,14 @@ static void send_add_failed(const char *owner, const char *path, static bool add_cmplt(void *user_data, uint8_t status, struct mesh_prov_node_info *info) { - struct add_data *pending = user_data; + struct prov_remote_data *pending = user_data; struct mesh_node *node = pending->node; struct l_dbus *dbus = dbus_get_bus(); struct l_dbus_message_builder *builder; struct l_dbus_message *msg; bool result; - if (pending != add_pending) + if (pending != prov_pending) return false; if (status != PROV_ERR_SUCCESS) { @@ -131,7 +193,12 @@ static bool add_cmplt(void *user_data, uint8_t status, return false; } - result = keyring_put_remote_dev_key(add_pending->node, info->unicast, + /* If Unicast address changing, delete old dev key */ + if (pending->transport == PB_NPPI_01) + keyring_del_remote_dev_key_all(pending->node, + pending->original); + + result = keyring_put_remote_dev_key(pending->node, info->unicast, info->num_ele, info->device_key); if (!result) { @@ -140,13 +207,29 @@ static bool add_cmplt(void *user_data, uint8_t status, return false; } - msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), + if (pending->transport > PB_NPPI_02) + msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), node_get_app_path(node), MESH_PROVISIONER_INTERFACE, "AddNodeComplete"); + else + msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), + node_get_app_path(node), + MESH_PROVISIONER_INTERFACE, + "ReprovComplete"); builder = l_dbus_message_builder_new(msg); - dbus_append_byte_array(builder, add_pending->uuid, 16); + + if (pending->transport > PB_NPPI_02) + dbus_append_byte_array(builder, pending->uuid, 16); + else { + uint8_t nppi = (uint8_t) pending->transport; + + l_dbus_message_builder_append_basic(builder, 'q', + &pending->original); + l_dbus_message_builder_append_basic(builder, 'y', &nppi); + } + l_dbus_message_builder_append_basic(builder, 'q', &info->unicast); l_dbus_message_builder_append_basic(builder, 'y', &info->num_ele); l_dbus_message_builder_finalize(builder); @@ -161,47 +244,66 @@ static bool add_cmplt(void *user_data, uint8_t status, static void mgr_prov_data (struct l_dbus_message *reply, void *user_data) { - struct add_data *pending = user_data; + struct prov_remote_data *pending = user_data; uint16_t net_idx; uint16_t primary; - if (pending != add_pending) + if (pending != prov_pending) return; if (l_dbus_message_is_error(reply)) return; - if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, &primary)) + if (pending->transport == PB_NPPI_01) { + /* If performing NPPI, we only get new primary unicast here */ + if (!l_dbus_message_get_arguments(reply, "q", &primary)) + return; + + net_idx = pending->net_idx; + + } else if (!l_dbus_message_get_arguments(reply, "qq", &net_idx, + &primary)) return; - add_pending->primary = primary; - add_pending->net_idx = net_idx; - initiator_prov_data(net_idx, primary, add_pending); + pending->primary = primary; + pending->net_idx = net_idx; + initiator_prov_data(net_idx, primary, pending); } static bool add_data_get(void *user_data, uint8_t num_ele) { - struct add_data *pending = user_data; + struct prov_remote_data *pending = user_data; struct l_dbus_message *msg; struct l_dbus *dbus; const char *app_path; const char *sender; - if (pending != add_pending) + if (pending != prov_pending) return false; dbus = dbus_get_bus(); - app_path = node_get_app_path(add_pending->node); - sender = node_get_owner(add_pending->node); + app_path = node_get_app_path(pending->node); + sender = node_get_owner(pending->node); - msg = l_dbus_message_new_method_call(dbus, sender, app_path, + if (pending->transport > PB_NPPI_02) { + msg = l_dbus_message_new_method_call(dbus, sender, app_path, MESH_PROVISIONER_INTERFACE, "RequestProvData"); - l_dbus_message_set_arguments(msg, "y", num_ele); - l_dbus_send_with_reply(dbus, msg, mgr_prov_data, add_pending, NULL); + l_dbus_message_set_arguments(msg, "y", num_ele); + } else if (pending->transport == PB_NPPI_01) { + msg = l_dbus_message_new_method_call(dbus, sender, app_path, + MESH_PROVISIONER_INTERFACE, + "RequestReprovData"); + + l_dbus_message_set_arguments(msg, "qy", pending->original, + num_ele); + } else + return false; - add_pending->num_ele = num_ele; + l_dbus_send_with_reply(dbus, msg, mgr_prov_data, pending, NULL); + + pending->num_ele = num_ele; return true; } @@ -213,15 +315,95 @@ static void add_start(void *user_data, int err) l_debug("Start callback"); if (err == MESH_ERROR_NONE) - reply = l_dbus_message_new_method_return(add_pending->msg); + reply = l_dbus_message_new_method_return(prov_pending->msg); else - reply = dbus_error(add_pending->msg, MESH_ERROR_FAILED, + reply = dbus_error(prov_pending->msg, MESH_ERROR_FAILED, "Failed to start provisioning initiator"); l_dbus_send(dbus_get_bus(), reply); - l_dbus_message_unref(add_pending->msg); + l_dbus_message_unref(prov_pending->msg); + + prov_pending->msg = NULL; +} + +static struct l_dbus_message *reprovision_call(struct l_dbus *dbus, + struct l_dbus_message *msg, + void *user_data) +{ + struct mesh_node *node = user_data; + struct l_dbus_message_iter options, var; + struct l_dbus_message *reply; + struct mesh_net *net = node_get_net(node); + const char *key; + uint16_t subidx; + uint16_t server = 0; + uint8_t nppi = 0; + + l_debug("Reprovision request"); + + if (!l_dbus_message_get_arguments(msg, "qa{sv}", &server, &options)) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); + + if (!IS_UNICAST(server)) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad Unicast"); + + /* Default to nodes primary subnet index */ + subidx = mesh_net_get_primary_idx(net); + + /* Get Provisioning Options */ + while (l_dbus_message_iter_next_entry(&options, &key, &var)) { + bool failed = true; + + if (!strcmp(key, "NPPI")) { + if (l_dbus_message_iter_get_variant(&var, "y", &nppi)) { + if (nppi <= 2) + failed = false; + } + } else if (!strcmp(key, "Subnet")) { + if (l_dbus_message_iter_get_variant(&var, "q", + &subidx)) { + if (subidx <= MAX_KEY_IDX) + failed = false; + } + } + + if (failed) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, + "Invalid options"); + } + + /* AddNode cancels all outstanding Scanning from node */ + manager_scan_cancel(node); - add_pending->msg = NULL; + /* Invoke Prov Initiator */ + prov_pending = l_new(struct prov_remote_data, 1); + + prov_pending->transport = nppi; + prov_pending->node = node; + prov_pending->original = server; + prov_pending->agent = node_get_agent(node); + + if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) { + reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, + "Missing Interfaces"); + goto fail; + } + + prov_pending->msg = l_dbus_message_ref(msg); + initiator_start(prov_pending->transport, server, subidx, NULL, 99, 60, + prov_pending->agent, add_start, + add_data_get, add_cmplt, node, + prov_pending); + + prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, + node_get_owner(node), + prov_disc_cb, NULL, NULL); + + return NULL; +fail: + l_free(prov_pending); + prov_pending = NULL; + return reply; } static struct l_dbus_message *add_node_call(struct l_dbus *dbus, @@ -229,55 +411,101 @@ static struct l_dbus_message *add_node_call(struct l_dbus *dbus, void *user_data) { struct mesh_node *node = user_data; - struct l_dbus_message_iter iter_uuid, options; + struct l_dbus_message_iter iter_uuid, options, var; struct l_dbus_message *reply; + struct mesh_net *net = node_get_net(node); + const char *key; uint8_t *uuid; - uint32_t n = 22; + uint32_t n = 0; + uint16_t subidx; + uint16_t sec = 60; + uint16_t server = 0; l_debug("AddNode request"); if (!l_dbus_message_get_arguments(msg, "aya{sv}", &iter_uuid, &options)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); - if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) - || n != 16) + if (!l_dbus_message_iter_get_fixed_array(&iter_uuid, &uuid, &n) || + n != 16) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, "Bad device UUID"); - /* Allow AddNode to cancel Scanning if from the same node */ - if (scan_node) { - if (scan_node != node) - return dbus_error(msg, MESH_ERROR_BUSY, NULL); + /* Default to nodes primary subnet index */ + subidx = mesh_net_get_primary_idx(net); - scan_cancel(NULL, node); + /* Get Provisioning Options */ + while (l_dbus_message_iter_next_entry(&options, &key, &var)) { + bool failed = true; + + if (!strcmp(key, "Seconds")) { + if (l_dbus_message_iter_get_variant(&var, "q", &sec)) + failed = false; + } else if (!strcmp(key, "Server")) { + if (l_dbus_message_iter_get_variant(&var, "q", + &server)) { + if (server < 0x8000) + failed = false; + } + } else if (!strcmp(key, "Subnet")) { + if (l_dbus_message_iter_get_variant(&var, "q", + &subidx)) { + if (subidx <= MAX_KEY_IDX) + failed = false; + } + } + + if (failed) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, + "Invalid options"); } + /* Device Key update/Composition update requires remote server */ + if (!n && !server) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, + "Invalid options"); + + /* If no server specified, use local */ + if (!server) + server = node_get_primary(node); + + /* AddNode cancels all outstanding Scanning from node */ + manager_scan_cancel(node); + /* Invoke Prov Initiator */ - add_pending = l_new(struct add_data, 1); - memcpy(add_pending->uuid, uuid, 16); - add_pending->node = node; - add_pending->agent = node_get_agent(node); + prov_pending = l_new(struct prov_remote_data, 1); + + if (n) + memcpy(prov_pending->uuid, uuid, 16); + else + uuid = NULL; - if (!node_is_provisioner(node) || (add_pending->agent == NULL)) { + prov_pending->transport = PB_ADV; + prov_pending->node = node; + prov_pending->agent = node_get_agent(node); + + if (!node_is_provisioner(node) || (prov_pending->agent == NULL)) { l_debug("Provisioner: %d", node_is_provisioner(node)); - l_debug("Agent: %p", add_pending->agent); + l_debug("Agent: %p", prov_pending->agent); reply = dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, "Missing Interfaces"); goto fail; } - add_pending->msg = l_dbus_message_ref(msg); - initiator_start(PB_ADV, uuid, 99, 60, add_pending->agent, add_start, - add_data_get, add_cmplt, node, add_pending); + prov_pending->msg = l_dbus_message_ref(msg); + initiator_start(PB_ADV, server, subidx, uuid, 99, sec, + prov_pending->agent, add_start, + add_data_get, add_cmplt, node, + prov_pending); - add_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, + prov_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, node_get_owner(node), prov_disc_cb, NULL, NULL); return NULL; fail: - l_free(add_pending); - add_pending = NULL; + l_free(prov_pending); + prov_pending = NULL; return reply; } @@ -337,38 +565,50 @@ static struct l_dbus_message *delete_node_call(struct l_dbus *dbus, return l_dbus_message_new_method_return(msg); } -static void prov_beacon_recv(void *user_data, struct mesh_io_recv_info *info, +static void manager_scan_result(void *user_data, uint16_t server, bool ext, const uint8_t *data, uint16_t len) { - struct mesh_node *node = user_data; + struct scan_req node_svr = { + .node = user_data, + .server = server, + }; + struct scan_req *req; struct l_dbus_message_builder *builder; struct l_dbus_message *msg; struct l_dbus *dbus; int16_t rssi; - if (scan_node != node || len < sizeof(scan_uuid) + 2 || data[1] != 0x00) + l_debug("scan_result %4.4x %p", server, user_data); + req = l_queue_find(scans, by_node_svr, &node_svr); + if (!req) { + l_debug("No scan_result req"); return; + } - if (!memcmp(data + 2, scan_uuid, sizeof(scan_uuid))) { - if (info->rssi <= scan_rssi) + /* Filter repeats with weaker signal */ + if (!memcmp(data + 1, req->uuid, sizeof(req->uuid))) { + if (!ext && ((int8_t) data[0] <= req->rssi)) { + l_debug("Already Seen"); return; + } } - memcpy(scan_uuid, data + 2, sizeof(scan_uuid)); - scan_rssi = info->rssi; - rssi = info->rssi; + if (!ext && ((int8_t) data[0] > req->rssi)) + req->rssi = (int8_t) data[0]; + rssi = req->rssi; + memcpy(req->uuid, data + 1, sizeof(req->uuid)); dbus = dbus_get_bus(); - msg = l_dbus_message_new_method_call(dbus, node_get_owner(node), - node_get_app_path(node), + msg = l_dbus_message_new_method_call(dbus, node_get_owner(req->node), + node_get_app_path(req->node), MESH_PROVISIONER_INTERFACE, "ScanResult"); builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_append_basic(builder, 'n', &rssi); - dbus_append_byte_array(builder, data + 2, len -2); + dbus_append_byte_array(builder, data + 1, len - 1); l_dbus_message_builder_enter_array(builder, "{sv}"); - /* TODO: populate with options when defined */ + append_dict_entry_basic(builder, "Server", "q", &server); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); @@ -380,27 +620,71 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { - struct mesh_node *node = user_data; - uint16_t duration = 0; - struct mesh_io *io; + struct scan_req new_req = { + .node = user_data, + .server = 0, + .timeout = NULL, + .ext = false, + }; + struct scan_req *req; struct mesh_net *net; + uint8_t *uuid, *ext = NULL; + uint8_t scan_req[21]; + int n; + uint32_t ext_len; + uint32_t flen = 0; + uint16_t sec = 60; const char *key; struct l_dbus_message_iter options, var; const char *sender = l_dbus_message_get_sender(msg); - if (strcmp(sender, node_get_owner(node))) + if (strcmp(sender, node_get_owner(new_req.node))) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); if (!l_dbus_message_get_arguments(msg, "a{sv}", &options)) return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL); + if (!node_is_provisioner(new_req.node)) + return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); + + net = node_get_net(new_req.node); + new_req.net_idx = mesh_net_get_primary_idx(net); + memset(new_req.uuid, 0, sizeof(new_req.uuid)); + while (l_dbus_message_iter_next_entry(&options, &key, &var)) { bool failed = true; if (!strcmp(key, "Seconds")) { - if (l_dbus_message_iter_get_variant(&var, "q", - &duration)) { + if (l_dbus_message_iter_get_variant(&var, "q", &sec)) failed = false; + } else if (!strcmp(key, "Subnet")) { + if (l_dbus_message_iter_get_variant(&var, "q", + &new_req.net_idx)) { + if (new_req.net_idx <= MAX_KEY_IDX) + failed = false; + } + } else if (!strcmp(key, "Server")) { + if (l_dbus_message_iter_get_variant(&var, "q", + &new_req.server)) { + if (new_req.server < 0x8000) + failed = false; + } + } else if (!strcmp(key, "Filter")) { + if (l_dbus_message_iter_get_variant(&var, "ay", &var)) { + if (l_dbus_message_iter_get_fixed_array(&var, + &uuid, &flen)) { + if (flen == 16) { + memcpy(new_req.uuid, uuid, + flen); + failed = false; + } + } + } + } else if (!strcmp(key, "Extended")) { + if (l_dbus_message_iter_get_variant(&var, "ay", &var)) { + if (l_dbus_message_iter_get_fixed_array(&var, + &ext, &ext_len)) + failed = false; } } @@ -409,27 +693,51 @@ static struct l_dbus_message *start_scan_call(struct l_dbus *dbus, "Invalid options"); } - if (scan_node && scan_node != node) - return dbus_error(msg, MESH_ERROR_BUSY, NULL); + if (!scans) + scans = l_queue_new(); - if (!node_is_provisioner(node)) - return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); + if (new_req.server) { + if (!sec || sec > 60) + return dbus_error(msg, MESH_ERROR_INVALID_ARGS, + "Invalid options"); + } else { + new_req.server = node_get_primary(new_req.node); + if (!sec || sec > 60) + sec = 60; + } + + req = l_queue_remove_if(scans, by_node_svr, &new_req); + + if (!req) + req = l_malloc(sizeof(new_req)); + + if (req->timeout) { + l_timeout_remove(req->timeout); + req->timeout = NULL; + } + + *req = new_req; + req->rssi = -128; + + if (sec) + req->timeout = l_timeout_create(sec, scan_cancel, req, NULL); - if (scan_timeout) - l_timeout_remove(scan_timeout); - memset(scan_uuid, 0, sizeof(scan_uuid)); - scan_rssi = -128; - scan_timeout = NULL; - net = node_get_net(node); - io = mesh_net_get_io(net); - scan_node = node; - mesh_io_register_recv_cb(io, prvb, sizeof(prvb), - prov_beacon_recv, node); + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_START, scan_req); + scan_req[n++] = 5; + scan_req[n++] = sec; + if (flen) { + memcpy(scan_req + n, req->uuid, flen); + n += flen; + } + + mesh_model_send(req->node, 0, req->server, APP_IDX_DEV_REMOTE, + req->net_idx, DEFAULT_TTL, + true, n, scan_req); - if (duration) - scan_timeout = l_timeout_create(duration, scan_cancel, - node, NULL); + initiator_scan_reg(manager_scan_result, req->node); + + l_queue_push_tail(scans, req); return l_dbus_message_new_method_return(msg); } @@ -444,12 +752,7 @@ static struct l_dbus_message *cancel_scan_call(struct l_dbus *dbus, if (strcmp(sender, node_get_owner(node)) || !node_is_provisioner(node)) return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL); - if (scan_node) { - if (scan_node != node) - return dbus_error(msg, MESH_ERROR_BUSY, NULL); - - scan_cancel(NULL, node); - } + manager_scan_cancel(node); return l_dbus_message_new_method_return(msg); } @@ -814,6 +1117,8 @@ static void setup_management_interface(struct l_dbus_interface *iface) "aya{sv}", "uuid", "options"); l_dbus_interface_method(iface, "ImportRemoteNode", 0, import_node_call, "", "qyay", "primary", "count", "dev_key"); + l_dbus_interface_method(iface, "Reprovision", 0, reprovision_call, + "", "qa{sv}", "unicast", "options"); l_dbus_interface_method(iface, "DeleteRemoteNode", 0, delete_node_call, "", "qy", "primary", "count"); l_dbus_interface_method(iface, "UnprovisionedScan", 0, start_scan_call, @@ -849,7 +1154,7 @@ bool manager_dbus_init(struct l_dbus *bus) if (!l_dbus_register_interface(bus, MESH_MANAGEMENT_INTERFACE, setup_management_interface, NULL, false)) { - l_info("Unable to register %s interface", + l_debug("Unable to register %s interface", MESH_MANAGEMENT_INTERFACE); return false; } @@ -859,8 +1164,8 @@ bool manager_dbus_init(struct l_dbus *bus) void manager_scan_cancel(struct mesh_node *node) { - if (scan_node != node) - return; + struct scan_req *req; - scan_cancel(NULL, node); + while ((req = l_queue_find(scans, by_node, node))) + scan_cancel(NULL, req); } diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c index 7f46c8582..8f321a731 100644 --- a/mesh/mesh-config-json.c +++ b/mesh/mesh-config-json.c @@ -58,6 +58,33 @@ static const char *cfgnode_name = "/node.json"; static const char *bak_ext = ".bak"; static const char *tmp_ext = ".tmp"; +/* JSON key words */ +static const char *unicastAddress = "unicastAddress"; +static const char *deviceCan = "deviceCan"; +static const char *deviceKey = "deviceKey"; +static const char *defaultTTL = "defaultTTL"; +static const char *sequenceNumber = "sequenceNumber"; +static const char *netKeys = "netKeys"; +static const char *appKeys = "appKeys"; +static const char *elements = "elements"; +static const char *models = "models"; +static const char *modelId = "modelId"; +static const char *address = "address"; +static const char *bind = "bind"; +static const char *publish = "publish"; +static const char *subscribe = "subscribe"; +static const char *boundNetKey = "boundNetKey"; +static const char *keyRefresh = "keyRefresh"; +static const char *subEnabled = "subEnabled"; +static const char *pubEnabled = "pubEnabled"; +static const char *retransmit = "retransmit"; + +/* Common JSON values */ +static const char *enabled = "enabled"; +static const char *disabled = "disabled"; +static const char *unsupported = "unsupported"; + + static bool save_config(json_object *jnode, const char *fname) { FILE *outfile; @@ -134,14 +161,14 @@ static int get_element_index(json_object *jnode, uint16_t ele_addr) uint16_t addr, num_ele; char *str; - if (!json_object_object_get_ex(jnode, "unicastAddress", &jvalue)) + if (!json_object_object_get_ex(jnode, unicastAddress, &jvalue)) return -1; str = (char *)json_object_get_string(jvalue); if (sscanf(str, "%04hx", &addr) != 1) return -1; - if (!json_object_object_get_ex(jnode, "elements", &jelements)) + if (!json_object_object_get_ex(jnode, elements, &jelements)) return -1; num_ele = json_object_array_length(jelements); @@ -160,14 +187,14 @@ static json_object *get_element_model(json_object *jnode, int ele_idx, size_t len; char buf[9]; - if (!json_object_object_get_ex(jnode, "elements", &jelements)) + if (!json_object_object_get_ex(jnode, elements, &jelements)) return NULL; jelement = json_object_array_get_idx(jelements, ele_idx); if (!jelement) return NULL; - if (!json_object_object_get_ex(jelement, "models", &jmodels)) + if (!json_object_object_get_ex(jelement, models, &jmodels)) return NULL; num_mods = json_object_array_length(jmodels); @@ -189,7 +216,7 @@ static json_object *get_element_model(json_object *jnode, int ele_idx, char *str; jmodel = json_object_array_get_idx(jmodels, i); - if (!json_object_object_get_ex(jmodel, "modelId", &jvalue)) + if (!json_object_object_get_ex(jmodel, modelId, &jvalue)) return NULL; str = (char *)json_object_get_string(jvalue); @@ -298,7 +325,7 @@ static bool read_unicast_address(json_object *jobj, uint16_t *unicast) json_object *jvalue; char *str; - if (!json_object_object_get_ex(jobj, "unicastAddress", &jvalue)) + if (!json_object_object_get_ex(jobj, unicastAddress, &jvalue)) return false; str = (char *)json_object_get_string(jvalue); @@ -314,7 +341,7 @@ static bool read_default_ttl(json_object *jobj, uint8_t *ttl) int val; /* defaultTTL is optional */ - if (!json_object_object_get_ex(jobj, "defaultTTL", &jvalue)) + if (!json_object_object_get_ex(jobj, defaultTTL, &jvalue)) return true; val = json_object_get_int(jvalue); @@ -336,7 +363,7 @@ static bool read_seq_number(json_object *jobj, uint32_t *seq_number) int val; /* sequenceNumber is optional */ - if (!json_object_object_get_ex(jobj, "sequenceNumber", &jvalue)) + if (!json_object_object_get_ex(jobj, sequenceNumber, &jvalue)) return true; val = json_object_get_int(jvalue); @@ -396,7 +423,25 @@ static bool read_device_key(json_object *jobj, uint8_t key_buf[16]) if (!key_buf) return false; - if (!json_object_object_get_ex(jobj, "deviceKey", &jvalue)) + if (!json_object_object_get_ex(jobj, deviceKey, &jvalue)) + return false; + + str = (char *)json_object_get_string(jvalue); + if (!str2hex(str, strlen(str), key_buf, 16)) + return false; + + return true; +} + +static bool read_candidate(json_object *jobj, uint8_t key_buf[16]) +{ + json_object *jvalue; + char *str; + + if (!key_buf) + return false; + + if (!json_object_object_get_ex(jobj, deviceCan, &jvalue)) return false; str = (char *)json_object_get_string(jvalue); @@ -460,7 +505,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node) int len; int i; - if (!json_object_object_get_ex(jobj, "appKeys", &jarray)) + if (!json_object_object_get_ex(jobj, appKeys, &jarray)) return true; if (json_object_get_type(jarray) != json_type_array) @@ -484,7 +529,7 @@ static bool read_app_keys(json_object *jobj, struct mesh_config_node *node) if (!get_key_index(jtemp, "index", &appkey->app_idx)) goto fail; - if (!get_key_index(jtemp, "boundNetKey", &appkey->net_idx)) + if (!get_key_index(jtemp, boundNetKey, &appkey->net_idx)) goto fail; if (!json_object_object_get_ex(jtemp, "key", &jvalue)) @@ -516,7 +561,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node) int i; /* At least one NetKey must be present for a provisioned node */ - if (!json_object_object_get_ex(jobj, "netKeys", &jarray)) + if (!json_object_object_get_ex(jobj, netKeys, &jarray)) return false; if (json_object_get_type(jarray) != json_type_array) @@ -547,7 +592,7 @@ static bool read_net_keys(json_object *jobj, struct mesh_config_node *node) if (!str2hex(str, strlen(str), netkey->new_key, 16)) goto fail; - if (!json_object_object_get_ex(jtemp, "keyRefresh", &jvalue)) + if (!json_object_object_get_ex(jtemp, keyRefresh, &jvalue)) netkey->phase = KEY_REFRESH_PHASE_NONE; else netkey->phase = (uint8_t) json_object_get_int(jvalue); @@ -598,7 +643,7 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx, jnode = cfg->jnode; l_debug("netKey %4.4x", idx); - json_object_object_get_ex(jnode, "netKeys", &jarray); + json_object_object_get_ex(jnode, netKeys, &jarray); if (jarray) jentry = get_key_object(jarray, idx); @@ -616,14 +661,14 @@ bool mesh_config_net_key_add(struct mesh_config *cfg, uint16_t idx, if (!add_key_value(jentry, "key", key)) goto fail; - json_object_object_add(jentry, "keyRefresh", + json_object_object_add(jentry, keyRefresh, json_object_new_int(KEY_REFRESH_PHASE_NONE)); if (!jarray) { jarray = json_object_new_array(); if (!jarray) goto fail; - json_object_object_add(jnode, "netKeys", jarray); + json_object_object_add(jnode, netKeys, jarray); } json_object_array_add(jarray, jentry); @@ -648,7 +693,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx, jnode = cfg->jnode; - if (!json_object_object_get_ex(jnode, "netKeys", &jarray)) + if (!json_object_object_get_ex(jnode, netKeys, &jarray)) return false; jentry = get_key_object(jarray, idx); @@ -667,7 +712,7 @@ bool mesh_config_net_key_update(struct mesh_config *cfg, uint16_t idx, if (!add_key_value(jentry, "key", key)) return false; - json_object_object_add(jentry, "keyRefresh", + json_object_object_add(jentry, keyRefresh, json_object_new_int(KEY_REFRESH_PHASE_ONE)); return save_config(jnode, cfg->node_dir_path); @@ -682,20 +727,55 @@ bool mesh_config_net_key_del(struct mesh_config *cfg, uint16_t idx) jnode = cfg->jnode; - if (!json_object_object_get_ex(jnode, "netKeys", &jarray)) + if (!json_object_object_get_ex(jnode, netKeys, &jarray)) return true; jarray_key_del(jarray, idx); if (!json_object_array_length(jarray)) - json_object_object_del(jnode, "netKeys"); + json_object_object_del(jnode, netKeys); return save_config(jnode, cfg->node_dir_path); } bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key) { - if (!cfg || !add_key_value(cfg->jnode, "deviceKey", key)) + if (!cfg || !add_key_value(cfg->jnode, deviceKey, key)) + return false; + + return save_config(cfg->jnode, cfg->node_dir_path); +} + +bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key) +{ + if (!cfg || !add_key_value(cfg->jnode, deviceCan, key)) + return false; + + return save_config(cfg->jnode, cfg->node_dir_path); +} + +bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key) +{ + if (!cfg) + return false; + + return read_candidate(cfg->jnode, key); +} + +bool mesh_config_finalize_candidate(struct mesh_config *cfg) +{ + uint8_t key[16]; + + if (!cfg) + return false; + + if (!read_candidate(cfg->jnode, key)) + return false; + + json_object_object_del(cfg->jnode, deviceCan); + json_object_object_del(cfg->jnode, deviceKey); + + if (!add_key_value(cfg->jnode, deviceKey, key)) return false; return save_config(cfg->jnode, cfg->node_dir_path); @@ -719,7 +799,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx, jnode = cfg->jnode; - json_object_object_get_ex(jnode, "appKeys", &jarray); + json_object_object_get_ex(jnode, appKeys, &jarray); if (jarray) jentry = get_key_object(jarray, app_idx); @@ -734,7 +814,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx, if (!write_int(jentry, "index", app_idx)) goto fail; - if (!write_int(jentry, "boundNetKey", net_idx)) + if (!write_int(jentry, boundNetKey, net_idx)) goto fail; if (!add_key_value(jentry, "key", key)) @@ -744,7 +824,7 @@ bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx, jarray = json_object_new_array(); if (!jarray) goto fail; - json_object_object_add(jnode, "appKeys", jarray); + json_object_object_add(jnode, appKeys, jarray); } json_object_array_add(jarray, jentry); @@ -770,7 +850,7 @@ bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx, jnode = cfg->jnode; - if (!json_object_object_get_ex(jnode, "appKeys", &jarray)) + if (!json_object_object_get_ex(jnode, appKeys, &jarray)) return false; /* The key entry should exist if the key is updated */ @@ -804,13 +884,13 @@ bool mesh_config_app_key_del(struct mesh_config *cfg, uint16_t net_idx, jnode = cfg->jnode; - if (!json_object_object_get_ex(jnode, "appKeys", &jarray)) + if (!json_object_object_get_ex(jnode, appKeys, &jarray)) return true; jarray_key_del(jarray, idx); if (!json_object_array_length(jarray)) - json_object_object_del(jnode, "appKeys"); + json_object_object_del(jnode, appKeys); return save_config(jnode, cfg->node_dir_path); } @@ -840,7 +920,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr, if (!jmodel) return false; - json_object_object_get_ex(jmodel, "bind", &jarray); + json_object_object_get_ex(jmodel, bind, &jarray); if (jarray && jarray_has_string(jarray, buf, 4)) return true; @@ -854,7 +934,7 @@ bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr, json_object_put(jstring); return false; } - json_object_object_add(jmodel, "bind", jarray); + json_object_object_add(jmodel, bind, jarray); } json_object_array_add(jarray, jstring); @@ -887,13 +967,13 @@ bool mesh_config_model_binding_del(struct mesh_config *cfg, uint16_t ele_addr, if (!jmodel) return false; - if (!json_object_object_get_ex(jmodel, "bind", &jarray)) + if (!json_object_object_get_ex(jmodel, bind, &jarray)) return true; jarray_string_del(jarray, buf, 4); if (!json_object_array_length(jarray)) - json_object_object_del(jmodel, "bind"); + json_object_object_del(jmodel, bind); return save_config(jnode, cfg->node_dir_path); } @@ -963,7 +1043,7 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub) int len, value; char *str; - if (!json_object_object_get_ex(jpub, "address", &jvalue)) + if (!json_object_object_get_ex(jpub, address, &jvalue)) return NULL; str = (char *)json_object_get_string(jvalue); @@ -998,9 +1078,10 @@ static struct mesh_config_pub *parse_model_publication(json_object *jpub) if (!get_int(jpub, "credentials", &value)) goto fail; + pub->credential = (uint8_t) value; - if (!json_object_object_get_ex(jpub, "retransmit", &jvalue)) + if (!json_object_object_get_ex(jpub, retransmit, &jvalue)) goto fail; if (!get_int(jvalue, "count", &value)) @@ -1093,7 +1174,7 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele) l_queue_push_tail(ele->models, mod); - if (!json_object_object_get_ex(jmodel, "modelId", &jvalue)) + if (!json_object_object_get_ex(jmodel, modelId, &jvalue)) goto fail; str = (char *)json_object_get_string(jvalue); @@ -1112,29 +1193,32 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele) mod->id = id; - if (json_object_object_get_ex(jmodel, "bind", &jarray)) { + if (len == 8) + mod->vendor = true; + + if (json_object_object_get_ex(jmodel, bind, &jarray)) { if (json_object_get_type(jarray) != json_type_array || !parse_bindings(jarray, mod)) goto fail; } - if (json_object_object_get_ex(jmodel, "pubEnabled", &jvalue)) + if (json_object_object_get_ex(jmodel, pubEnabled, &jvalue)) mod->pub_enabled = json_object_get_boolean(jvalue); else mod->pub_enabled = true; - if (json_object_object_get_ex(jmodel, "subEnabled", &jvalue)) + if (json_object_object_get_ex(jmodel, subEnabled, &jvalue)) mod->sub_enabled = json_object_get_boolean(jvalue); else mod->sub_enabled = true; - if (json_object_object_get_ex(jmodel, "publish", &jvalue)) { + if (json_object_object_get_ex(jmodel, publish, &jvalue)) { mod->pub = parse_model_publication(jvalue); if (!mod->pub) goto fail; } - if (json_object_object_get_ex(jmodel, "subscribe", &jarray)) { + if (json_object_object_get_ex(jmodel, subscribe, &jarray)) { if (!parse_model_subscriptions(jarray, mod)) goto fail; } @@ -1187,7 +1271,7 @@ static bool parse_elements(json_object *jelems, struct mesh_config_node *node) if (sscanf(str, "%04hx", &(ele->location)) != 1) goto fail; - if (json_object_object_get_ex(jelement, "models", &jmodels)) { + if (json_object_object_get_ex(jelement, models, &jmodels)) { if (json_object_get_type(jmodels) != json_type_array || !parse_models(jmodels, ele)) goto fail; @@ -1211,13 +1295,13 @@ static int get_mode(json_object *jvalue) if (!str) return 0xffffffff; - if (!strncasecmp(str, "disabled", strlen("disabled"))) + if (!strncasecmp(str, disabled, strlen(disabled))) return MESH_MODE_DISABLED; - if (!strncasecmp(str, "enabled", strlen("enabled"))) + if (!strncasecmp(str, enabled, strlen(enabled))) return MESH_MODE_ENABLED; - if (!strncasecmp(str, "unsupported", strlen("unsupported"))) + if (!strncasecmp(str, unsupported, strlen(unsupported))) return MESH_MODE_UNSUPPORTED; return 0xffffffff; @@ -1323,7 +1407,7 @@ static bool read_net_transmit(json_object *jobj, struct mesh_config_node *node) uint16_t interval; uint8_t cnt; - if (!json_object_object_get_ex(jobj, "retransmit", &jrtx)) + if (!json_object_object_get_ex(jobj, retransmit, &jrtx)) return true; if (!json_object_object_get_ex(jrtx, "count", &jvalue)) @@ -1386,7 +1470,7 @@ static bool read_node(json_object *jnode, struct mesh_config_node *node) } /* Check for required "elements" property */ - if (!json_object_object_get_ex(jnode, "elements", &jvalue)) + if (!json_object_object_get_ex(jnode, elements, &jvalue)) return false; if (!read_net_transmit(jnode, node)) { @@ -1460,11 +1544,11 @@ static const char *mode_to_string(int mode) { switch (mode) { case MESH_MODE_DISABLED: - return "disabled"; + return disabled; case MESH_MODE_ENABLED: - return "enabled"; + return enabled; default: - return "unsupported"; + return unsupported; } } @@ -1522,7 +1606,7 @@ fail: bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast) { - if (!cfg || !write_uint16_hex(cfg->jnode, "unicastAddress", unicast)) + if (!cfg || !write_uint16_hex(cfg->jnode, unicastAddress, unicast)) return false; return save_config(cfg->jnode, cfg->node_dir_path); @@ -1558,8 +1642,8 @@ bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt, if (!write_int(jrtx, "interval", interval)) goto fail; - json_object_object_del(jnode, "retransmit"); - json_object_object_add(jnode, "retransmit", jrtx); + json_object_object_del(jnode, retransmit); + json_object_object_add(jnode, retransmit, jrtx); return save_config(cfg->jnode, cfg->node_dir_path); @@ -1599,8 +1683,8 @@ static void add_model(void *a, void *b) if (!jmodel) return; - result = (mod->vendor) ? write_uint32_hex(jmodel, "modelId", mod->id) : - write_uint16_hex(jmodel, "modelId", (uint16_t) mod->id); + result = (mod->vendor) ? write_uint32_hex(jmodel, modelId, mod->id) : + write_uint16_hex(jmodel, modelId, (uint16_t) mod->id); if (!result) { json_object_put(jmodel); @@ -1608,10 +1692,10 @@ static void add_model(void *a, void *b) } jval = json_object_new_boolean(mod->sub_enabled); - json_object_object_add(jmodel, "subEnabled", jval); + json_object_object_add(jmodel, subEnabled, jval); jval = json_object_new_boolean(mod->pub_enabled); - json_object_object_add(jmodel, "pubEnabled", jval); + json_object_object_add(jmodel, pubEnabled, jval); json_object_array_add(jmodels, jmodel); } @@ -1663,11 +1747,11 @@ static struct mesh_config *create_config(const char *cfg_path, return NULL; /* Sequence number */ - json_object_object_add(jnode, "sequenceNumber", + json_object_object_add(jnode, sequenceNumber, json_object_new_int(node->seq_number)); /* Default TTL */ - json_object_object_add(jnode, "defaultTTL", + json_object_object_add(jnode, defaultTTL, json_object_new_int(node->ttl)); /* Elements */ @@ -1702,11 +1786,11 @@ static struct mesh_config *create_config(const char *cfg_path, if (!jmodels) goto fail; - json_object_object_add(jelement, "models", jmodels); + json_object_object_add(jelement, models, jmodels); l_queue_foreach(ele->models, add_model, jmodels); } - json_object_object_add(jnode, "elements", jelems); + json_object_object_add(jnode, elements, jelems); cfg = l_new(struct mesh_config, 1); @@ -1724,6 +1808,55 @@ fail: return NULL; } +void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node) +{ + json_object *jelems; + const struct l_queue_entry *entry; + + if (!cfg || !cfg->jnode) + return; + + /* TODO: Recreate Element Array */ + jelems = json_object_new_array(); + if (!jelems) + return; + + entry = l_queue_get_entries(node->elements); + + for (; entry; entry = entry->next) { + struct mesh_config_element *ele = entry->data; + json_object *jelement, *jmodels; + + jelement = json_object_new_object(); + + if (!jelement) { + json_object_put(jelems); + return; + } + + write_int(jelement, "elementIndex", ele->index); + write_uint16_hex(jelement, "location", ele->location); + json_object_array_add(jelems, jelement); + + /* Models */ + if (l_queue_isempty(ele->models)) + continue; + + jmodels = json_object_new_array(); + if (!jmodels) { + json_object_put(jelems); + return; + } + + json_object_object_add(jelement, models, jmodels); + l_queue_foreach(ele->models, add_model, jmodels); + } + + /* Replace element array */ + json_object_object_del(cfg->jnode, elements); + json_object_object_add(cfg->jnode, elements, jelems); +} + struct mesh_config *mesh_config_create(const char *cfgdir_name, const uint8_t uuid[16], struct mesh_config_node *db_node) { @@ -1768,7 +1901,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx) int i, len; /* Clean up all the bound appkeys */ - if (!json_object_object_get_ex(jobj, "appKeys", &jarray)) + if (!json_object_object_get_ex(jobj, appKeys, &jarray)) return; len = json_object_array_length(jarray); @@ -1779,7 +1912,7 @@ static void finish_key_refresh(json_object *jobj, uint16_t net_idx) jentry = json_object_array_get_idx(jarray, i); - if (!get_key_index(jentry, "boundNetKey", &idx)) + if (!get_key_index(jentry, boundNetKey, &idx)) continue; if (idx != net_idx) @@ -1803,14 +1936,14 @@ bool mesh_config_net_key_set_phase(struct mesh_config *cfg, uint16_t idx, jnode = cfg->jnode; - if (json_object_object_get_ex(jnode, "netKeys", &jarray)) + if (json_object_object_get_ex(jnode, netKeys, &jarray)) jentry = get_key_object(jarray, idx); if (!jentry) return false; - json_object_object_del(jentry, "keyRefresh"); - json_object_object_add(jentry, "keyRefresh", + json_object_object_del(jentry, keyRefresh); + json_object_object_add(jentry, keyRefresh, json_object_new_int(phase)); if (phase == KEY_REFRESH_PHASE_NONE) { @@ -1842,16 +1975,16 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr, if (!jmodel) return false; - json_object_object_del(jmodel, "publish"); + json_object_object_del(jmodel, publish); jpub = json_object_new_object(); if (!jpub) return false; if (pub->virt) - res = add_key_value(jpub, "address", pub->virt_addr); + res = add_key_value(jpub, address, pub->virt_addr); else - res = write_uint16_hex(jpub, "address", pub->addr); + res = write_uint16_hex(jpub, address, pub->addr); if (!res) goto fail; @@ -1878,8 +2011,8 @@ bool mesh_config_model_pub_add(struct mesh_config *cfg, uint16_t ele_addr, if (!write_int(jrtx, "interval", pub->interval)) goto fail; - json_object_object_add(jpub, "retransmit", jrtx); - json_object_object_add(jmodel, "publish", jpub); + json_object_object_add(jpub, retransmit, jrtx); + json_object_object_add(jmodel, publish, jpub); return save_config(jnode, cfg->node_dir_path); @@ -1911,23 +2044,23 @@ bool mesh_config_model_pub_del(struct mesh_config *cfg, uint16_t addr, uint32_t mod_id, bool vendor) { if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor, - "publish")) + publish)) return false; return save_config(cfg->jnode, cfg->node_dir_path); } -static void del_page(json_object *jarray, uint8_t page) +static bool del_page(json_object *jarray, uint8_t page) { char buf[3]; int i, len, ret; if (!jarray) - return; + return false; ret = snprintf(buf, 3, "%2.2x", page); if (ret < 0) - return; + return false; len = json_object_array_length(jarray); @@ -1938,10 +2071,29 @@ static void del_page(json_object *jarray, uint8_t page) jentry = json_object_array_get_idx(jarray, i); str = (char *)json_object_get_string(jentry); - /* Delete matching page(s) */ - if (!memcmp(str, buf, 2)) + /* Delete matching page */ + if (!memcmp(str, buf, 2)) { json_object_array_del_idx(jarray, i, 1); + break; + } } + + return true; +} + +void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page) +{ + json_object *jnode, *jarray = NULL; + + if (!cfg) + return; + + jnode = cfg->jnode; + + json_object_object_get_ex(jnode, "pages", &jarray); + + if (del_page(jarray, page)) + save_config(jnode, cfg->node_dir_path); } bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, @@ -1984,56 +2136,6 @@ bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, return save_config(jnode, cfg->node_dir_path); } -bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw) -{ - json_object *jnode, *jarray = NULL; - uint8_t *data; - char *str; - char old_buf[3]; - int i, len, ret, dlen = 0; - bool status = true; - - if (!cfg || old == nw) - return false; - - ret = snprintf(old_buf, 3, "%2.2x", old); - if (ret < 0) - return false; - - jnode = cfg->jnode; - - json_object_object_get_ex(jnode, "pages", &jarray); - - if (!jarray) - return false; - - data = l_malloc(MAX_MSG_LEN); - - len = json_object_array_length(jarray); - - for (i = 0; i < len; i++) { - json_object *jentry; - - jentry = json_object_array_get_idx(jarray, i); - str = (char *)json_object_get_string(jentry); - - /* Delete matching page(s) but save data*/ - if (!memcmp(str, old_buf, 2)) { - dlen = strlen(str + 2); - str2hex(str + 2, dlen, data, MAX_MSG_LEN); - dlen /= 2; - json_object_array_del_idx(jarray, i, 1); - } - } - - if (dlen) - status = mesh_config_comp_page_add(cfg, nw, data, dlen); - - l_free(data); - - return status; -} - bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, struct mesh_config_sub *sub) @@ -2064,7 +2166,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr, len = 32; } - json_object_object_get_ex(jmodel, "subscribe", &jarray); + json_object_object_get_ex(jmodel, subscribe, &jarray); if (jarray && jarray_has_string(jarray, buf, len)) return true; @@ -2078,7 +2180,7 @@ bool mesh_config_model_sub_add(struct mesh_config *cfg, uint16_t ele_addr, json_object_put(jstring); return false; } - json_object_object_add(jmodel, "subscribe", jarray); + json_object_object_add(jmodel, subscribe, jarray); } json_object_array_add(jarray, jstring); @@ -2107,7 +2209,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr, if (!jmodel) return false; - if (!json_object_object_get_ex(jmodel, "subscribe", &jarray)) + if (!json_object_object_get_ex(jmodel, subscribe, &jarray)) return true; if (!sub->virt) { @@ -2122,7 +2224,7 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr, jarray_string_del(jarray, buf, len); if (!json_object_array_length(jarray)) - json_object_object_del(jmodel, "subscribe"); + json_object_object_del(jmodel, subscribe); return save_config(jnode, cfg->node_dir_path); } @@ -2131,7 +2233,7 @@ bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr, uint32_t mod_id, bool vendor) { if (!cfg || !delete_model_property(cfg->jnode, addr, mod_id, vendor, - "subscribe")) + subscribe)) return false; return save_config(cfg->jnode, cfg->node_dir_path); @@ -2161,7 +2263,7 @@ bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr, json_object_object_add(jmodel, "pubDisabled", jval); if (!enable) - json_object_object_del(jmodel, "publish"); + json_object_object_del(jmodel, publish); return save_config(cfg->jnode, cfg->node_dir_path); } @@ -2184,13 +2286,13 @@ bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr, if (!jmodel) return false; - json_object_object_del(jmodel, "subEnabled"); + json_object_object_del(jmodel, subEnabled); jval = json_object_new_boolean(enable); - json_object_object_add(jmodel, "subEnabled", jval); + json_object_object_add(jmodel, subEnabled, jval); if (!enable) - json_object_object_del(jmodel, "subscribe"); + json_object_object_del(jmodel, subscribe); return save_config(cfg->jnode, cfg->node_dir_path); } @@ -2205,14 +2307,14 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, return false; if (!cache) { - if (!write_int(cfg->jnode, "sequenceNumber", seq)) + if (!write_int(cfg->jnode, sequenceNumber, seq)) return false; return mesh_config_save(cfg, true, NULL, NULL); } /* If resetting seq to Zero, make sure cached value reset as well */ - if (seq && get_int(cfg->jnode, "sequenceNumber", &value)) + if (seq && get_int(cfg->jnode, sequenceNumber, &value)) cached = (uint32_t)value; /* @@ -2262,8 +2364,8 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, l_debug("Seq Cache: %d -> %d", seq, cached); - if (!write_int(cfg->jnode, "sequenceNumber", cached)) - return false; + if (!write_int(cfg->jnode, sequenceNumber, cached)) + return false; return mesh_config_save(cfg, false, NULL, NULL); } @@ -2273,7 +2375,7 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl) { - if (!cfg || !write_int(cfg->jnode, "defaultTTL", ttl)) + if (!cfg || !write_int(cfg->jnode, defaultTTL, ttl)) return false; return save_config(cfg->jnode, cfg->node_dir_path); diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h index 420775829..ed1b610de 100644 --- a/mesh/mesh-config.h +++ b/mesh/mesh-config.h @@ -119,6 +119,7 @@ void mesh_config_release(struct mesh_config *cfg); void mesh_config_destroy_nvm(struct mesh_config *cfg); bool mesh_config_save(struct mesh_config *cfg, bool no_wait, mesh_config_status_func_t cb, void *user_data); +void mesh_config_reset(struct mesh_config *cfg, struct mesh_config_node *node); struct mesh_config *mesh_config_create(const char *cfgdir_name, const uint8_t uuid[16], struct mesh_config_node *node); @@ -126,6 +127,9 @@ struct mesh_config *mesh_config_create(const char *cfgdir_name, bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt, uint16_t interval); bool mesh_config_write_device_key(struct mesh_config *cfg, uint8_t *key); +bool mesh_config_write_candidate(struct mesh_config *cfg, uint8_t *key); +bool mesh_config_read_candidate(struct mesh_config *cfg, uint8_t *key); +bool mesh_config_finalize_candidate(struct mesh_config *cfg); bool mesh_config_write_token(struct mesh_config *cfg, uint8_t *token); bool mesh_config_write_network_key(struct mesh_config *cfg, uint16_t idx, uint8_t *key, uint8_t *new_key, int phase); @@ -141,7 +145,7 @@ bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword, int value); bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, uint8_t *data, uint16_t size); -bool mesh_config_comp_page_mv(struct mesh_config *cfg, uint8_t old, uint8_t nw); +void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page); bool mesh_config_model_binding_add(struct mesh_config *cfg, uint16_t ele_addr, uint32_t mod_id, bool vendor, uint16_t app_idx); diff --git a/mesh/model.c b/mesh/model.c index d48e6ef12..e2babea10 100644 --- a/mesh/model.c +++ b/mesh/model.c @@ -24,6 +24,8 @@ #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/cfgmod.h" +#include "mesh/prov.h" +#include "mesh/remprv.h" #include "mesh/error.h" #include "mesh/dbus.h" #include "mesh/util.h" @@ -76,6 +78,9 @@ static bool is_internal(uint32_t id) if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL) return true; + if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL) + return true; + return false; } @@ -457,13 +462,25 @@ static int dev_packet_decrypt(struct mesh_node *node, const uint8_t *data, dst, key_aid, seq, iv_idx, out, key)) return APP_IDX_DEV_LOCAL; - if (!keyring_get_remote_dev_key(node, src, dev_key)) + key = dev_key; + + if (keyring_get_remote_dev_key(node, src, dev_key)) { + if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, + src, dst, key_aid, seq, iv_idx, out, key)) + return APP_IDX_DEV_REMOTE; + } + + /* See if there is a local Device Key Candidate as last resort */ + if (!node_get_device_key_candidate(node, dev_key)) return -1; - key = dev_key; - if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, src, - dst, key_aid, seq, iv_idx, out, key)) - return APP_IDX_DEV_REMOTE; + if (mesh_crypto_payload_decrypt(NULL, 0, data, size, szmict, + src, dst, key_aid, seq, iv_idx, out, key)) { + + /* If candidate dev_key worked, it is considered finalized */ + node_finalize_candidate(node); + return APP_IDX_DEV_LOCAL; + } return -1; } diff --git a/mesh/node.c b/mesh/node.c index cf4ed140e..5150a085a 100644 --- a/mesh/node.c +++ b/mesh/node.c @@ -27,9 +27,11 @@ #include "mesh/appkey.h" #include "mesh/mesh-config.h" #include "mesh/provision.h" +#include "mesh/prov.h" #include "mesh/keyring.h" #include "mesh/model.h" #include "mesh/cfgmod.h" +#include "mesh/remprv.h" #include "mesh/util.h" #include "mesh/error.h" #include "mesh/dbus.h" @@ -347,6 +349,15 @@ static bool add_elements_from_storage(struct mesh_node *node, if (!add_element_from_storage(node, entry->data)) return false; + /* Add configuration server model on the primary element */ + mesh_model_add(node, PRIMARY_ELE_IDX, CONFIG_SRV_MODEL, NULL); + + /* Add remote provisioning models on the primary element */ + mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_SRV_MODEL, NULL); + + if (node->provisioner) + mesh_model_add(node, PRIMARY_ELE_IDX, REM_PROV_CLI_MODEL, NULL); + return true; } @@ -489,6 +500,10 @@ static bool init_from_storage(struct mesh_config_node *db_node, /* Initialize configuration server model */ cfgmod_server_init(node, PRIMARY_ELE_IDX); + /* Initialize remote provisioning models */ + remote_prov_server_init(node, PRIMARY_ELE_IDX); + remote_prov_client_init(node, PRIMARY_ELE_IDX); + node->cfg = cfg; return true; @@ -550,12 +565,78 @@ uint16_t node_get_primary(struct mesh_node *node) return node->primary; } +bool node_refresh(struct mesh_node *node, bool hard, void *prov_info) +{ + struct mesh_prov_node_info *info = prov_info; + bool res = true; + + if (!node || !info) + return false; + + if (!IS_UNICAST(info->unicast)) + return false; + + /* Changing Unicast addresses requires a hard node reset */ + if (!hard && info->unicast != node->primary) + return false; + + /* + * Hard refresh results in immediate use of new Device Key. + * Soft refresh saves new device key as Candidate until we + * successfully receive new incoming message on that key. + */ + if (hard) { + if (!mesh_config_write_device_key(node->cfg, info->device_key)) + return false; + + memcpy(node->dev_key, info->device_key, sizeof(node->dev_key)); + + } else if (!mesh_config_write_candidate(node->cfg, info->device_key)) + return false; + + /* Replace Primary Unicast address if it has changed */ + if (node->primary != info->unicast) { + res = mesh_config_write_unicast(node->cfg, info->unicast); + if (res) { + node->primary = info->unicast; + node->num_ele = info->num_ele; + mesh_net_register_unicast(node->net, node->primary, + node->num_ele); + } + } + + /* Replace Page 0 with Page 128 if it exists */ + if (res) { + if (node_replace_comp(node, 0, 128)) + return true; + } + + return res; +} + const uint8_t *node_get_device_key(struct mesh_node *node) { if (!node) return NULL; - else - return node->dev_key; + + return node->dev_key; +} + +bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key) +{ + if (!node) + return false; + + return mesh_config_read_candidate(node->cfg, key); +} + +void node_finalize_candidate(struct mesh_node *node) +{ + if (!node) + return; + + if (mesh_config_read_candidate(node->cfg, node->dev_key)) + mesh_config_finalize_candidate(node->cfg); } void node_set_token(struct mesh_node *node, uint8_t token[8]) @@ -785,7 +866,7 @@ uint8_t node_friend_mode_get(struct mesh_node *node) return node->friend; } -static uint16_t generate_node_comp(struct mesh_node *node, uint8_t *buf, +static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz) { uint16_t n, features, num_ele = 0; @@ -895,6 +976,21 @@ static void convert_node_to_storage(struct mesh_node *node, } +static void free_db_storage(struct mesh_config_node *db_node) +{ + const struct l_queue_entry *entry; + + /* Free temporarily allocated resources */ + entry = l_queue_get_entries(db_node->elements); + for (; entry; entry = entry->next) { + struct mesh_config_element *db_ele = entry->data; + + l_queue_destroy(db_ele->models, l_free); + } + + l_queue_destroy(db_node->elements, l_free); +} + static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16]) { struct mesh_config_node db_node; @@ -922,7 +1018,22 @@ static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16]) return node->cfg != NULL; } -static bool set_node_comp(struct mesh_node *node, uint8_t page_num, +static void node_del_comp(struct mesh_node *node, uint8_t page_num) +{ + struct mesh_config_comp_page *page; + + if (!node) + return; + + page = l_queue_remove_if(node->pages, match_page, + L_UINT_TO_PTR(page_num)); + + l_free(page); + + mesh_config_comp_page_del(node->cfg, page_num); +} + +static bool node_set_comp(struct mesh_node *node, uint8_t page_num, const uint8_t *data, uint16_t len) { struct mesh_config_comp_page *page; @@ -944,16 +1055,6 @@ static bool set_node_comp(struct mesh_node *node, uint8_t page_num, return mesh_config_comp_page_add(node->cfg, page_num, page->data, len); } -static bool create_node_comp(struct mesh_node *node) -{ - uint16_t len; - uint8_t comp[MAX_MSG_LEN - 2]; - - len = generate_node_comp(node, comp, sizeof(comp)); - - return set_node_comp(node, 0, comp, len); -} - const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num, uint16_t *len) { @@ -975,6 +1076,7 @@ const uint8_t *node_get_comp(struct mesh_node *node, uint8_t page_num, bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with) { struct mesh_config_comp_page *old_page, *keep; + bool status; if (!node) return false; @@ -989,9 +1091,13 @@ bool node_replace_comp(struct mesh_node *node, uint8_t retire, uint8_t with) l_free(old_page); keep->page_num = retire; - mesh_config_comp_page_mv(node->cfg, with, retire); + status = mesh_config_comp_page_add(node->cfg, keep->page_num, + keep->data, keep->len); - return true; + if (with != retire) + mesh_config_comp_page_del(node->cfg, with); + + return status; } static void attach_io(void *a, void *b) @@ -1170,8 +1276,13 @@ static bool get_element_properties(struct mesh_node *node, const char *path, * daemon. If the model is present in the application properties, * the operation below will be a "no-op". */ - if (ele->idx == PRIMARY_ELE_IDX) + if (ele->idx == PRIMARY_ELE_IDX) { mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL); + mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL); + if (node->provisioner) + mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL, + NULL); + } return true; fail: @@ -1232,6 +1343,15 @@ static bool get_app_properties(struct mesh_node *node, const char *path, return true; } +static void save_pages(void *data, void *user_data) +{ + struct mesh_config_comp_page *page = data; + struct mesh_node *node = user_data; + + mesh_config_comp_page_add(node->cfg, page->page_num, page->data, + page->len); +} + static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr, bool ivu, uint32_t iv_idx, uint8_t dev_key[16], uint16_t net_key_idx, uint8_t net_key[16]) @@ -1275,10 +1395,14 @@ static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr, return false; } + l_queue_foreach(node->pages, save_pages, node); + update_net_settings(node); - /* Initialize configuration server model */ + /* Initialize internal server models */ cfgmod_server_init(node, PRIMARY_ELE_IDX); + remote_prov_server_init(node, PRIMARY_ELE_IDX); + remote_prov_client_init(node, PRIMARY_ELE_IDX); node->busy = true; @@ -1326,39 +1450,59 @@ static void update_model_options(struct mesh_node *node, static bool check_req_node(struct managed_obj_request *req) { + struct mesh_node *node; const int offset = 8; uint16_t node_len, len; uint8_t comp[MAX_MSG_LEN - 2]; const uint8_t *node_comp; - len = generate_node_comp(req->node, comp, sizeof(comp)); + if (req->type != REQUEST_TYPE_ATTACH) { + node = req->node; - if (len < MIN_COMP_SIZE) - return false; + if (!create_node_config(node, node->uuid)) + return false; + } else + node = req->attach; - node_comp = node_get_comp(req->attach, 0, &node_len); + node_comp = node_get_comp(node, 0, &node_len); + len = node_generate_comp(req->node, comp, sizeof(comp)); - /* If no page 0 exists, create it and accept */ - if (!node_len || !node_comp) - return set_node_comp(req->attach, 0, comp, len); + /* If no page 0 exists, then current composition as valid */ + if (req->type != REQUEST_TYPE_ATTACH || !node_len) + goto page_zero_valid; - /* Test Element/Model part of composition and reject if changed */ + /* + * If composition has materially changed, save new composition + * in page 128 until next NPPI procedure. But we do allow + * for CID, PID, VID and/or CRPL to freely change without + * requiring a NPPI procedure. + */ if (node_len != len || memcmp(&node_comp[offset], &comp[offset], node_len - offset)) - return false; + return node_set_comp(node, 128, comp, len); - /* If comp has changed, but not Element/Models, resave and accept */ - else if (memcmp(node_comp, comp, node_len)) - return set_node_comp(req->attach, 0, comp, len); +page_zero_valid: + /* If page 0 represents current App, ensure page 128 doesn't exist */ + node_del_comp(node, 128); - /* Nothing has changed */ - return true; + if (len == node_len && !memcmp(node_comp, comp, len)) + return true; + + return node_set_comp(node, 0, comp, len); +} + +static bool is_zero(const void *a, const void *b) +{ + const struct node_element *element = a; + + return !element->idx; } static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node) { const struct l_queue_entry *attach_entry; const struct l_queue_entry *node_entry; + bool comp_changed = false; attach->obj_path = node->obj_path; node->obj_path = NULL; @@ -1368,6 +1512,34 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node) return false; } + if (attach->num_ele != node->num_ele) { + struct mesh_config_node db_node; + struct node_element *old_ele, *new_ele; + + convert_node_to_storage(node, &db_node); + + /* + * If composition has materially changed, we need to discard + * everything we knew about elements in the old application, + * and start from what they are telling us now. + */ + old_ele = l_queue_remove_if(attach->elements, is_zero, NULL); + new_ele = l_queue_remove_if(node->elements, is_zero, NULL); + element_free(new_ele); + + l_queue_destroy(attach->elements, element_free); + attach->elements = node->elements; + attach->num_ele = node->num_ele; + + /* Restore primary elements */ + l_queue_push_head(attach->elements, old_ele); + + comp_changed = true; + + mesh_config_reset(attach->cfg, &db_node); + free_db_storage(&db_node); + } + attach_entry = l_queue_get_entries(attach->elements); node_entry = l_queue_get_entries(node->elements); @@ -1384,6 +1556,10 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node) attach_entry = attach_entry->next; node_entry = node_entry->next; + + /* Only need the Primary element during Composition change */ + if (comp_changed) + break; } mesh_agent_remove(attach->agent); @@ -1399,8 +1575,12 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node) node->owner = NULL; update_composition(node, attach); + update_model_options(node, attach); + if (comp_changed) + node->elements = NULL; + node_remove(node); return true; @@ -1499,16 +1679,7 @@ static void get_managed_objects_cb(struct l_dbus_message *msg, void *user_data) node->num_ele = num_ele; - if (req->type != REQUEST_TYPE_ATTACH) { - /* Generate node configuration for a brand new node */ - if (!create_node_config(node, node->uuid)) - goto fail; - - /* Create node composition */ - if (!create_node_comp(node)) - goto fail; - } else if (!check_req_node(req)) - /* Check the integrity of the node composition */ + if (!check_req_node(req)) goto fail; switch (req->type) { diff --git a/mesh/node.h b/mesh/node.h index 2e3d89812..a98945223 100644 --- a/mesh/node.h +++ b/mesh/node.h @@ -38,6 +38,8 @@ uint16_t node_get_primary_net_idx(struct mesh_node *node); void node_set_token(struct mesh_node *node, uint8_t token[8]); const uint8_t *node_get_token(struct mesh_node *node); const uint8_t *node_get_device_key(struct mesh_node *node); +bool node_get_device_key_candidate(struct mesh_node *node, uint8_t *key); +void node_finalize_candidate(struct mesh_node *node); void node_set_num_elements(struct mesh_node *node, uint8_t num_ele); uint8_t node_get_num_elements(struct mesh_node *node); uint8_t node_default_ttl_get(struct mesh_node *node); @@ -89,3 +91,4 @@ const char *node_get_storage_dir(struct mesh_node *node); bool node_load_from_storage(const char *storage_dir); void node_finalize_new_node(struct mesh_node *node, struct mesh_io *io); void node_property_changed(struct mesh_node *node, const char *property); +bool node_refresh(struct mesh_node *node, bool hard, void *prov_info); diff --git a/mesh/pb-adv.c b/mesh/pb-adv.c index 180b16258..385d81d65 100644 --- a/mesh/pb-adv.c +++ b/mesh/pb-adv.c @@ -219,7 +219,7 @@ static void tx_timeout(struct l_timeout *timeout, void *user_data) cb(user_data, 1); } -static void pb_adv_tx(void *user_data, void *data, uint16_t len) +static void pb_adv_tx(void *user_data, const void *data, uint16_t len) { struct pb_adv_session *session = user_data; @@ -478,7 +478,7 @@ static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len) bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, - uint8_t uuid[16], void *user_data) + const uint8_t *uuid, void *user_data) { struct pb_adv_session *session, *old_session; diff --git a/mesh/pb-adv.h b/mesh/pb-adv.h index 5b1e03dae..e33ba8e35 100644 --- a/mesh/pb-adv.h +++ b/mesh/pb-adv.h @@ -11,5 +11,5 @@ bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb, mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb, - uint8_t uuid[16], void *user_data); + const uint8_t *uuid, void *user_data); void pb_adv_unreg(void *user_data); diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c index bf8c573da..fd9d4cd5d 100644 --- a/mesh/prov-acceptor.c +++ b/mesh/prov-acceptor.c @@ -22,6 +22,7 @@ #include "mesh/net.h" #include "mesh/prov.h" #include "mesh/provision.h" +#include "mesh/remprv.h" #include "mesh/pb-adv.h" #include "mesh/mesh.h" #include "mesh/agent.h" @@ -169,9 +170,6 @@ static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx, prov->transport != transport) return; - if (transport != PB_ADV) - return; - prov->trans_tx = trans_tx; prov->transport = transport; prov->trans_data = trans_data; @@ -425,9 +423,10 @@ static bool prov_start_check(struct prov_start *start, return true; } -static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len) +static void acp_prov_rx(void *user_data, const void *dptr, uint16_t len) { struct mesh_prov_acceptor *rx_prov = user_data; + const uint8_t *data = dptr; struct mesh_prov_node_info *info; struct prov_fail_msg fail; uint8_t type = *data++; @@ -654,14 +653,19 @@ static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len) info->flags = prov->rand_auth_workspace[18]; info->iv_index = l_get_be32(prov->rand_auth_workspace + 19); info->unicast = l_get_be16(prov->rand_auth_workspace + 23); + info->num_ele = prov->conf_inputs.caps.num_ele; + + /* Send prov complete */ + prov->rand_auth_workspace[0] = PROV_COMPLETE; + prov->trans_tx(prov->trans_data, + prov->rand_auth_workspace, 1); result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info); prov->cmplt = NULL; l_free(info); if (result) { - prov->rand_auth_workspace[0] = PROV_COMPLETE; - prov_send(prov, prov->rand_auth_workspace, 1); + l_debug("PROV_COMPLETE"); goto cleanup; } else { fail.reason = PROV_ERR_UNEXPECTED_ERR; @@ -721,7 +725,7 @@ static void acp_prov_ack(void *user_data, uint8_t msg_num) /* This starts unprovisioned device beacon */ -bool acceptor_start(uint8_t num_ele, uint8_t uuid[16], +bool acceptor_start(uint8_t num_ele, uint8_t *uuid, uint16_t algorithms, uint32_t timeout, struct mesh_agent *agent, mesh_prov_acceptor_complete_func_t complete_cb, @@ -733,8 +737,10 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16], uint8_t len = sizeof(beacon) - sizeof(uint32_t); bool result; - /* Invoked from Join() method in mesh-api.txt, to join a - * remote mesh network. + /* + * Invoked from Join() method in mesh-api.txt, to join a + * remote mesh network. May also be invoked with a NULL + * uuid to perform a Device Key Refresh procedure. */ if (prov) @@ -752,37 +758,50 @@ bool acceptor_start(uint8_t num_ele, uint8_t uuid[16], caps = mesh_agent_get_caps(agent); - /* TODO: Should we sanity check values here or elsewhere? */ prov->conf_inputs.caps.num_ele = num_ele; - prov->conf_inputs.caps.pub_type = caps->pub_type; - prov->conf_inputs.caps.static_type = caps->static_type; - prov->conf_inputs.caps.output_size = caps->output_size; - prov->conf_inputs.caps.input_size = caps->input_size; - - /* Store UINT16 values in Over-the-Air order, in packed structure - * for crypto inputs - */ l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms); - l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action); - l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action); - - /* Compose Unprovisioned Beacon */ - memcpy(beacon + 2, uuid, 16); - l_put_be16(caps->oob_info, beacon + 18); - if (caps->oob_info & OOB_INFO_URI_HASH){ - l_put_be32(caps->uri_hash, beacon + 20); - len += sizeof(uint32_t); + + if (caps) { + /* TODO: Should we sanity check values here or elsewhere? */ + prov->conf_inputs.caps.pub_type = caps->pub_type; + prov->conf_inputs.caps.static_type = caps->static_type; + prov->conf_inputs.caps.output_size = caps->output_size; + prov->conf_inputs.caps.input_size = caps->input_size; + + /* Store UINT16 values in Over-the-Air order, in packed + * structure for crypto inputs + */ + l_put_be16(caps->output_action, + &prov->conf_inputs.caps.output_action); + l_put_be16(caps->input_action, + &prov->conf_inputs.caps.input_action); + + /* Populate Caps fields of beacon */ + l_put_be16(caps->oob_info, beacon + 18); + if (caps->oob_info & OOB_INFO_URI_HASH) { + l_put_be32(caps->uri_hash, beacon + 20); + len += sizeof(uint32_t); + } } - /* Infinitely Beacon until Canceled, or Provisioning Starts */ - result = mesh_send_pkt(0, 500, beacon, len); + if (uuid) { + /* Compose Unprovisioned Beacon */ + memcpy(beacon + 2, uuid, 16); + + /* Infinitely Beacon until Canceled, or Provisioning Starts */ + result = mesh_send_pkt(0, 500, beacon, len); - if (!result) - goto error_fail; + if (!result) + goto error_fail; - /* Always register for PB-ADV */ - result = pb_adv_reg(false, acp_prov_open, acp_prov_close, acp_prov_rx, - acp_prov_ack, uuid, prov); + /* Always register for PB-ADV */ + result = pb_adv_reg(false, acp_prov_open, acp_prov_close, + acp_prov_rx, acp_prov_ack, uuid, prov); + } else { + /* Run Device Key Refresh Procedure */ + result = register_nppi_acceptor(acp_prov_open, acp_prov_close, + acp_prov_rx, acp_prov_ack, prov); + } if (result) return true; diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c index c62577523..653f3ae3e 100644 --- a/mesh/prov-initiator.c +++ b/mesh/prov-initiator.c @@ -21,10 +21,12 @@ #include "mesh/crypto.h" #include "mesh/net.h" #include "mesh/node.h" +#include "mesh/model.h" #include "mesh/keyring.h" #include "mesh/prov.h" #include "mesh/provision.h" #include "mesh/pb-adv.h" +#include "mesh/remprv.h" #include "mesh/mesh.h" #include "mesh/agent.h" #include "mesh/error.h" @@ -82,12 +84,16 @@ struct mesh_prov_initiator { struct l_timeout *timeout; uint32_t to_secs; enum int_state state; - enum trans_type transport; uint16_t net_idx; + uint16_t svr_idx; uint16_t unicast; + uint16_t server; + uint8_t transport; uint8_t material; uint8_t expected; int8_t previous; + uint8_t out_num; + uint8_t rpr_state; struct conf_input conf_inputs; uint8_t calc_key[16]; uint8_t salt[16]; @@ -100,14 +106,23 @@ struct mesh_prov_initiator { uint8_t uuid[16]; }; +struct scan_req { + mesh_prov_initiator_scan_result_t scan_result; + struct mesh_node *node; + int count; +}; + static struct mesh_prov_initiator *prov = NULL; +static struct l_queue *scans; static void initiator_free(void) { - if (prov) + if (prov) { l_timeout_remove(prov->timeout); - mesh_send_cancel(&pkt_filter, sizeof(pkt_filter)); + if (!prov->server) + mesh_send_cancel(&pkt_filter, sizeof(pkt_filter)); + } pb_adv_unreg(prov); @@ -119,6 +134,15 @@ static void int_prov_close(void *user_data, uint8_t reason) { struct mesh_prov_initiator *prov = user_data; struct mesh_prov_node_info info; + uint8_t msg[4]; + int n; + + if (prov->server) { + n = mesh_model_opcode_set(OP_REM_PROV_LINK_CLOSE, msg); + msg[n++] = reason == PROV_ERR_SUCCESS ? 0x00 : 0x02; + mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE, + prov->svr_idx, DEFAULT_TTL, true, n, msg); + } if (reason != PROV_ERR_SUCCESS) { prov->complete_cb(prov->caller_data, reason, NULL); @@ -626,9 +650,10 @@ static void int_prov_start_auth(const struct mesh_agent_prov_caps *prov_caps, } } -static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len) +static void int_prov_rx(void *user_data, const void *dptr, uint16_t len) { struct mesh_prov_initiator *rx_prov = user_data; + const uint8_t *data = dptr; uint8_t *out; uint8_t type = *data++; uint8_t fail_code[2]; @@ -651,7 +676,7 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len) if (type >= L_ARRAY_SIZE(expected_pdu_size) || len != expected_pdu_size[type]) { l_error("Expected PDU size %d, Got %d (type: %2.2x)", - len, expected_pdu_size[type], type); + expected_pdu_size[type], len, type); fail_code[1] = PROV_ERR_INVALID_FORMAT; goto failure; } @@ -773,7 +798,12 @@ static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len) goto failure; } - if (!prov->data_req_cb(prov->caller_data, + if (prov->transport == PB_NPPI_00 || + prov->transport == PB_NPPI_02) { + /* No App data needed */ + initiator_prov_data(prov->svr_idx, prov->server, + prov->caller_data); + } else if (!prov->data_req_cb(prov->caller_data, prov->conf_inputs.caps.num_ele)) { l_error("Provisioning Failed-Data Get"); fail_code[1] = PROV_ERR_CANT_ASSIGN_ADDR; @@ -851,6 +881,8 @@ static void int_prov_ack(void *user_data, uint8_t msg_num) static void initiator_open_cb(void *user_data, int err) { + uint8_t msg[20]; + int n; bool result; if (!prov) @@ -859,18 +891,30 @@ static void initiator_open_cb(void *user_data, int err) if (err != MESH_ERROR_NONE) goto fail; - /* Always register for PB-ADV */ - result = pb_adv_reg(true, int_prov_open, int_prov_close, int_prov_rx, - int_prov_ack, prov->uuid, prov); + if (prov->server) { + n = mesh_model_opcode_set(OP_REM_PROV_LINK_OPEN, msg); + + if (prov->transport <= PB_NPPI_02) { + msg[n++] = prov->transport; + } else { + memcpy(msg + n, prov->uuid, 16); + n += 16; + } + + result = mesh_model_send(prov->node, 0, prov->server, + APP_IDX_DEV_REMOTE, prov->svr_idx, + DEFAULT_TTL, true, n, msg); + } else { + /* Always register for PB-ADV */ + result = pb_adv_reg(true, int_prov_open, int_prov_close, + int_prov_rx, int_prov_ack, prov->uuid, prov); + } if (!result) { err = MESH_ERROR_FAILED; goto fail; } - if (!prov) - return; - prov->start_cb(prov->caller_data, MESH_ERROR_NONE); return; fail: @@ -878,10 +922,20 @@ fail: initiator_free(); } -bool initiator_start(enum trans_type transport, - uint8_t uuid[16], - uint16_t max_ele, - uint32_t timeout, /* in seconds from mesh.conf */ +static void initiate_to(struct l_timeout *timeout, void *user_data) +{ + struct mesh_prov_initiator *rx_prov = user_data; + + if (rx_prov != prov) { + l_timeout_remove(timeout); + return; + } + + int_prov_close(user_data, PROV_ERR_TIMEOUT); +} + +bool initiator_start(uint8_t transport, uint16_t server, uint16_t svr_idx, + uint8_t uuid[16], uint16_t max_ele, uint32_t timeout, struct mesh_agent *agent, mesh_prov_initiator_start_func_t start_cb, mesh_prov_initiator_data_req_func_t data_req_cb, @@ -904,6 +958,10 @@ bool initiator_start(enum trans_type transport, prov->data_req_cb = data_req_cb; prov->caller_data = caller_data; prov->previous = -1; + prov->server = server; + prov->svr_idx = svr_idx; + prov->transport = transport; + prov->timeout = l_timeout_create(timeout, initiate_to, prov, NULL); memcpy(prov->uuid, uuid, 16); mesh_agent_refresh(prov->agent, initiator_open_cb, prov); @@ -915,3 +973,182 @@ void initiator_cancel(void *user_data) { initiator_free(); } + +static void rpr_tx(void *user_data, const void *data, uint16_t len) +{ + struct mesh_prov_initiator *prov = user_data; + uint8_t msg[72]; + int n; + + n = mesh_model_opcode_set(OP_REM_PROV_PDU_SEND, msg); + msg[n++] = ++prov->out_num; + memcpy(msg + n, data, len); + l_debug("Send OB %2.2x, with packet type %d", msg[n], prov->out_num); + n += len; + + prov->rpr_state = PB_REMOTE_STATE_OB_PKT_TX; + mesh_model_send(prov->node, 0, prov->server, APP_IDX_DEV_REMOTE, + prov->svr_idx, DEFAULT_TTL, true, n, msg); +} + +static bool match_req_node(const void *a, const void *b) +{ + const struct scan_req *req = a; + const struct mesh_node *node = b; + + return req->node == node; +} + +static bool remprv_cli_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx, + uint16_t net_idx, const uint8_t *data, + uint16_t size, const void *user_data) +{ + struct mesh_node *node = (struct mesh_node *) user_data; + const uint8_t *pkt = data; + struct scan_req *req; + uint32_t opcode; + uint16_t n; + + if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { + size -= n; + pkt += n; + } else + return false; + + if (opcode < OP_REM_PROV_SCAN_CAP_GET || + opcode > OP_REM_PROV_PDU_REPORT) + return false; + + if (app_idx != APP_IDX_DEV_REMOTE && app_idx != APP_IDX_DEV_LOCAL) + return true; + + /* Local Dev key only allowed for Loop-backs */ + if (app_idx == APP_IDX_DEV_LOCAL && unicast != src) + return true; + + if (prov && (prov->server != src || prov->node != node)) + return true; + + n = 0; + + switch (opcode) { + default: + return false; + + /* Provisioning Opcodes */ + case OP_REM_PROV_LINK_STATUS: + if (size != 2 || !prov) + break; + + if (pkt[0] == PB_REM_ERR_SUCCESS) + prov->rpr_state = pkt[1]; + + break; + + case OP_REM_PROV_LINK_REPORT: + if (size != 2 || !prov) + return true; + + if (pkt[0] != PB_REM_ERR_SUCCESS) { + if (pkt[0] == PB_REM_ERR_CLOSED_BY_DEVICE || + pkt[0] == PB_REM_ERR_CLOSED_BY_SERVER) + int_prov_close(prov, pkt[1]); + + break; + } + + + if (prov->rpr_state == PB_REMOTE_STATE_LINK_OPENING) + int_prov_open(prov, rpr_tx, prov, prov->transport); + else if (prov->rpr_state == PB_REMOTE_STATE_LINK_CLOSING) { + prov->rpr_state = PB_REMOTE_STATE_IDLE; + int_prov_close(prov, pkt[1]); + break; + } + + prov->rpr_state = pkt[1]; + + break; + + case OP_REM_PROV_PDU_REPORT: + int_prov_rx(prov, pkt + 1, size - 1); + break; + + case OP_REM_PROV_PDU_OB_REPORT: + if (size != 1 || !prov) + break; + + l_debug("Got Ack for OB %d", pkt[0]); + if (prov->rpr_state == PB_REMOTE_STATE_OB_PKT_TX && + pkt[0] == prov->out_num) + int_prov_ack(prov, pkt[0]); + + break; + + /* Scan Opcodes */ + case OP_REM_PROV_SCAN_CAP_STATUS: + case OP_REM_PROV_SCAN_STATUS: + break; + + case OP_REM_PROV_SCAN_REPORT: + case OP_REM_PROV_EXT_SCAN_REPORT: + req = l_queue_find(scans, match_req_node, node); + if (req) { + req->scan_result(node, src, + opcode == OP_REM_PROV_EXT_SCAN_REPORT, + pkt, size); + } + } + + return true; +} + +void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result, + void *user_data) +{ + struct scan_req *req; + + if (!scans) + scans = l_queue_new(); + + req = l_queue_find(scans, match_req_node, user_data); + if (!req) { + req = l_new(struct scan_req, 1); + l_queue_push_head(scans, req); + } + + req->scan_result = scan_result; + req->node = user_data; + req->count++; +} + +void initiator_scan_unreg(void *user_data) +{ + struct scan_req *req; + + req = l_queue_find(scans, match_req_node, user_data); + if (req) { + req->count--; + if (!req->count) { + l_queue_remove(scans, req); + l_free(req); + } + } +} + +static void remprv_cli_unregister(void *user_data) +{ +} + +static const struct mesh_model_ops ops = { + .unregister = remprv_cli_unregister, + .recv = remprv_cli_pkt, + .bind = NULL, + .sub = NULL, + .pub = NULL +}; + +void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx) +{ + mesh_model_register(node, ele_idx, REM_PROV_CLI_MODEL, &ops, node); +} diff --git a/mesh/prov.h b/mesh/prov.h index 99e864c50..e86668fe4 100644 --- a/mesh/prov.h +++ b/mesh/prov.h @@ -39,14 +39,14 @@ enum mesh_prov_mode { struct mesh_prov; -typedef void (*prov_trans_tx_t)(void *trans_data, void *data, uint16_t len); +typedef void (*prov_trans_tx_t)(void *tx_data, const void *data, uint16_t len); typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx, void *trans_data, uint8_t trans_type); typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason); typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov); typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num); -typedef void (*mesh_prov_receive_func_t)(void *user_data, const uint8_t *data, +typedef void (*mesh_prov_receive_func_t)(void *user_data, const void *data, uint16_t size); diff --git a/mesh/provision.h b/mesh/provision.h index 1634c4d40..cfeb6deba 100644 --- a/mesh/provision.h +++ b/mesh/provision.h @@ -70,10 +70,11 @@ struct mesh_agent; #define OOB_INFO_URI_HASH 0x0002 /* PB_REMOTE not supported from unprovisioned state */ -enum trans_type { - PB_ADV = 0, - PB_GATT, -}; +#define PB_NPPI_00 0x00 +#define PB_NPPI_01 0x01 +#define PB_NPPI_02 0x02 +#define PB_ADV 0x03 /* Internal only, and may be reassigned */ +#define PB_GATT 0x04 /* Internal only, and may be reassigned */ #define PROV_FLAG_KR 0x01 #define PROV_FLAG_IVU 0x02 @@ -101,15 +102,21 @@ typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data, uint8_t status, struct mesh_prov_node_info *info); +typedef void (*mesh_prov_initiator_scan_result_t)(void *user_data, + uint16_t server, bool extended, + const uint8_t *data, uint16_t len); + /* This starts unprovisioned device beacon */ -bool acceptor_start(uint8_t num_ele, uint8_t uuid[16], +bool acceptor_start(uint8_t num_ele, uint8_t *uuid, uint16_t algorithms, uint32_t timeout, struct mesh_agent *agent, mesh_prov_acceptor_complete_func_t complete_cb, void *caller_data); void acceptor_cancel(void *user_data); -bool initiator_start(enum trans_type transport, +bool initiator_start(uint8_t transport, + uint16_t server, + uint16_t svr_idx, uint8_t uuid[16], uint16_t max_ele, uint32_t timeout, /* in seconds from mesh.conf */ @@ -120,3 +127,7 @@ bool initiator_start(enum trans_type transport, void *node, void *caller_data); void initiator_prov_data(uint16_t net_idx, uint16_t primary, void *caller_data); void initiator_cancel(void *caller_data); + +void initiator_scan_reg(mesh_prov_initiator_scan_result_t scan_result, + void *user_data); +void initiator_scan_unreg(void *caller_data); diff --git a/mesh/remprv-server.c b/mesh/remprv-server.c new file mode 100644 index 000000000..85b473128 --- /dev/null +++ b/mesh/remprv-server.c @@ -0,0 +1,907 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "src/shared/ad.h" + +#include "mesh/mesh-defs.h" +#include "mesh/mesh-io.h" +#include "mesh/util.h" +#include "mesh/node.h" +#include "mesh/net.h" +#include "mesh/appkey.h" +#include "mesh/model.h" +#include "mesh/prov.h" +#include "mesh/provision.h" +#include "mesh/pb-adv.h" +#include "mesh/remprv.h" + +#define EXT_LIST_SIZE 60 + +#define RPR_DEV_KEY 0x00 +#define RPR_ADDR 0x01 +#define RPR_COMP 0x02 +#define RPR_ADV 0xFF /* Internal use only*/ + +struct rem_scan_data { + struct mesh_node *node; + struct l_timeout *timeout; + uint8_t *list; + uint16_t client; + uint16_t oob_info; + uint16_t net_idx; + uint8_t state; + uint8_t scanned_limit; + uint8_t addr[6]; + uint8_t uuid[16]; + uint8_t to_secs; + uint8_t rxed_ads; + uint8_t ext_cnt; + bool fltr; + uint8_t ext[0]; +}; + +static struct rem_scan_data *rpb_scan; + +struct rem_prov_data { + struct mesh_node *node; + struct l_timeout *timeout; + void *trans_data; + uint16_t client; + uint16_t net_idx; + uint8_t svr_pdu_num; + uint8_t cli_pdu_num; + uint8_t state; + uint8_t nppi_proc; + union { + struct { + mesh_prov_open_func_t open_cb; + mesh_prov_close_func_t close_cb; + mesh_prov_receive_func_t rx_cb; + mesh_prov_ack_func_t ack_cb; + struct mesh_prov_node_info info; + } nppi; + struct { + uint8_t uuid[17]; + prov_trans_tx_t tx; + } adv; + } u; +}; + +static struct rem_prov_data *rpb_prov; + +static const uint8_t prvb[2] = {BT_AD_MESH_BEACON, 0x00}; +static const uint8_t pkt_filter = BT_AD_MESH_PROV; +static const char *name = "Test Name"; + +static const uint8_t zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static void srv_open(void *user_data, prov_trans_tx_t adv_tx, + void *trans_data, uint8_t nppi_proc) +{ + struct rem_prov_data *prov = user_data; + uint8_t msg[5]; + int n; + + if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING) + return; + + l_debug("Remote Link open confirmed"); + prov->u.adv.tx = adv_tx; + prov->trans_data = trans_data; + prov->state = PB_REMOTE_STATE_LINK_ACTIVE; + + n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + msg[n++] = prov->state; + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, true, n, msg); +} + +static void srv_rx(void *user_data, const void *dptr, uint16_t len) +{ + struct rem_prov_data *prov = user_data; + const uint8_t *data = dptr; + uint8_t msg[69]; + int n; + + if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE || + len > 65) + return; + + l_debug("Remote PB IB-PDU"); + + prov->svr_pdu_num++; + n = mesh_model_opcode_set(OP_REM_PROV_PDU_REPORT, msg); + msg[n++] = prov->svr_pdu_num; + memcpy(msg + n, data, len); + n += len; + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, true, n, msg); +} + +static void srv_ack(void *user_data, uint8_t msg_num) +{ + struct rem_prov_data *prov = user_data; + uint8_t msg[4]; + int n; + + if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_OB_PKT_TX) + return; + + l_debug("Remote PB ACK"); + + prov->state = PB_REMOTE_STATE_LINK_ACTIVE; + n = mesh_model_opcode_set(OP_REM_PROV_PDU_OB_REPORT, msg); + msg[n++] = prov->cli_pdu_num; + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, true, n, msg); +} + +static void srv_close(void *user_data, uint8_t reason) +{ + struct rem_prov_data *prov = user_data; + uint8_t msg[4]; + int n; + + if (prov != rpb_prov || prov->state < PB_REMOTE_STATE_LINK_ACTIVE) + return; + + l_debug("Remote PB Close"); + + prov->state = PB_REMOTE_STATE_LINK_CLOSING; + n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); + msg[n++] = prov->state; + msg[n++] = reason; + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, true, n, msg); +} + +static void send_prov_status(struct rem_prov_data *prov, uint8_t status) +{ + uint16_t n; + uint8_t msg[5]; + bool segmented = prov->state == PB_REMOTE_STATE_LINK_CLOSING ? + true : false; + + n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); + msg[n++] = status; + msg[n++] = prov->state; + + l_info("RPB-Link Status(%d): dst %4.4x", prov->state, prov->client); + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, segmented, n, msg); +} + +static void remprv_prov_cancel(struct l_timeout *timeout, + void *user_data) +{ + struct rem_prov_data *prov = user_data; + + if (prov != rpb_prov) + return; + + l_timeout_remove(prov->timeout); + l_free(prov); + rpb_prov = NULL; +} + +static void deregister_ext_ad_type(uint8_t ad_type) +{ + uint8_t short_ad; + + switch (ad_type) { + case BT_AD_MESH_BEACON: + case BT_AD_MESH_DATA: + case BT_AD_MESH_PROV: + case BT_AD_UUID16_SOME: + case BT_AD_UUID32_SOME: + case BT_AD_UUID128_SOME: + case BT_AD_NAME_SHORT: + return; + + case BT_AD_UUID16_ALL: + case BT_AD_UUID32_ALL: + case BT_AD_UUID128_ALL: + case BT_AD_NAME_COMPLETE: + /* Automatically get short versions */ + short_ad = ad_type - 1; + mesh_io_deregister_recv_cb(NULL, &short_ad, 1); + + /* fallthrough */ + default: + mesh_io_deregister_recv_cb(NULL, &ad_type, 1); + break; + } +} + +static void remprv_scan_cancel(struct l_timeout *timeout, + void *user_data) +{ + struct rem_scan_data *scan = user_data; + uint8_t msg[22 + EXT_LIST_SIZE]; + uint16_t i, n; + + if (!scan || scan != rpb_scan) + return; + + for (n = 0; n < scan->ext_cnt; n++) + deregister_ext_ad_type(scan->ext[n]); + + if (scan->timeout == timeout) { + /* Return Extended Results */ + if (scan->ext_cnt) { + /* Return Extended Result */ + n = mesh_model_opcode_set( + OP_REM_PROV_EXT_SCAN_REPORT, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + memcpy(msg + n, scan->uuid, 16); + n += 16; + + if (scan->oob_info) { + l_put_le16(0, msg + n); + n += 2; + } + + i = 0; + while (scan->list[i]) { + msg[n++] = scan->list[i]; + memcpy(msg + n, &scan->list[i + 1], + scan->list[i]); + n += scan->list[i]; + i += scan->list[i] + 1; + } + } + } + + l_timeout_remove(scan->timeout); + l_free(scan->list); + l_free(scan); + rpb_scan = NULL; +} + +static void scan_pkt(void *user_data, struct mesh_io_recv_info *info, + const uint8_t *data, uint16_t len) +{ + struct rem_scan_data *scan = user_data; + uint8_t msg[22 + EXT_LIST_SIZE]; + uint16_t i, n; + uint8_t filled = 0; + bool report = false; + + if (scan != rpb_scan) + return; + + if (scan->ext_cnt) + goto extended_scan; + + /* RX Unprovisioned Beacon */ + if (data[0] != BT_AD_MESH_BEACON || data[1] || + (len != 18 && len != 20 && len != 24)) + return; + + data += 2; + len -= 2; + + for (n = 0; !report && n < scan->scanned_limit; n++) { + if (!memcmp(&scan->list[n * 17 + 1], data, 16)) { + + /* Repeat UUID, check RSSI */ + if ((int8_t) scan->list[n * 17] < info->rssi) { + report = true; + scan->list[n * 17] = (uint8_t) info->rssi; + } + + } else if (!memcmp(&scan->list[n * 17 + 1], zero, 16)) { + + /* Found Empty slot */ + report = true; + scan->list[n * 17] = (uint8_t) info->rssi; + memcpy(&scan->list[n * 17 + 1], data, 16); + } + + filled++; + } + + if (!report) + return; + + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_REPORT, msg); + msg[n++] = (uint8_t) info->rssi; + memcpy(msg + n, data, len); + n += len; + + /* Always return oob_info, even if it wasn't in beacon */ + if (len == 16) { + l_put_le16(0, msg + n); + n += 2; + } + + goto send_report; + +extended_scan: + if (data[0] == BT_AD_MESH_BEACON && !data[1]) { + if (len != 18 && len != 20 && len != 24) + return; + + /* Check UUID */ + if (memcmp(data + 2, scan->uuid, 16)) + return; + + /* Zero AD list if prior data RXed from different bd_addr */ + if (memcmp(scan->addr, info->addr, 6)) { + scan->list[0] = 0; + scan->rxed_ads = 0; + } + + memcpy(scan->addr, info->addr, 6); + scan->fltr = true; + + if (len >= 20) + scan->oob_info = l_get_le16(data + 18); + + if (scan->rxed_ads != scan->ext_cnt) + return; + + + } else if (data[0] != BT_AD_MESH_BEACON) { + if (!scan->fltr || !memcmp(scan->addr, info->addr, 6)) { + i = 0; + while (scan->list[i]) { + /* check if seen */ + if (scan->list[i + 1] == data[0]) + return; + + i += scan->list[i] + 1; + } + + /* Overflow Protection */ + if (i + len + 1 > EXT_LIST_SIZE) + return; + + scan->list[i] = len; + scan->list[i + len + 1] = 0; + memcpy(scan->list + i + 1, data, len); + scan->rxed_ads++; + } + + if (scan->rxed_ads != scan->ext_cnt) + return; + + } else + return; + + n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + memcpy(msg + n, scan->uuid, 16); + n += 16; + l_put_le16(scan->oob_info, msg + n); + n += 2; + + i = 0; + while (scan->list[i]) { + msg[n++] = scan->list[i]; + memcpy(msg + n, &scan->list[i + 1], scan->list[i]); + n += scan->list[i]; + i += scan->list[i]; + } + +send_report: + print_packet("App Tx", msg, n); + mesh_model_send(scan->node, 0, scan->client, APP_IDX_DEV_LOCAL, + scan->net_idx, DEFAULT_TTL, true, n, msg); + + /* Clean-up if we are done reporting*/ + if (filled == scan->scanned_limit || scan->ext_cnt) + remprv_scan_cancel(NULL, scan); +} + +static bool register_ext_ad_type(uint8_t ad_type, struct rem_scan_data *scan) +{ + uint8_t short_ad; + + switch (ad_type) { + case BT_AD_MESH_PROV: + case BT_AD_UUID16_SOME: + case BT_AD_UUID32_SOME: + case BT_AD_UUID128_SOME: + case BT_AD_NAME_SHORT: + /* Illegal Requests */ + return false; + + case BT_AD_UUID16_ALL: + case BT_AD_UUID32_ALL: + case BT_AD_UUID128_ALL: + case BT_AD_NAME_COMPLETE: + /* Automatically get short versions */ + short_ad = ad_type - 1; + mesh_io_register_recv_cb(NULL, &short_ad, 1, scan_pkt, scan); + + /* fallthrough */ + default: + mesh_io_register_recv_cb(NULL, &ad_type, 1, scan_pkt, scan); + + /* fallthrough */ + + case BT_AD_MESH_BEACON: + /* Ignored/auto request */ + break; + } + + return true; +} + +static void link_active(void *user_data) +{ + struct rem_prov_data *prov = user_data; + uint8_t msg[5]; + int n; + + if (prov != rpb_prov || prov->state != PB_REMOTE_STATE_LINK_OPENING) + return; + + l_debug("Remote Link open confirmed"); + prov->state = PB_REMOTE_STATE_LINK_ACTIVE; + + n = mesh_model_opcode_set(OP_REM_PROV_LINK_REPORT, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; + + mesh_model_send(prov->node, 0, prov->client, APP_IDX_DEV_LOCAL, + prov->net_idx, DEFAULT_TTL, true, n, msg); +} + +bool register_nppi_acceptor(mesh_prov_open_func_t open_cb, + mesh_prov_close_func_t close_cb, + mesh_prov_receive_func_t rx_cb, + mesh_prov_ack_func_t ack_cb, + void *user_data) +{ + struct rem_prov_data *prov = rpb_prov; + + if (!prov || prov->nppi_proc == RPR_ADV) + return false; + + prov->u.nppi.open_cb = open_cb; + prov->u.nppi.close_cb = close_cb; + prov->u.nppi.rx_cb = rx_cb; + prov->u.nppi.ack_cb = ack_cb; + prov->trans_data = user_data; + + open_cb(user_data, srv_rx, prov, prov->nppi_proc); + + l_idle_oneshot(link_active, prov, NULL); + + return true; +} + +static bool nppi_cmplt(void *user_data, uint8_t status, + struct mesh_prov_node_info *info) +{ + struct rem_prov_data *prov = user_data; + + if (prov != rpb_prov) + return false; + + /* Save new info to apply on Link Close */ + prov->u.nppi.info = *info; + return true; +} + +static bool start_dev_key_refresh(struct mesh_node *node, uint8_t nppi_proc, + struct rem_prov_data *prov) +{ + uint8_t num_ele = node_get_num_elements(node); + + prov->nppi_proc = nppi_proc; + return acceptor_start(num_ele, NULL, 0x0001, 60, NULL, nppi_cmplt, + prov); +} + +static bool remprv_srv_pkt(uint16_t src, uint16_t unicast, uint16_t app_idx, + uint16_t net_idx, const uint8_t *data, + uint16_t size, const void *user_data) +{ + struct rem_prov_data *prov = rpb_prov; + struct rem_scan_data *scan = rpb_scan; + struct mesh_node *node = (struct mesh_node *) user_data; + const uint8_t *pkt = data; + bool segmented = false; + uint32_t opcode; + uint8_t msg[69]; + uint8_t status; + uint16_t n; + + if (app_idx != APP_IDX_DEV_LOCAL) + return false; + + if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { + size -= n; + pkt += n; + } else + return false; + + n = 0; + + switch (opcode) { + default: + return false; + + case OP_REM_PROV_SCAN_CAP_GET: + if (size != 0) + return true; + + /* Compose Scan Info Status */ + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_CAP_STATUS, msg); + msg[n++] = PB_REMOTE_MAX_SCAN_QUEUE_SIZE; + msg[n++] = 1; /* Active Scanning Supported */ + break; + + case OP_REM_PROV_EXT_SCAN_START: + if (!size || !pkt[0]) + return true; + + /* Size check the message */ + if (pkt[0] + 18 == size) { + /* Range check the Timeout */ + if (!pkt[size - 1] || pkt[size - 1] > 5) + return true; + } else if (pkt[0] + 1 != size) + return true; + + /* Get local device extended info */ + if (pkt[0] + 18 != size) { + n = mesh_model_opcode_set( + OP_REM_PROV_EXT_SCAN_REPORT, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + memcpy(msg + n, node_uuid_get(node), 16); + n += 16; + l_put_le16(0, msg + n); + n += 2; + size--; + pkt++; + + while (size--) { + if (*pkt++ == BT_AD_NAME_COMPLETE) { + msg[n] = strlen(name) + 1; + if (msg[n] > sizeof(msg) - n - 1) + msg[n] = sizeof(msg) - n - 1; + n++; + msg[n++] = BT_AD_NAME_COMPLETE; + memcpy(&msg[n], name, msg[n - 2] - 1); + n += msg[n - 2] - 1; + goto send_pkt; + } + } + + /* Send internal report */ + l_debug("Send internal extended info %d", n); + goto send_pkt; + } + + status = PB_REM_ERR_SUCCESS; + if (scan) { + if (scan->client != src || scan->node != node || + scan->ext_cnt != pkt[0]) + status = PB_REM_ERR_SCANNING_CANNOT_START; + else if (memcmp(scan->ext, pkt + 1, pkt[0])) + status = PB_REM_ERR_SCANNING_CANNOT_START; + else if (memcmp(scan->uuid, pkt + 2, 16)) + status = PB_REM_ERR_SCANNING_CANNOT_START; + } + + if (status != PB_REM_ERR_SUCCESS) { + n = mesh_model_opcode_set(OP_REM_PROV_EXT_SCAN_REPORT, + msg); + msg[n++] = status; + memset(msg + n, 0, 16); + n += 16; + segmented = true; + break; + } + + /* Ignore extended requests while already scanning */ + if (scan) + return true; + + scan = (void *) l_new(uint8_t, + sizeof(struct rem_scan_data) + pkt[0]); + + /* Validate and register Extended AD types */ + for (n = 0; n < pkt[0]; n++) { + if (!register_ext_ad_type(pkt[1 + n], scan)) { + /* Invalid AD type detected -- Undo */ + while (n--) + deregister_ext_ad_type(pkt[1 + n]); + + l_free(scan); + return true; + } + } + + rpb_scan = scan; + scan->client = src; + scan->net_idx = net_idx; + memcpy(scan->uuid, pkt + size - 17, 16); + scan->ext_cnt = pkt[0]; + memcpy(scan->ext, pkt + 1, pkt[0]); + scan->list = l_malloc(EXT_LIST_SIZE); + scan->list[0] = 0; + + mesh_io_register_recv_cb(NULL, prvb, sizeof(prvb), + scan_pkt, scan); + + scan->timeout = l_timeout_create(pkt[size-1], + remprv_scan_cancel, scan, NULL); + return true; + + case OP_REM_PROV_SCAN_START: + if (size != 2 && size != 18) + return true; + + /* Reject Timeout of Zero */ + if (!pkt[1]) + return true; + + status = PB_REM_ERR_SUCCESS; + if (scan) { + if (scan->ext_cnt || scan->client != src || + scan->node != node) + status = PB_REM_ERR_SCANNING_CANNOT_START; + else if (!!(scan->fltr) != !!(size != 18)) + status = PB_REM_ERR_SCANNING_CANNOT_START; + else if (scan->fltr && memcmp(scan->uuid, pkt + 2, 16)) + status = PB_REM_ERR_SCANNING_CANNOT_START; + } + + if (status != PB_REM_ERR_SUCCESS) { + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg); + msg[n++] = status; + msg[n++] = scan ? scan->state : 0; + msg[n++] = scan ? scan->scanned_limit : + PB_REMOTE_MAX_SCAN_QUEUE_SIZE; + msg[n++] = scan ? scan->to_secs : 0; + break; + } + + if (!scan) + scan = l_new(struct rem_scan_data, 1); + + rpb_scan = scan; + + if (size == 18) { + memcpy(scan->uuid, pkt + 2, 16); + scan->fltr = true; + scan->state = 0x02; /* Limited */ + } else { + memset(scan->uuid, 0, 16); + scan->fltr = false; + scan->state = 0x01; /* Unlimited */ + } + + scan->client = src; + scan->net_idx = net_idx; + scan->node = node; + + if (!scan->list) + scan->list = l_new(uint8_t, + 23 * PB_REMOTE_MAX_SCAN_QUEUE_SIZE); + + mesh_io_register_recv_cb(NULL, prvb, 2, scan_pkt, scan); + + scan->to_secs = pkt[1]; + + if (pkt[0]) + scan->scanned_limit = pkt[0]; + else + scan->scanned_limit = PB_REMOTE_MAX_SCAN_QUEUE_SIZE; + + scan->timeout = l_timeout_create(pkt[1], + remprv_scan_cancel, scan, NULL); + + /* fallthrough */ + + case OP_REM_PROV_SCAN_GET: + /* Compose Scan Status */ + n = mesh_model_opcode_set(OP_REM_PROV_SCAN_STATUS, msg); + msg[n++] = PB_REM_ERR_SUCCESS; + msg[n++] = scan ? scan->state : 0; + msg[n++] = scan ? scan->scanned_limit : + PB_REMOTE_MAX_SCAN_QUEUE_SIZE; + msg[n++] = scan ? scan->to_secs : 0; + break; + + case OP_REM_PROV_SCAN_STOP: + if (size != 0 || !scan) + return true; + + remprv_scan_cancel(NULL, scan); + return true; + + case OP_REM_PROV_LINK_GET: + if (size != 0 || !prov) + return true; + + send_prov_status(prov, PB_REM_ERR_SUCCESS); + return true; + + case OP_REM_PROV_LINK_OPEN: + /* Sanity check args */ + if (size != 16 && size != 17 && size != 1) + return true; + + if (size == 17 && (pkt[16] == 0 || pkt[16] > 0x3c)) + return true; + + if (size == 1 && pkt[0] > 0x02) + return true; + + if (prov) { + if (prov->client != src || prov->node != node || + (size == 1 && prov->nppi_proc != pkt[0]) || + (size >= 16 && (prov->nppi_proc != RPR_ADV || + memcmp(prov->u.adv.uuid, pkt, 16)))) { + + /* Send Reject (in progress) */ + send_prov_status(prov, PB_REM_ERR_CANNOT_OPEN); + n = mesh_model_opcode_set( + OP_REM_PROV_LINK_STATUS, msg); + msg[n++] = PB_REM_ERR_CANNOT_OPEN; + msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; + break; + } + + /* Send redundant Success */ + send_prov_status(prov, PB_REM_ERR_SUCCESS); + return true; + } + + if (scan && scan->client != src && scan->node != node) { + n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); + msg[n++] = PB_REM_ERR_CANNOT_OPEN; + msg[n++] = PB_REMOTE_STATE_LINK_ACTIVE; + break; + } + + print_packet("Remote Prov Link Open", pkt, size); + + remprv_scan_cancel(NULL, scan); + + rpb_prov = prov = l_new(struct rem_prov_data, 1); + prov->client = src; + prov->net_idx = net_idx; + prov->node = node; + prov->state = PB_REMOTE_STATE_LINK_OPENING; + + if (size == 1) { + status = start_dev_key_refresh(node, pkt[0], prov); + + } else { + if (size == 17) + prov->timeout = l_timeout_create(pkt[16], + remprv_prov_cancel, prov, NULL); + + + prov->nppi_proc = RPR_ADV; + memcpy(prov->u.adv.uuid, pkt, 16); + status = pb_adv_reg(true, srv_open, srv_close, srv_rx, + srv_ack, pkt, prov); + } + + if (status) + send_prov_status(prov, PB_REM_ERR_SUCCESS); + else { + n = mesh_model_opcode_set(OP_REM_PROV_LINK_STATUS, msg); + msg[n++] = PB_REM_ERR_CANNOT_OPEN; + msg[n++] = PB_REMOTE_STATE_IDLE; + remprv_prov_cancel(NULL, prov); + } + + return true; + + case OP_REM_PROV_LINK_CLOSE: + if (size != 1) + return true; + + if (!prov || prov->node != node || prov->client != src) + return true; + + prov->state = PB_REMOTE_STATE_LINK_CLOSING; + mesh_io_send_cancel(NULL, &pkt_filter, sizeof(pkt_filter)); + send_prov_status(prov, PB_REM_ERR_SUCCESS); + if (pkt[0] == 0x02) { + msg[0] = PROV_FAILED; + msg[1] = PROV_ERR_CANT_ASSIGN_ADDR; + if (prov->nppi_proc == RPR_ADV) + prov->u.adv.tx(prov->trans_data, msg, 2); + else + prov->u.nppi.rx_cb(prov->trans_data, msg, 2); + } + + if (prov->nppi_proc == RPR_ADV) + pb_adv_unreg(prov); + + else if (prov->nppi_proc <= RPR_COMP) { + /* Hard or Soft refresh of local node, based on NPPI */ + node_refresh(prov->node, (prov->nppi_proc == RPR_ADDR), + &prov->u.nppi.info); + } + + remprv_prov_cancel(NULL, prov); + + return true; + + case OP_REM_PROV_PDU_SEND: + if (!prov || prov->node != node || prov->client != src) + return true; + + if (size < 2) + return true; + + + prov->cli_pdu_num = *pkt++; + size--; + prov->state = PB_REMOTE_STATE_OB_PKT_TX; + + if (prov->nppi_proc == RPR_ADV) + prov->u.adv.tx(prov->trans_data, pkt, size); + else { + srv_ack(prov, prov->cli_pdu_num); + prov->u.nppi.rx_cb(prov->trans_data, pkt, size); + } + + return true; + } + +send_pkt: + l_info("PB-SVR: src %4.4x dst %4.4x", unicast, src); + print_packet("App Tx", msg, n); + mesh_model_send(node, 0, src, APP_IDX_DEV_LOCAL, + net_idx, DEFAULT_TTL, segmented, n, msg); + + return true; +} + +static void remprv_srv_unregister(void *user_data) +{ +} + +static const struct mesh_model_ops ops = { + .unregister = remprv_srv_unregister, + .recv = remprv_srv_pkt, + .bind = NULL, + .sub = NULL, + .pub = NULL +}; + +void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx) +{ + mesh_model_register(node, ele_idx, REM_PROV_SRV_MODEL, &ops, node); +} diff --git a/mesh/remprv.h b/mesh/remprv.h new file mode 100644 index 000000000..49b4e2c7c --- /dev/null +++ b/mesh/remprv.h @@ -0,0 +1,78 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#define REM_PROV_SRV_MODEL SET_ID(SIG_VENDOR, 0x0004) +#define REM_PROV_CLI_MODEL SET_ID(SIG_VENDOR, 0x0005) + +#define PB_REMOTE_MAX_SCAN_QUEUE_SIZE 5 + +#define PB_REMOTE_STATE_IDLE 0x00 +#define PB_REMOTE_STATE_LINK_OPENING 0x01 +#define PB_REMOTE_STATE_LINK_ACTIVE 0x02 +#define PB_REMOTE_STATE_OB_PKT_TX 0x03 +#define PB_REMOTE_STATE_LINK_CLOSING 0x04 + +#define PB_REMOTE_TYPE_LOCAL 0x01 +#define PB_REMOTE_TYPE_ADV 0x02 +#define PB_REMOTE_TYPE_GATT 0x04 + +#define PB_REMOTE_SCAN_TYPE_NONE 0x00 +#define PB_REMOTE_SCAN_TYPE_UNLIMITED 0x01 +#define PB_REMOTE_SCAN_TYPE_LIMITED 0x02 +#define PB_REMOTE_SCAN_TYPE_DETAILED 0x03 + +/* Remote Provisioning Opcode List */ +#define OP_REM_PROV_SCAN_CAP_GET 0x804F +#define OP_REM_PROV_SCAN_CAP_STATUS 0x8050 +#define OP_REM_PROV_SCAN_GET 0x8051 +#define OP_REM_PROV_SCAN_START 0x8052 +#define OP_REM_PROV_SCAN_STOP 0x8053 +#define OP_REM_PROV_SCAN_STATUS 0x8054 +#define OP_REM_PROV_SCAN_REPORT 0x8055 +#define OP_REM_PROV_EXT_SCAN_START 0x8056 +#define OP_REM_PROV_EXT_SCAN_REPORT 0x8057 +#define OP_REM_PROV_LINK_GET 0x8058 +#define OP_REM_PROV_LINK_OPEN 0x8059 +#define OP_REM_PROV_LINK_CLOSE 0x805A +#define OP_REM_PROV_LINK_STATUS 0x805B +#define OP_REM_PROV_LINK_REPORT 0x805C +#define OP_REM_PROV_PDU_SEND 0x805D +#define OP_REM_PROV_PDU_OB_REPORT 0x805E +#define OP_REM_PROV_PDU_REPORT 0x805F + +/* Remote Provisioning Errors */ +#define PB_REM_ERR_SUCCESS 0x00 +#define PB_REM_ERR_SCANNING_CANNOT_START 0x01 +#define PB_REM_ERR_INVALID_STATE 0x02 +#define PB_REM_ERR_LIMITED_RESOURCES 0x03 +#define PB_REM_ERR_CANNOT_OPEN 0x04 +#define PB_REM_ERR_OPEN_FAILED 0x05 +#define PB_REM_ERR_CLOSED_BY_DEVICE 0x06 +#define PB_REM_ERR_CLOSED_BY_SERVER 0x07 +#define PB_REM_ERR_CLOSED_BY_CLIENT 0x08 +#define PB_REM_ERR_CLOSED_CANNOT_RX_PDU 0x09 +#define PB_REM_ERR_CLOSED_CANNOT_TX_PDU 0x0A + +void remote_prov_server_init(struct mesh_node *node, uint8_t ele_idx); +void remote_prov_client_init(struct mesh_node *node, uint8_t ele_idx); +bool register_nppi_acceptor(mesh_prov_open_func_t open_cb, + mesh_prov_close_func_t close_cb, + mesh_prov_receive_func_t rx_cb, + mesh_prov_ack_func_t ack_cb, + void *user_data); From patchwork Mon Jan 23 19:48:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112868 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 19EB8C05027 for ; Mon, 23 Jan 2023 19:49:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232934AbjAWTtH (ORCPT ); Mon, 23 Jan 2023 14:49:07 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53722 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232954AbjAWTs7 (ORCPT ); Mon, 23 Jan 2023 14:48:59 -0500 Received: from mail-pj1-x102f.google.com (mail-pj1-x102f.google.com [IPv6:2607:f8b0:4864:20::102f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83DFD35267 for ; Mon, 23 Jan 2023 11:48:46 -0800 (PST) Received: by mail-pj1-x102f.google.com with SMTP id o13so12699427pjg.2 for ; Mon, 23 Jan 2023 11:48:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Jgg095BP9CvRZQPCBsSZJQvpKkFnYIbeudLqnD2y42A=; b=VzDucwm9eVrd3wk6Af9BsPjoGsyrlycjp/6jC8/ZhX63oYmZUwTnxhDlGc1EBXFq9I 61Ae2RKFNtQRSoBGKyyAc7N9vGvIoHacouIJa8gaLJDfRN7JBEnw9PsKSTewujRop0Jp VlsCzIXfyCGZ549ubOF8A6Hp+MIiGFJ6mtOx+CwX+aJiw72zu8IIJQJjpa8soj2Uti25 neSYvK4USOcvrOiu1XNVE6yXVtiKZuV/2wLcBktR93kTWfpa8twuC2ByK6SxtfyX+UgV peRiTH4PjAb33iFi3S6T46uYQWQmBcWiJXY9Wecy3wuzjaMSJNQEnNJC5zdHShlB/isj yEOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Jgg095BP9CvRZQPCBsSZJQvpKkFnYIbeudLqnD2y42A=; b=uvuX5TIwfGSOZ2LDAGAKj5Iy98AVUH3+x598gOeU+iGD3RmAbBYZluX8L6YtxW5N1C sIDHCabr8vgUBvLzkfsvqKZmY2fB7Qe+oQ3XzYHh9ESTzLgMx/ibhQGW7FxxjGE0Q0GS c+NESgzEfqZvctgMjnOjjxI5EXlSNDdFrIX2JFBVhs6gpClrApxYQRrXLtlCiEgirKvo KbwBXTwBYVmX45p2zaLqYoWgEyG+Mr/a16DhM1MZVYtyFkYWAOaOeTw4fEh54zKAl/it 0vbO3Xax2kOM6ejTCwk8QKuiXaRVvSr/mAh5+Le2bxRH1KduVEd6zBwvzUXoemZoJm6V NXVQ== X-Gm-Message-State: AFqh2ko4BBg4dTw5papR9L+Ab6TGTzjPNsXZK3aXzy57okFimgyiHVZk Tg5lCx9jJU3dnIAgD7kde5+n5MlB8DxuAw== X-Google-Smtp-Source: AMrXdXvasWgbmYofjGx64kmX0EgP858q4+1/v62QbhfYno8lXALeV9Fe2S7F83GKto7fgiNd4M8fXA== X-Received: by 2002:a17:902:cec3:b0:194:d9b5:3bf0 with SMTP id d3-20020a170902cec300b00194d9b53bf0mr20235091plg.18.1674503325379; Mon, 23 Jan 2023 11:48:45 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:45 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 03/11] tools/mesh: Optimize for multiple RPR servers and NPPI Date: Mon, 23 Jan 2023 11:48:16 -0800 Message-Id: <20230123194824.257351-4-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix These changes allow the mesh-cfgclient tool to request remote node compositions from page 128. Depending on the differences between there and what is stored in the local configuration database, it may recomend reprovisioning with NPPI-1 (Address Refresh) or NPPI-2 (Composition Refresh). Additionally, NPPI-0 may be performed to refresh the Device Key only. --- tools/mesh-cfgclient.c | 479 ++++++++++++++++++++++++++++++++++------- tools/mesh/cfgcli.c | 37 +++- tools/mesh/mesh-db.c | 37 +++- tools/mesh/mesh-db.h | 1 + tools/mesh/remote.c | 122 +++++++++++ tools/mesh/remote.h | 9 + 6 files changed, 598 insertions(+), 87 deletions(-) diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c index 237afbb5f..f3e9af8fb 100644 --- a/tools/mesh-cfgclient.c +++ b/tools/mesh-cfgclient.c @@ -43,6 +43,7 @@ #define CFG_SRV_MODEL 0x0000 #define CFG_CLI_MODEL 0x0001 +#define RPR_SVR_MODEL 0xFFFF0004 #define UNPROV_SCAN_MAX_SECS 300 @@ -83,8 +84,12 @@ struct meshcfg_node { struct unprov_device { time_t last_seen; - int16_t rssi; + int id; + uint32_t uri_hash; uint8_t uuid[16]; + int16_t rssi; + uint16_t server; + uint16_t oob_info; }; struct generic_request { @@ -96,8 +101,16 @@ struct generic_request { const char *str; }; +struct scan_data { + uint16_t dst; + uint16_t secs; +}; + +static void *finalized = L_UINT_TO_PTR(-1); + static struct l_dbus *dbus; +static struct l_timeout *scan_timeout; static struct l_queue *node_proxies; static struct l_dbus_proxy *net_proxy; static struct meshcfg_node *local; @@ -197,23 +210,57 @@ static bool parse_argument_on_off(int argc, char *argv[], bool *value) static bool match_device_uuid(const void *a, const void *b) { const struct unprov_device *dev = a; - const uint8_t *uuid = b; - return (memcmp(dev->uuid, uuid, 16) == 0); + if (a == finalized) + return false; + + return memcmp(dev->uuid, b, 16) == 0; } -static void print_device(void *a, void *b) +static bool match_by_id(const void *a, const void *b) { const struct unprov_device *dev = a; - struct tm *tm = localtime(&dev->last_seen); + int id = L_PTR_TO_UINT(b); + + if (a == finalized) + return false; + + l_info("test %d %d", dev->id, id); + return dev->id == id; +} + +static bool match_by_srv_uuid(const void *a, const void *b) +{ + const struct unprov_device *dev = a; + const struct unprov_device *new_dev = b; + + if (a == finalized) + return false; + + return (dev->server == new_dev->server) && + (memcmp(dev->uuid, new_dev->uuid, 16) == 0); +} + +static void print_device(void *a, void *b) +{ + struct unprov_device *dev = a; + int *cnt = b; + struct tm *tm; char buf[80]; char *str; + if (a == finalized) + return; + + tm = localtime(&dev->last_seen); assert(strftime(buf, sizeof(buf), "%c", tm)); + (*cnt)++; + dev->id = *cnt; str = l_util_hexstring_upper(dev->uuid, sizeof(dev->uuid)); - bt_shell_printf("UUID: %s, RSSI %d, Seen: %s\n", - str, dev->rssi, buf); + bt_shell_printf(COLOR_YELLOW "#%d" COLOR_OFF + " UUID: %s, RSSI %d, Server: %4.4x\n Seen: %s\n", + *cnt, str, dev->rssi, dev->server, buf); l_free(str); } @@ -794,15 +841,56 @@ static void scan_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *msg, static void scan_setup(struct l_dbus_message *msg, void *user_data) { - uint16_t secs = (uint16_t) L_PTR_TO_UINT(user_data); + struct scan_data *data = user_data; struct l_dbus_message_builder *builder; builder = l_dbus_message_builder_new(msg); l_dbus_message_builder_enter_array(builder, "{sv}"); - append_dict_entry_basic(builder, "Seconds", "q", &secs); + append_dict_entry_basic(builder, "Seconds", "q", &data->secs); + append_dict_entry_basic(builder, "Server", "q", &data->dst); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); + + /* Destination info not needed after call */ + l_free(data); +} + +static void scan_start(void *user_data, uint16_t dst, uint32_t model) +{ + struct scan_data *data; + + if (model != RPR_SVR_MODEL) + return; + + data = l_malloc(sizeof(struct scan_data)); + data->secs = L_PTR_TO_UINT(user_data); + data->dst = dst; + + if (!l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan", + scan_setup, scan_reply, data, NULL)) + l_free(data); +} + +static void scan_to(struct l_timeout *timeout, void *user_data) +{ + int cnt = 0; + + if (l_queue_peek_head(devices) != finalized) + l_queue_push_head(devices, finalized); + + l_timeout_remove(timeout); + scan_timeout = NULL; + bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF); + l_queue_foreach(devices, print_device, &cnt); +} + +static void free_devices(void *a) +{ + if (a == finalized) + return; + + l_free(a); } static void cmd_scan_unprov(int argc, char *argv[]) @@ -820,21 +908,28 @@ static void cmd_scan_unprov(int argc, char *argv[]) return bt_shell_noninteractive_quit(EXIT_FAILURE); } - if (argc == 3) + if (argc == 3) { sscanf(argv[2], "%u", &secs); - if (secs > UNPROV_SCAN_MAX_SECS) - secs = UNPROV_SCAN_MAX_SECS; + if (secs > UNPROV_SCAN_MAX_SECS) + secs = UNPROV_SCAN_MAX_SECS; + } else + secs = 60; - if (enable) - l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan", - scan_setup, scan_reply, - L_UINT_TO_PTR(secs), NULL); - else + l_timeout_remove(scan_timeout); + scan_timeout = NULL; + + if (enable) { + l_queue_clear(devices, free_devices); + remote_foreach_model(scan_start, L_UINT_TO_PTR(secs)); + scan_timeout = l_timeout_create(secs, scan_to, NULL, NULL); + } else { + /* Mark devices queue as finalized */ + l_queue_push_head(devices, finalized); l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScanCancel", NULL, NULL, NULL, NULL); - + } } static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id, @@ -1030,8 +1125,10 @@ static void cmd_export_db(int argc, char *argv[]) static void cmd_list_unprov(int argc, char *argv[]) { + int cnt = 0; + bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF); - l_queue_foreach(devices, print_device, NULL); + l_queue_foreach(devices, print_device, &cnt); } static void cmd_list_nodes(int argc, char *argv[]) @@ -1505,32 +1602,56 @@ static void add_node_reply(struct l_dbus_proxy *proxy, bt_shell_printf("Provisioning started\n"); } -static void add_node_setup(struct l_dbus_message *msg, void *user_data) +static void reprov_reply(struct l_dbus_proxy *proxy, + struct l_dbus_message *msg, void *user_data) { - char *str = user_data; - size_t sz; - unsigned char *uuid; - struct l_dbus_message_builder *builder; + if (l_dbus_message_is_error(msg)) { + const char *name; - uuid = l_util_from_hexstring(str, &sz); - if (!uuid || sz != 16 || !l_uuid_is_valid(uuid)) { - l_error("Failed to generate UUID array from %s", str); + prov_in_progress = false; + l_dbus_message_get_error(msg, &name, NULL); + l_error("Failed to start provisioning: %s", name); return; } + bt_shell_printf("Reprovisioning started\n"); +} + +static void reprovision_setup(struct l_dbus_message *msg, void *user_data) +{ + uint16_t target = L_PTR_TO_UINT(user_data); + uint8_t nppi = L_PTR_TO_UINT(user_data) >> 16; + struct l_dbus_message_builder *builder; + builder = l_dbus_message_builder_new(msg); - append_byte_array(builder, uuid, 16); + l_dbus_message_builder_append_basic(builder, 'q', &target); l_dbus_message_builder_enter_array(builder, "{sv}"); /* TODO: populate with options when defined */ + append_dict_entry_basic(builder, "NPPI", "y", &nppi); l_dbus_message_builder_leave_array(builder); l_dbus_message_builder_finalize(builder); l_dbus_message_builder_destroy(builder); +} - l_free(uuid); +static void add_node_setup(struct l_dbus_message *msg, void *user_data) +{ + struct unprov_device *dev = user_data; + struct l_dbus_message_builder *builder; + + builder = l_dbus_message_builder_new(msg); + append_byte_array(builder, dev->uuid, 16); + l_dbus_message_builder_enter_array(builder, "{sv}"); + /* TODO: populate with options when defined */ + l_dbus_message_builder_leave_array(builder); + l_dbus_message_builder_finalize(builder); + l_dbus_message_builder_destroy(builder); } static void cmd_start_prov(int argc, char *argv[]) { + struct unprov_device *dev = NULL; + int id; + if (!local || !local->proxy || !local->mgmt_proxy) { bt_shell_printf("Node is not attached\n"); return; @@ -1541,14 +1662,96 @@ static void cmd_start_prov(int argc, char *argv[]) return; } - if (!argv[1] || (strlen(argv[1]) != 32)) { + if (!argv[1]) { + bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED); + return; + } + + if (*(argv[1]) == '#') { + if (sscanf(argv[1] + 1, "%d", &id) == 1) + dev = l_queue_find(devices, match_by_id, + L_UINT_TO_PTR(id)); + + if (!dev) { + bt_shell_printf(COLOR_RED "unknown id\n" COLOR_RED); + return; + } + } else if (strlen(argv[1]) == 32) { + size_t sz; + uint8_t *uuid = l_util_from_hexstring(argv[1], &sz); + + if (sz != 16) { + bt_shell_printf(COLOR_RED "Invalid UUID\n" COLOR_RED); + return; + } + + dev = l_queue_find(devices, match_device_uuid, uuid); + + if (!dev) { + dev = l_new(struct unprov_device, 1); + memcpy(dev->uuid, uuid, 16); + l_queue_push_tail(devices, dev); + } + + l_free(uuid); + + } else { bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED); return; } if (l_dbus_proxy_method_call(local->mgmt_proxy, "AddNode", add_node_setup, add_node_reply, - argv[1], NULL)) + dev, NULL)) + prov_in_progress = true; +} + +static void cmd_start_reprov(int argc, char *argv[]) +{ + uint16_t target = 0; + uint8_t nppi = 0; + + if (!local || !local->proxy || !local->mgmt_proxy) { + bt_shell_printf("Node is not attached\n"); + return; + } + + if (prov_in_progress) { + bt_shell_printf("Provisioning is already in progress\n"); + return; + } + + if (!argv[1]) { + bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED); + return; + } + + if (argv[2]) { + char *end; + + nppi = strtol(argv[2], &end, 16); + } + + if (strlen(argv[1]) == 4) { + char *end; + + target = strtol(argv[1], &end, 16); + + if (end != (argv[1] + 4)) { + bt_shell_printf(COLOR_RED "Invalid Unicast\n" + COLOR_RED); + return; + } + + } else { + bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED); + return; + } + + if (l_dbus_proxy_method_call(local->mgmt_proxy, "Reprovision", + reprovision_setup, reprov_reply, + L_UINT_TO_PTR(target + (nppi << 16)), + NULL)) prov_in_progress = true; } @@ -1581,6 +1784,8 @@ static const struct bt_shell_menu main_menu = { "List unprovisioned devices" }, { "provision", "", cmd_start_prov, "Initiate provisioning"}, + { "reprovision", " [0|1|2]", cmd_start_reprov, + "Refresh Device Key"}, { "node-import", " ", cmd_import_node, "Import an externally provisioned remote node"}, @@ -1758,18 +1963,34 @@ static void setup_ele_iface(struct l_dbus_interface *iface) /* TODO: Other methods */ } +static int sort_rssi(const void *a, const void *b, void *user_data) +{ + const struct unprov_device *new_dev = a; + const struct unprov_device *dev = b; + + if (b == finalized) + return 1; + + return dev->rssi - new_dev->rssi; +} + static struct l_dbus_message *scan_result_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { - struct l_dbus_message_iter iter, opts; + struct l_dbus_message_iter iter, opts, var; + struct unprov_device result, *dev; int16_t rssi; + uint16_t server = 0; uint32_t n; uint8_t *prov_data; - char *str; - struct unprov_device *dev; + const char *key; const char *sig = "naya{sv}"; + if (finalized == l_queue_peek_head(devices)) + goto done; + + if (!l_dbus_message_get_arguments(msg, sig, &rssi, &iter, &opts)) { l_error("Cannot parse scan results"); return l_dbus_message_new_error(msg, dbus_err_args, NULL); @@ -1781,42 +2002,72 @@ static struct l_dbus_message *scan_result_call(struct l_dbus *dbus, return l_dbus_message_new_error(msg, dbus_err_args, NULL); } - bt_shell_printf("Scan result:\n"); - bt_shell_printf("\t" COLOR_GREEN "rssi = %d\n" COLOR_OFF, rssi); - str = l_util_hexstring_upper(prov_data, 16); - bt_shell_printf("\t" COLOR_GREEN "UUID = %s\n" COLOR_OFF, str); - l_free(str); - - if (n >= 18) { - str = l_util_hexstring_upper(prov_data + 16, 2); - bt_shell_printf("\t" COLOR_GREEN "OOB = %s\n" COLOR_OFF, str); - l_free(str); + while (l_dbus_message_iter_next_entry(&opts, &key, &var)) { + if (!strcmp(key, "Server")) + l_dbus_message_iter_get_variant(&var, "q", &server); } - if (n >= 22) { - str = l_util_hexstring_upper(prov_data + 18, 4); - bt_shell_printf("\t" COLOR_GREEN "URI Hash = %s\n" COLOR_OFF, - str); - l_free(str); - } + memcpy(result.uuid, prov_data, 16); + result.server = server; + result.rssi = rssi; + result.id = 0; - /* TODO: Handle the rest of provisioning data if present */ + if (n > 16 && n <= 18) + result.oob_info = l_get_be16(prov_data + 16); + else + result.oob_info = 0; + + if (n > 18 && n <= 22) + result.uri_hash = l_get_be32(prov_data + 18); + else + result.uri_hash = 0; + + dev = l_queue_remove_if(devices, match_by_srv_uuid, &result); - dev = l_queue_find(devices, match_device_uuid, prov_data); if (!dev) { - dev = l_new(struct unprov_device, 1); - memcpy(dev->uuid, prov_data, sizeof(dev->uuid)); - /* TODO: timed self-destructor */ - l_queue_push_tail(devices, dev); - } + bt_shell_printf("\r" COLOR_YELLOW "Results = %d\n" COLOR_OFF, + l_queue_length(devices) + 1); + dev = l_malloc(sizeof(struct unprov_device)); + *dev = result; + + } else if (dev->rssi < result.rssi) + *dev = result; - /* Update with the latest rssi */ - dev->rssi = rssi; dev->last_seen = time(NULL); + l_queue_insert(devices, dev, sort_rssi, NULL); + +done: return l_dbus_message_new_method_return(msg); } +static struct l_dbus_message *req_reprov_call(struct l_dbus *dbus, + struct l_dbus_message *msg, + void *user_data) +{ + uint8_t cnt; + uint16_t unicast, original; + struct l_dbus_message *reply; + + + if (!l_dbus_message_get_arguments(msg, "qy", &original, &cnt) || + !IS_UNICAST(original)) { + l_error("Cannot parse request for reprov data"); + return l_dbus_message_new_error(msg, dbus_err_args, NULL); + + } + + unicast = remote_get_next_unicast(low_addr, high_addr, cnt); + + bt_shell_printf("Assign addresses for %u elements\n", cnt); + bt_shell_printf("Original: %4.4x New: %4.4x\n", original, unicast); + + reply = l_dbus_message_new_method_return(msg); + l_dbus_message_set_arguments(reply, "q", unicast); + + return reply; +} + static struct l_dbus_message *req_prov_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) @@ -1825,6 +2076,7 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus, uint16_t unicast; struct l_dbus_message *reply; + /* Both calls handled identicaly except for parameter list */ if (!l_dbus_message_get_arguments(msg, "y", &cnt)) { l_error("Cannot parse request for prov data"); return l_dbus_message_new_error(msg, dbus_err_args, NULL); @@ -1833,14 +2085,14 @@ static struct l_dbus_message *req_prov_call(struct l_dbus *dbus, unicast = remote_get_next_unicast(low_addr, high_addr, cnt); - if (unicast == 0) { + if (!IS_UNICAST(unicast)) { l_error("Failed to allocate addresses for %u elements\n", cnt); return l_dbus_message_new_error(msg, "org.freedesktop.DBus.Error." "Failed to allocate address", NULL); } - bt_shell_printf("Assign addresses for %u elements\n", cnt); + bt_shell_printf("Assign addresses: %4.4x (cnt: %d)\n", unicast, cnt); reply = l_dbus_message_new_method_return(msg); l_dbus_message_set_arguments(reply, "qq", prov_net_idx, unicast); @@ -1852,11 +2104,13 @@ static void remove_device(uint8_t *uuid) { struct unprov_device *dev; - dev = l_queue_remove_if(devices, match_device_uuid, uuid); - l_free(dev); + do { + dev = l_queue_remove_if(devices, match_device_uuid, uuid); + l_free(dev); + } while (dev); } -static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus, +static struct l_dbus_message *prov_cmplt_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { @@ -1866,6 +2120,7 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus, uint32_t n; uint8_t *uuid; + l_debug("ProvComplete"); if (!prov_in_progress) return l_dbus_message_new_error(msg, dbus_err_fail, NULL); @@ -1896,7 +2151,49 @@ static struct l_dbus_message *add_node_cmplt_call(struct l_dbus *dbus, return l_dbus_message_new_method_return(msg); } -static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus, +static struct l_dbus_message *reprov_cmplt_call(struct l_dbus *dbus, + struct l_dbus_message *msg, + void *user_data) +{ + uint16_t unicast, original; + uint8_t old_cnt, cnt, nppi; + + l_debug("ReprovComplete"); + if (!prov_in_progress) + return l_dbus_message_new_error(msg, dbus_err_fail, NULL); + + prov_in_progress = false; + + if (!l_dbus_message_get_arguments(msg, "qyqy", &original, &nppi, + &unicast, &cnt)) { + l_error("Cannot parse reprov complete message"); + return l_dbus_message_new_error(msg, dbus_err_args, NULL); + + } + + l_debug("ReprovComplete org: %4.4x, nppi: %d, new: %4.4x, cnt: %d", + original, nppi, unicast, cnt); + old_cnt = remote_ele_cnt(original); + + if (nppi != 1 && (original != unicast || cnt != old_cnt)) { + l_error("Invalid reprov complete message (NPPI == %d)", nppi); + return l_dbus_message_new_error(msg, dbus_err_args, NULL); + } + + if (nppi) + remote_reset_node(original, unicast, cnt, + mesh_db_get_iv_index()); + + bt_shell_printf("Reprovisioning done (nppi: %d):\n", nppi); + remote_print_node(unicast); + + if (!mesh_db_reset_node(original, unicast, cnt)) + l_error("Failed to reset remote node"); + + return l_dbus_message_new_method_return(msg); +} + +static struct l_dbus_message *prov_fail_call(struct l_dbus *dbus, struct l_dbus_message *msg, void *user_data) { @@ -1911,24 +2208,49 @@ static struct l_dbus_message *add_node_fail_call(struct l_dbus *dbus, prov_in_progress = false; if (!l_dbus_message_get_arguments(msg, "ays", &iter, &reason)) { - l_error("Cannot parse add node failed message"); + l_error("Cannot parse failed message"); return l_dbus_message_new_error(msg, dbus_err_args, NULL); - } - if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) || - n != 16) { - l_error("Cannot parse add node failed message: uuid"); + if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) || n != 16) { + l_error("Cannot parse failed message: uuid"); return l_dbus_message_new_error(msg, dbus_err_args, NULL); } bt_shell_printf("Provisioning failed:\n"); + str = l_util_hexstring_upper(uuid, 16); bt_shell_printf("\t" COLOR_RED "UUID = %s\n" COLOR_OFF, str); l_free(str); + remove_device(uuid); bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason); - remove_device(uuid); + return l_dbus_message_new_method_return(msg); +} + +static struct l_dbus_message *reprov_fail_call(struct l_dbus *dbus, + struct l_dbus_message *msg, + void *user_data) +{ + struct l_dbus_message_iter iter; + uint16_t original = UNASSIGNED_ADDRESS; + char *reason; + + if (!prov_in_progress) + return l_dbus_message_new_error(msg, dbus_err_fail, NULL); + + prov_in_progress = false; + + if (!l_dbus_message_get_arguments(msg, "qs", &iter, &reason) || + !IS_UNICAST(original)) { + + l_error("Cannot parse Reprov failed message"); + return l_dbus_message_new_error(msg, dbus_err_args, NULL); + } + + bt_shell_printf("Reprovisioning failed:\n"); + bt_shell_printf("\t" COLOR_RED "UNICAST = %4.4x\n" COLOR_OFF, original); + bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason); return l_dbus_message_new_method_return(msg); } @@ -1941,12 +2263,23 @@ static void setup_prov_iface(struct l_dbus_interface *iface) l_dbus_interface_method(iface, "RequestProvData", 0, req_prov_call, "qq", "y", "net_index", "unicast", "count"); + l_dbus_interface_method(iface, "RequestReprovData", 0, req_reprov_call, + "q", "qy", "unicast", + "original", "count"); + l_dbus_interface_method(iface, "AddNodeComplete", 0, - add_node_cmplt_call, "", "ayqy", + prov_cmplt_call, "", "ayqy", "uuid", "unicast", "count"); - l_dbus_interface_method(iface, "AddNodeFailed", 0, add_node_fail_call, + l_dbus_interface_method(iface, "ReprovComplete", 0, + reprov_cmplt_call, "", "qyqy", + "original", "nppi", "unicast", "count"); + + l_dbus_interface_method(iface, "AddNodeFailed", 0, prov_fail_call, "", "ays", "uuid", "reason"); + + l_dbus_interface_method(iface, "ReprovFailed", 0, reprov_fail_call, + "", "qs", "unicast", "reason"); } static bool cid_getter(struct l_dbus *dbus, diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c index a48eace74..ad572f694 100644 --- a/tools/mesh/cfgcli.c +++ b/tools/mesh/cfgcli.c @@ -266,14 +266,21 @@ static uint32_t print_mod_id(uint8_t *data, bool vendor, const char *offset) return mod_id; } -static void print_composition(uint8_t *data, uint16_t len) +static uint8_t print_composition(uint8_t *data, uint16_t len) { uint16_t features; int i = 0; + bool nppi = false; - bt_shell_printf("Received composion:\n"); + bt_shell_printf("Received composition:\n"); + + /* We only support Pages 0 && 128 */ + if (*data == 128) { + bt_shell_printf("Dev Key Refresh (NPPI) required\n"); + nppi = true; + } else if (*data != 0) + return 0; - /* skip page -- We only support Page Zero */ data++; len--; @@ -328,6 +335,11 @@ static void print_composition(uint8_t *data, uint16_t len) i++; } + + if (nppi) + return (uint8_t) i; + else + return 0; } static void print_pub(uint16_t ele_addr, uint32_t mod_id, @@ -402,6 +414,7 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data, const struct cfg_cmd *cmd; uint16_t app_idx, net_idx, addr, ele_addr, features; struct mesh_group *grp; + uint8_t page128_cnt; struct model_pub pub; int n; struct pending_req *req; @@ -431,7 +444,19 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data, if (len < MIN_COMPOSITION_LEN) return true; - print_composition(data, len); + page128_cnt = print_composition(data, len); + if (page128_cnt) { + if (page128_cnt != remote_ele_cnt(src)) { + bt_shell_printf("Ele count was %d, now %d\n", + remote_ele_cnt(src), page128_cnt); + bt_shell_printf("Reprovision with NPPI-1\n"); + } else { + bt_shell_printf("Models or Features changed\n"); + bt_shell_printf("Reprovision with NPPI-2\n"); + } + + break; + } saved = mesh_db_node_set_composition(src, data, len); if (saved) @@ -1051,8 +1076,8 @@ static void cmd_composition_get(int argc, char *argv[]) n = mesh_opcode_set(OP_DEV_COMP_GET, msg); - /* By default, use page 0 */ - msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0; + /* By default, use page 128 */ + msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 128; if (!config_send(msg, n, OP_DEV_COMP_GET)) return bt_shell_noninteractive_quit(EXIT_FAILURE); diff --git a/tools/mesh/mesh-db.c b/tools/mesh/mesh-db.c index 896ff722c..c0c05a29a 100644 --- a/tools/mesh/mesh-db.c +++ b/tools/mesh/mesh-db.c @@ -1702,6 +1702,29 @@ static json_object *init_elements(uint8_t num_els) return jelements; } +bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els) +{ + json_object *jnode, *jelements; + + if (!cfg || !cfg->jcfg) + return false; + + jnode = get_node_by_unicast(cfg->jcfg, original); + if (!jnode) { + l_error("Node %4.4x does not exist", original); + return false; + } + + if (!write_uint16_hex(jnode, "unicastAddress", unicast)) + return false; + + json_object_object_del(jnode, "elements"); + jelements = init_elements(num_els); + json_object_object_add(jnode, "elements", jelements); + + return save_config(); +} + bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast, uint16_t net_idx) { @@ -1864,13 +1887,11 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len) if (!jnode) return false; - /* skip page -- We only support Page Zero */ - data++; - len--; + /* This is for page-0 only */ + if (*data++ != 0) + return false; - /* If "crpl" property is present, composition is already recorded */ - if (json_object_object_get_ex(jnode, "crpl", &jobj)) - return true; + len--; if (!write_uint16_hex(jnode, "cid", l_get_le16(&data[0]))) return false; @@ -1954,7 +1975,6 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len) } while (len >= 4 && v--) { - jobj = json_object_new_object(); mod_id = l_get_le16(data + 2); mod_id = l_get_le16(data) << 16 | mod_id; @@ -1984,7 +2004,8 @@ bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len) fail: /* Reset elements array */ json_object_object_del(jnode, "elements"); - init_elements(sz); + jelements = init_elements(sz); + json_object_object_add(jnode, "elements", jelements); return false; } diff --git a/tools/mesh/mesh-db.h b/tools/mesh/mesh-db.h index 4b6b2adb3..0e45112b7 100644 --- a/tools/mesh/mesh-db.h +++ b/tools/mesh/mesh-db.h @@ -29,6 +29,7 @@ bool mesh_db_del_app_key(uint16_t app_idx); bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high); bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast, uint16_t net_idx); +bool mesh_db_reset_node(uint16_t original, uint16_t unicast, uint8_t num_els); bool mesh_db_del_node(uint16_t unicast); bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len); diff --git a/tools/mesh/remote.c b/tools/mesh/remote.c index 6ec220a6f..cee711dec 100644 --- a/tools/mesh/remote.c +++ b/tools/mesh/remote.c @@ -30,6 +30,12 @@ struct remote_key { bool updated; }; +struct foreach_data { + remote_foreach_t each; + void *user_data; + uint16_t dst; +}; + struct remote_node { uint16_t unicast; struct l_queue *net_keys; @@ -138,6 +144,40 @@ uint8_t remote_del_node(uint16_t unicast) return num_ele; } +bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt, + uint32_t iv_index) +{ + struct remote_node *rmt; + bool reject = true; + int i; + + rmt = l_queue_remove_if(nodes, match_node_addr, + L_UINT_TO_PTR(original)); + if (!rmt) + return false; + + if (unicast == rmt->unicast) + reject = false; + + for (i = 0; i < rmt->num_ele; ++i) { + l_queue_destroy(rmt->els[i], NULL); + if (reject) + remote_add_rejected_address(rmt->unicast + i, + iv_index, true); + } + + if (ele_cnt != rmt->num_ele) { + l_free(rmt->els); + rmt->els = l_new(struct l_queue *, ele_cnt); + } else + memset(rmt->els, 0, sizeof(struct l_queue *) * ele_cnt); + + rmt->unicast = unicast; + rmt->num_ele = ele_cnt; + l_queue_insert(nodes, rmt, compare_unicast, NULL); + return true; +} + bool remote_add_node(const uint8_t uuid[16], uint16_t unicast, uint8_t ele_cnt, uint16_t net_idx) { @@ -526,6 +566,76 @@ void remote_print_all(void) l_queue_foreach(nodes, print_node, NULL); } +static void each_node(void *rmt, void *user_data) +{ + struct remote_node *node = rmt; + struct foreach_data *data = user_data; + + data->each(data->user_data, node->unicast, (uint32_t) -1); +} + +static void each_addr(void *rmt, void *user_data) +{ + struct remote_node *node = rmt; + struct foreach_data *data = user_data; + uint16_t cnt; + + for (cnt = 0; cnt <= node->num_ele; cnt++) + data->each(data->user_data, node->unicast + cnt, (uint32_t) -1); +} + +static void parse_model(void *model, void *user_data) +{ + struct foreach_data *data = user_data; + + data->each(data->user_data, data->dst, L_PTR_TO_UINT(model)); +} + +static void each_model(void *rmt, void *user_data) +{ + struct remote_node *node = rmt; + struct foreach_data *data = user_data; + uint16_t cnt; + + for (cnt = 0; cnt < node->num_ele; cnt++) { + data->dst = node->unicast + cnt; + l_queue_foreach(node->els[cnt], parse_model, data); + } +} + +void remote_foreach(remote_foreach_t each, void *user_data) +{ + struct foreach_data data = { + .each = each, + .user_data = user_data + }; + + if (each) + l_queue_foreach(nodes, each_node, &data); +} + +void remote_foreach_unicast(remote_foreach_t each, void *user_data) +{ + struct foreach_data data = { + .each = each, + .user_data = user_data + }; + + if (each) + l_queue_foreach(nodes, each_addr, &data); +} + +void remote_foreach_model(remote_foreach_t each, void *user_data) +{ + struct foreach_data data = { + .each = each, + .user_data = user_data + }; + + if (each) + l_queue_foreach(nodes, each_model, &data); +} + uint16_t remote_get_next_unicast(uint16_t low, uint16_t high, uint8_t ele_cnt) { struct remote_node *rmt; @@ -598,3 +708,15 @@ void remote_clear_rejected_addresses(uint32_t iv_index) mesh_db_clear_rejected(iv_index); } + +uint8_t remote_ele_cnt(uint16_t unicast) +{ + struct remote_node *rmt; + + rmt = l_queue_find(nodes, match_node_addr, L_UINT_TO_PTR(unicast)); + + if (rmt) + return rmt->num_ele; + + return 0; +} diff --git a/tools/mesh/remote.h b/tools/mesh/remote.h index 66457237e..2a3947b58 100644 --- a/tools/mesh/remote.h +++ b/tools/mesh/remote.h @@ -8,8 +8,13 @@ * */ +typedef void (*remote_foreach_t)(void *user_data, uint16_t dst, + uint32_t model); + bool remote_add_node(const uint8_t uuid[16], uint16_t unicast, uint8_t ele_cnt, uint16_t net_idx); +bool remote_reset_node(uint16_t original, uint16_t unicast, uint8_t ele_cnt, + uint32_t iv_index); uint8_t remote_del_node(uint16_t unicast); bool remote_set_model(uint16_t unicast, uint8_t ele_idx, uint32_t mod_id, bool vendor); @@ -30,3 +35,7 @@ bool remote_has_composition(uint16_t addr); uint16_t remote_get_subnet_idx(uint16_t addr); void remote_print_node(uint16_t addr); void remote_print_all(void); +void remote_foreach(remote_foreach_t each, void *user_data); +void remote_foreach_unicast(remote_foreach_t each, void *user_data); +void remote_foreach_model(remote_foreach_t each, void *user_data); +uint8_t remote_ele_cnt(uint16_t unicast); From patchwork Mon Jan 23 19:48:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112866 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3362BC25B50 for ; Mon, 23 Jan 2023 19:49:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232933AbjAWTtF (ORCPT ); Mon, 23 Jan 2023 14:49:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232944AbjAWTs6 (ORCPT ); Mon, 23 Jan 2023 14:48:58 -0500 Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1CEF13250D for ; Mon, 23 Jan 2023 11:48:47 -0800 (PST) Received: by mail-pj1-x102a.google.com with SMTP id n20-20020a17090aab9400b00229ca6a4636so15429668pjq.0 for ; Mon, 23 Jan 2023 11:48:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vPnHCmRMLf6IgFM07niQU0RihdYqnsg77B8pitYt8tE=; b=NZkBE/NGUv7Kn/6b+MumG7nxfuN5fr9QXVJWhFiyXYbXBpcIYhhl6/m6xaVcqIfWLe fbUpynnY/JWhFG5+tXTPCXQfOPdOxmmjQ75HhYwyh6Et0NTcvMe/bMAMsksnN5nHAFms djlB/o6pxbdoovJYwD3soHWekOoto8r4MnhWgGGJ/HVP9JiFmAdaT/+e1vG13cwYuSHz QULvenSTd7WWssuDuv7U6olCi4fDVBRq+6mYN+puZbBfNYAn6X/lVAYqdOAftVFawI/N NFPjUooImez3ntJ4EZeT2/dTAkYimVxwDTDpK2dkdf/zdQxZTdUv9oGyH9mdnxN+Q8Wf l1kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vPnHCmRMLf6IgFM07niQU0RihdYqnsg77B8pitYt8tE=; b=OWxIUikXiMi4TqJSuXe81Ajzw+QKoLl3a/u9jiEY6eZeFbkrgDmloTK5ciRIE56FAe JMFiaqLWqOiHrVcfhoUKx/ODmZ50xI+OPeY+Po97pSfyd6D6llD6KZpxwxyxbbf934TC sk1690URpWjHMWsFDPyxnOpQWreMJYWHpUBGAlfyjeZcV9WvCdCCuqXf/t+62puuJoUA CH6YMS7arSAJ1oW0S37TrFkcWxsrh68+f+XSZS2mu8WBTs5bthrTPjC/n/FB3yRSf0P6 +/dIai4Ef8bcmkYlarwRZ+QbUygFdfzM+WpdXqGjZQaNBNi2Hm55w90Z6CLQYn1TyXKX MNJQ== X-Gm-Message-State: AFqh2kofYG7tmIDcFr16+bSApSFwFrSVLQo7yp/2TuS57rcqyUgjc0Rg JMWUXN/Vv758cFUtuT3cZBG+6qdQ0vb/kg== X-Google-Smtp-Source: AMrXdXuVGM8yzTVaOxtWE2NI9pZkUFF3izDZEeNQQiTC4QP1aTRtFkm86YKtfAmKuTSibu7MbU5g7A== X-Received: by 2002:a17:902:c652:b0:195:f265:66da with SMTP id s18-20020a170902c65200b00195f26566damr10387979pls.9.1674503326103; Mon, 23 Jan 2023 11:48:46 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:45 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 04/11] mesh: Rename parameter list per crypto usage Date: Mon, 23 Jan 2023 11:48:17 -0800 Message-Id: <20230123194824.257351-5-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix The derived key generated by the "nkpk" salt and network master key is used to create Private Beacons as of Mesh Profile Specification v1.1 --- mesh/crypto.c | 4 ++-- mesh/crypto.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mesh/crypto.c b/mesh/crypto.c index 668d16877..3754cb012 100644 --- a/mesh/crypto.c +++ b/mesh/crypto.c @@ -251,9 +251,9 @@ bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16]) return crypto_128(n, "nkbk", beacon_key); } -bool mesh_crypto_nkpk(const uint8_t n[16], uint8_t proxy_key[16]) +bool mesh_crypto_nkpk(const uint8_t n[16], uint8_t private_key[16]) { - return crypto_128(n, "nkpk", proxy_key); + return crypto_128(n, "nkpk", private_key); } bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]) diff --git a/mesh/crypto.h b/mesh/crypto.h index c31abbbbd..55789886e 100644 --- a/mesh/crypto.h +++ b/mesh/crypto.h @@ -26,7 +26,7 @@ bool mesh_aes_ecb_one(const uint8_t key[16], const uint8_t plaintext[16], uint8_t encrypted[16]); bool mesh_crypto_nkik(const uint8_t network_key[16], uint8_t identity_key[16]); bool mesh_crypto_nkbk(const uint8_t network_key[16], uint8_t beacon_key[16]); -bool mesh_crypto_nkpk(const uint8_t network_key[16], uint8_t proxy_key[16]); +bool mesh_crypto_nkpk(const uint8_t network_key[16], uint8_t private_key[16]); bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr, uint8_t id[16]); bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16], From patchwork Mon Jan 23 19:48:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112869 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 527F7C05027 for ; Mon, 23 Jan 2023 19:49:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231803AbjAWTtK (ORCPT ); Mon, 23 Jan 2023 14:49:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53048 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231717AbjAWTs7 (ORCPT ); Mon, 23 Jan 2023 14:48:59 -0500 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 138C236461 for ; Mon, 23 Jan 2023 11:48:48 -0800 (PST) Received: by mail-pl1-x62f.google.com with SMTP id k18so12516868pll.5 for ; Mon, 23 Jan 2023 11:48:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4ynNzGasY3WYA3dopM2vRzwNteTuCcqvqHHCejCA+Rw=; b=TL2mdaDk+7c+adSxH4KH6s/hUhBETVHQYf9AWpV1JM4IwF89iPsPajzS4UoH/Tpxqa tkb4Osuxe7oOtJnnuHdhX2zyUmxZkpUjh8MFD9/Jm8r99bquj5H28NYwCo650SwX6E4p 8KjE+ede8bEcRdLEjF90NwHOYbpc3LIThWIsGIwZtMY9IOtIM0d+oFjgbLqNql72KTNC B6Yn1EdGNQAZE9RC/83YGF3BtRO2kpF5ZNIsJxR0j503Kcj9syqEFEsQHl2XVZdRktoT rh5MzMB15LjfpFXfKK33/r+OW6iO4jerccJwnt2xbbC0kEVEJUN705uis8TxqQFJIYLZ aohg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4ynNzGasY3WYA3dopM2vRzwNteTuCcqvqHHCejCA+Rw=; b=WIfKKXQGApgTx/UunIJqSgfiO7rX/zM0rztYHu73Me/zfpXCtNQ3HdlkMLhBwQHde2 /F5aeZD1INS4gdDfQ04SLoSpNhM5O4749yyQrf9uHy0erIL+x5/DJVP5Yo6T9HIf1dbg 1G+bwsTH+2InIDE+4cz2YqXz9xuWLg4E/fLoakx2nr30AnE5PF+IIy8oQaK6v+pwZF7P vF3cm2T8ATTwSjLXBzJ37KtNDEKwOrprJ7Ry9GUr+gSPpzAdosmn6UQU8qnCzUcfA1xd 6+7dRAN1VkP/+Th9Amp9Cz4MplnxJylZvEP7a+jm35kFsqUH6/VsB/F72jSPaimZkvji AsPA== X-Gm-Message-State: AFqh2kpkS8xRruWY4BWSZ2WR8HJL2VEEDJk2IY2dsoEVzy3fMp4K2pkZ bUdMVeSLAUx2gqBL0gfstclPG8e1zFzjEQ== X-Google-Smtp-Source: AMrXdXtLCEYaVn6tUdmPa1TkXCvWdJs6HPEsPLN+pA/KyDUZTvQdYEgfvVrIStc3k1ZUcKKfJ0Cj6A== X-Received: by 2002:a17:902:8216:b0:194:59c2:a155 with SMTP id x22-20020a170902821600b0019459c2a155mr23431682pln.16.1674503327049; Mon, 23 Jan 2023 11:48:47 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:46 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 05/11] unit/mesh: Add unit testing of Mesh Private Beaconing Date: Mon, 23 Jan 2023 11:48:18 -0800 Message-Id: <20230123194824.257351-6-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix This includes the Sample Data from the Mesh Profile specification v1.1, and validates that the beacon crypto functions work as expected. --- unit/test-mesh-crypto.c | 125 ++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 24 deletions(-) diff --git a/unit/test-mesh-crypto.c b/unit/test-mesh-crypto.c index f9b7d81da..205a302c8 100644 --- a/unit/test-mesh-crypto.c +++ b/unit/test-mesh-crypto.c @@ -633,6 +633,36 @@ static const struct mesh_crypto_test s8_4_3 = { .beacon = "01003ecaff672f673370123456788ea261582f364f6f", }; +static const struct mesh_crypto_test s8_4_6_1 = { + .name = "8.4.6.1 Private Beacon IVU", + + .net_key = "f7a2a44f8e8a8029064f173ddc1e2b00", + .iv_index = 0x1010abcd, + + .enc_key = "6be76842460b2d3a5850d4698409f1bb", + .rand = "435f18f85cf78a3121f58478a5", + + .beacon_type = 0x02, + .beacon_flags = 0x02, + .beacon_cmac = "f3174f022a514741", + .beacon = "02435f18f85cf78a3121f58478a561e488e7cbf3174f022a514741", +}; + +static const struct mesh_crypto_test s8_4_6_2 = { + .name = "8.4.6.2 Private Beacon IVU Complete", + + .net_key = "3bbb6f1fbd53e157417f308ce7aec58f", + .iv_index = 0x00000000, + + .enc_key = "ca478cdac626b7a8522d7272dd124f26", + .rand = "1b998f82927535ea6f3076f422", + + .beacon_type = 0x02, + .beacon_flags = 0x00, + .beacon_cmac = "2f0ffb94cf97f881", + .beacon = "021b998f82927535ea6f3076f422ce827408ab2f0ffb94cf97f881", +}; + static const struct mesh_crypto_test s8_6_2 = { .name = "8.6.2 Service Data using Node Identity", @@ -683,6 +713,17 @@ static void verify_data(const char *label, unsigned int indent, l_free(str); } +static void verify_bool(const char *label, unsigned int indent, + bool sample, bool data) +{ + l_info("%-20s =%*c%s", label, 1 + (indent * 2), ' ', + sample ? "true" : "false"); + l_info("%-20s %*c%s => %s", "", 1 + (indent * 2), ' ', + data ? "true" : "false", + EVALNUM(sample, data)); + EXITNUM(sample, data); +} + static void verify_bool_not_both(const char *label, unsigned int indent, bool sample, bool data) { @@ -926,7 +967,7 @@ static void check_encrypt(const struct mesh_crypto_test *keys) uint8_t *dev_key; uint8_t *app_key; uint8_t *net_key; - uint8_t nid; + uint8_t nid = 0; uint8_t enc_key[16]; uint8_t priv_key[16]; uint8_t net_nonce[13]; @@ -960,8 +1001,7 @@ static void check_encrypt(const struct mesh_crypto_test *keys) show_data("NetworkKey", 0, net_key, 16); - if (keys->akf) { - mesh_crypto_k4(app_key, &key_aid); + if (keys->akf && mesh_crypto_k4(app_key, &key_aid)) { key_aid |= KEY_ID_AKF; } else { key_aid = 0; @@ -1265,7 +1305,7 @@ static void check_decrypt_segment(const struct mesh_crypto_test *keys, uint8_t net_clr[29]; uint64_t net_mic64, calc_net_mic64; uint32_t hdr, net_mic32, calc_net_mic32; - bool ctl, segmented, relay, szmic, key_akf; + bool ctl, segmented, relay, szmic, key_akf, status; uint8_t ttl, opcode, key_aid, segO, segN; uint32_t seq; uint16_t src, dst, seqZero; @@ -1277,7 +1317,7 @@ static void check_decrypt_segment(const struct mesh_crypto_test *keys, enc_key, priv_key); show_data("Decoded", 0, net_clr, pkt_len); - mesh_crypto_packet_parse(net_clr, pkt_len, + status = mesh_crypto_packet_parse(net_clr, pkt_len, &ctl, &ttl, &seq, &src, &dst, NULL, &opcode, @@ -1286,6 +1326,10 @@ static void check_decrypt_segment(const struct mesh_crypto_test *keys, &segO, &segN, &msg, &msg_len); + verify_bool("Crypto Parse", 0, true, status); + if (!status) + return; + if (ctl) { net_mic64 = l_get_be64(pkt + pkt_len - 8); show_data("EncryptedPayload", 7, pkt + 7, pkt_len - 7 - 8); @@ -1416,7 +1460,7 @@ static void check_decrypt(const struct mesh_crypto_test *keys) uint16_t app_msg_len = 0; uint32_t calc_net_mic32, net_mic32 = 0; uint64_t calc_net_mic64, net_mic64 = 0; - bool net_ctl, net_segmented, net_rly, net_akf; + bool net_ctl, net_segmented, net_rly, net_akf, status; uint8_t net_aid, net_ttl, nid, net_segO, net_segN = 0; uint32_t net_seq, hdr, seqZero = 0; uint16_t net_src, net_dst; @@ -1501,8 +1545,14 @@ static void check_decrypt(const struct mesh_crypto_test *keys) net_msg = packet + 7; net_msg_len = packet_len - 7; - mesh_crypto_network_clarify(packet, priv_key, keys->iv_index, - &net_ctl, &net_ttl, &net_seq, &net_src); + status = mesh_crypto_network_clarify(packet, priv_key, + keys->iv_index, &net_ctl, &net_ttl, &net_seq, + &net_src); + + verify_bool("Crypto Clarify", 0, true, status); + if (!status) + return; + show_str("Packet", 0, keys->packet[i]); @@ -1731,42 +1781,67 @@ static void check_beacon(const struct mesh_crypto_test *keys) { uint8_t *net_key; uint8_t *beacon_cmac; - uint8_t beacon[22]; + uint8_t *random = NULL; + uint8_t beacon[29]; uint8_t enc_key[16]; uint8_t net_id[8]; uint8_t cmac[8]; - uint64_t cmac_tmp; + uint64_t cmac_tmp = 0; + + if (keys->beacon_type < 1 || keys->beacon_type > 2) + verify_uint8("Unknown Beacon", 0, true, + (keys->beacon_type >= 1 || keys->beacon_type <= 2)); net_key = l_util_from_hexstring(keys->net_key, NULL); beacon_cmac = l_util_from_hexstring(keys->beacon_cmac, NULL); - mesh_crypto_nkbk(net_key, enc_key); + if (keys->beacon_type == 1) { + mesh_crypto_nkbk(net_key, enc_key); + } else { + mesh_crypto_nkpk(net_key, enc_key); + random = l_util_from_hexstring(keys->rand, NULL); + } + mesh_crypto_k3(net_key, net_id); l_info(COLOR_BLUE "[%s]" COLOR_OFF, keys->name); verify_data("NetworkKey", 0, keys->net_key, net_key, 16); + show_uint8("Beacon Flags", 0, keys->beacon_flags); show_uint32("IVindex", 0, keys->iv_index); verify_data("BeaconKey", 0, keys->enc_key, enc_key, 16); - verify_data("NetworkID", 0, keys->net_id, net_id, 8); beacon[0] = keys->beacon_type; - beacon[1] = keys->beacon_flags; - memcpy(beacon + 2, net_id, 8); - l_put_be32(keys->iv_index, beacon + 10); - mesh_crypto_beacon_cmac(enc_key, net_id, keys->iv_index, - !!(keys->beacon_flags & 0x01), - !!(keys->beacon_flags & 0x02), - &cmac_tmp); - - l_put_be64(cmac_tmp, cmac); - l_put_be64(cmac_tmp, beacon + 14); - verify_data("BeaconCMAC", 0, keys->beacon_cmac, cmac, 8); - verify_data("Beacon", 0, keys->beacon, beacon, sizeof(beacon)); + if (keys->beacon_type == 1) { + verify_data("NetworkID", 0, keys->net_id, net_id, 8); + beacon[1] = keys->beacon_flags; + memcpy(beacon + 2, net_id, 8); + l_put_be32(keys->iv_index, beacon + 10); + mesh_crypto_beacon_cmac(enc_key, net_id, keys->iv_index, + !!(keys->beacon_flags & 0x01), + !!(keys->beacon_flags & 0x02), + &cmac_tmp); + + l_put_be64(cmac_tmp, cmac); + l_put_be64(cmac_tmp, beacon + 14); + verify_data("BeaconCMAC", 0, keys->beacon_cmac, cmac, 8); + verify_data("SNBeacon", 0, keys->beacon, beacon, 22); + } else { + show_data("Random", 0, random, sizeof(random)); + beacon[14] = keys->beacon_flags; + l_put_be32(keys->iv_index, beacon + 15); + mesh_crypto_aes_ccm_encrypt(random, enc_key, NULL, 0, + beacon + 14, 5, + beacon + 14, NULL, 8); + memcpy(beacon + 1, random, 13); + verify_data("BeaconMIC", 0, keys->beacon_cmac, beacon + 19, 8); + verify_data("PrivBeacon", 0, keys->beacon, beacon, 27); + } l_info(""); + l_free(random); l_free(beacon_cmac); l_free(net_key); } @@ -2071,6 +2146,8 @@ int main(int argc, char *argv[]) /* Section 8.4 Beacon Sample Data */ check_beacon(&s8_4_3); + check_beacon(&s8_4_6_1); + check_beacon(&s8_4_6_2); /* Section 8.6 Mesh Proxy Service sample data */ check_id_beacon(&s8_6_2); From patchwork Mon Jan 23 19:48:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112870 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C0CD9C54EAA for ; Mon, 23 Jan 2023 19:49:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232888AbjAWTtM (ORCPT ); Mon, 23 Jan 2023 14:49:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232744AbjAWTtB (ORCPT ); Mon, 23 Jan 2023 14:49:01 -0500 Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 02B7C3250A for ; Mon, 23 Jan 2023 11:48:48 -0800 (PST) Received: by mail-pl1-x62a.google.com with SMTP id p24so12490796plw.11 for ; Mon, 23 Jan 2023 11:48:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=MxFB/vGyvOLqHBHjxb1DG1dv0eiHN4kUObFXqXhRuYU=; b=EG5V9woxCDgChsQbUtxN89Q/eai9B8k23FLXUTD0zdb6M4kJYsGbAbdew66tbvceTi op9Zoy4rFvx6lD/Eeds3aD1U0bi+hOoPh5XCd6maZiVCI4m4uCpm2xPK6+BkFb4DT4Vm 54ZfimYYvKnOEAoT5fILXCfctHFRrYCqgzGppQFMgbTUGpx0vxk7dK/i7wUXyBfZ9ZPF mfGueMs/YeQy/uZp1ty9sW3NPizXtAcd2QHWgr0GkbMqSO5OH/fPp50/74RajjH5IoXp +2MwmEdhtwas816f9voo0+dFzHxHi8teZFYSUUgCzz39WjYT1Q74CFBiH4cis19Xtddu iE2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MxFB/vGyvOLqHBHjxb1DG1dv0eiHN4kUObFXqXhRuYU=; b=BpdIifqhRUBOGCyMBQE+ChqaIhopwrkKsC2lKr6lbTS94goyyw/BNag53Vfa94e+pB DIjtBUSw3X7w4ARIm9hRjEsbHjnU2anxYQ3sYHxQgpa/K/iRwzMASIRq1fHUD9dW27oM HihMyVu/kcvXKWQf6a4rm3Er6SwOICiTfFnnd5toK8FPCzsoliiSwzysPGM5n2Tqyond vQ4k/tHnuWSDl6Z6FebScncGv8vu7/PpC3Y2HstwKj8OtfIM2IFX4wyMUkB8y6TcsRNi rFLoeB1U8DY3CO5M+ZZmyuGufgtI0wAI6YwORtS1isl/Q7KbNWwIBhQMRBLxzMrSNDCM 0y7A== X-Gm-Message-State: AFqh2kqZt+azVjV7Q0G7FINv7B51NWxc9aUIQHcmxPdZV/zYJW/RewN7 ox0beydjlHbAtZrWXQxyaeFefv3fd91CoA== X-Google-Smtp-Source: AMrXdXsNu+lTVetuE1TCibjqZMQuJw3wECK09jIltk3jpP3DL5sF1KbrHabbx7croidmkP6KiSOc3g== X-Received: by 2002:a17:902:7881:b0:192:bb38:c412 with SMTP id q1-20020a170902788100b00192bb38c412mr25080056pll.44.1674503327979; Mon, 23 Jan 2023 11:48:47 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:47 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 06/11] mesh: Add storage of Mesh Private Beacon settings Date: Mon, 23 Jan 2023 11:48:19 -0800 Message-Id: <20230123194824.257351-7-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix If current storage does not exist in node.json, the Mesh Private Beacon will be disabled. --- mesh/mesh-config-json.c | 48 +++++++++++++++++++++++++++++++++++++++++ mesh/mesh-config.h | 6 ++++++ 2 files changed, 54 insertions(+) diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c index 8f321a731..c198627c6 100644 --- a/mesh/mesh-config-json.c +++ b/mesh/mesh-config-json.c @@ -1337,6 +1337,19 @@ static void parse_features(json_object *jconfig, struct mesh_config_node *node) node->modes.beacon = mode; } + if (json_object_object_get_ex(jconfig, "mpb", &jvalue)) { + mode = get_mode(jvalue); + if (mode <= MESH_MODE_UNSUPPORTED) + node->modes.mpb = mode; + + if (node->modes.mpb == MESH_MODE_ENABLED) { + if (json_object_object_get_ex(jconfig, "mpbPeriod", + &jvalue)) + node->modes.mpb_period = + json_object_get_int(jvalue); + } + } + if (!json_object_object_get_ex(jconfig, "relay", &jrelay)) return; @@ -1576,6 +1589,18 @@ bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword, return save_config(cfg->jnode, cfg->node_dir_path); } +bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword, + int value, bool save) +{ + if (!cfg) + return false; + + if (save) + return mesh_config_write_mode(cfg, keyword, value); + else + return write_mode(cfg->jnode, keyword, value); +} + static bool write_relay_mode(json_object *jobj, uint8_t mode, uint8_t count, uint16_t interval) { @@ -1622,6 +1647,21 @@ bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode, return save_config(cfg->jnode, cfg->node_dir_path); } +bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode, + uint8_t period) +{ + + if (!cfg || !write_mode(cfg->jnode, "mpb", mode)) + return false; + + if (mode) { + if (!write_int(cfg->jnode, "mpbPeriod", period)) + return false; + } + + return save_config(cfg->jnode, cfg->node_dir_path); +} + bool mesh_config_write_net_transmit(struct mesh_config *cfg, uint8_t cnt, uint16_t interval) { @@ -1746,6 +1786,14 @@ static struct mesh_config *create_config(const char *cfg_path, if (!write_mode(jnode, "beacon", modes->beacon)) return NULL; + if (!write_mode(jnode, "mpb", modes->mpb)) + return NULL; + + if (modes->mpb) { + if (!write_int(jnode, "mpbPeriod", modes->mpb_period)) + return NULL; + } + /* Sequence number */ json_object_object_add(jnode, sequenceNumber, json_object_new_int(node->seq_number)); diff --git a/mesh/mesh-config.h b/mesh/mesh-config.h index ed1b610de..3cb20b85d 100644 --- a/mesh/mesh-config.h +++ b/mesh/mesh-config.h @@ -60,6 +60,8 @@ struct mesh_config_modes { uint8_t friend; uint8_t proxy; uint8_t beacon; + uint8_t mpb; + uint8_t mpb_period; }; struct mesh_config_netkey { @@ -140,9 +142,13 @@ bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq, bool mesh_config_write_unicast(struct mesh_config *cfg, uint16_t unicast); bool mesh_config_write_relay_mode(struct mesh_config *cfg, uint8_t mode, uint8_t count, uint16_t interval); +bool mesh_config_write_mpb(struct mesh_config *cfg, uint8_t mode, + uint8_t period); bool mesh_config_write_ttl(struct mesh_config *cfg, uint8_t ttl); bool mesh_config_write_mode(struct mesh_config *cfg, const char *keyword, int value); +bool mesh_config_write_mode_ex(struct mesh_config *cfg, const char *keyword, + int value, bool save); bool mesh_config_comp_page_add(struct mesh_config *cfg, uint8_t page, uint8_t *data, uint16_t size); void mesh_config_comp_page_del(struct mesh_config *cfg, uint8_t page); From patchwork Mon Jan 23 19:48:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112872 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BB6E9C25B50 for ; Mon, 23 Jan 2023 19:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232894AbjAWTtN (ORCPT ); Mon, 23 Jan 2023 14:49:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232883AbjAWTtB (ORCPT ); Mon, 23 Jan 2023 14:49:01 -0500 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CBC1836441 for ; Mon, 23 Jan 2023 11:48:49 -0800 (PST) Received: by mail-pj1-x1032.google.com with SMTP id b10so12704286pjo.1 for ; Mon, 23 Jan 2023 11:48:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XdkWNN5xfRheWct+35hGqDUCaKNywAbxYOWcT27fHGI=; b=dj2WadGETafdgES+YRGK1lxQJOFQPsoLBrnnFBJeLJ4K7XA0Bs4LzBlxjshVIX5ro8 OgKyjvs48WySEULw5DqfjbcDCbl07+d7akfXa318wLA49NWY9nw49S6gGrwLNNlNA1Sw rCCB5tJTAu+9AECUQdOU7s2DnF5GdTGvDwzdBlbT/iRVNsQdnGujVQJxVo4tGMTIaYMj LvnAqj4K2RajLr7VeO2r0SA3W3CJ3NtBgGfFlcNpJ5aqnPzKm9mhXMnpArAUZtQDX2r7 8pqRiVVdjRG8U5/yCMX2aIb7DNewZXpjMJmx3GE+zFQOxHxN9piRdjewemSe0ssfk+ZD dy+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XdkWNN5xfRheWct+35hGqDUCaKNywAbxYOWcT27fHGI=; b=xX6rZLhJ256Dt4yMeJF06oVRCmTj6cR4MFwVoVLXDZ0Bw8CzhrAtEmz3knTqtACTq1 bpvFhNvL1NLAv1OCeq3XpIAUrzXswaXqLn7/L8h9naZ6ZJhNWYBWX+pDK7SaZs01mubj l6G89ikY1hRYgwmhkJVLdhcZvF6/8n8S+E6jUQwZzLWvplVEa9AqDAY/ypnWRa7oPlaq 9K+gfRmHD+1GyvCaW7pKzkTTrNeLyrLxCkdxBv+Hk06dgDjCx+l5DLcwmtQfC83v/VjH jIHiUN6Lze9NxF0+fdCAtT5UhZl1IKRmuYjg1Og5Pcm/m/dhKHvA2TAy6nrI94fkzzcR m2/g== X-Gm-Message-State: AFqh2krYiHYdQfZ9dUGj5Te5ZR1QO37+/i4wDSA4/Y8/BP9l34KkWLTt L8jIC678OhEpQiVWlNkD9Xl2/lJZ54piyQ== X-Google-Smtp-Source: AMrXdXuzDhrPkZ/rYd2LFpT1vJQEOZjKJDbxOnZF5Zank18inzcN+ta0DDHbcBhOP6yoz05tfzGhew== X-Received: by 2002:a17:902:c1d2:b0:194:708f:6483 with SMTP id c18-20020a170902c1d200b00194708f6483mr26535759plc.57.1674503328861; Mon, 23 Jan 2023 11:48:48 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:48 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 07/11] mesh: Add Mesh Private Beacon server Date: Mon, 23 Jan 2023 11:48:20 -0800 Message-Id: <20230123194824.257351-8-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix This initial server supports only the Mesh Private Beacon and returns "Not Suppoerted" for Get/Set of Private GATT Proxy and Private Node Identity beacons. --- Makefile.mesh | 1 + mesh/prv-beacon.h | 36 +++++++++++++ mesh/prvbeac-server.c | 123 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 mesh/prv-beacon.h create mode 100644 mesh/prvbeac-server.c diff --git a/Makefile.mesh b/Makefile.mesh index e18a169eb..63f085de1 100644 --- a/Makefile.mesh +++ b/Makefile.mesh @@ -36,6 +36,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \ mesh/pb-adv.h mesh/pb-adv.c \ mesh/keyring.h mesh/keyring.c \ mesh/rpl.h mesh/rpl.c \ + mesh/prv-beacon.h mesh/prvbeac-server.c \ mesh/mesh-defs.h pkglibexec_PROGRAMS += mesh/bluetooth-meshd diff --git a/mesh/prv-beacon.h b/mesh/prv-beacon.h new file mode 100644 index 000000000..7be7a01c8 --- /dev/null +++ b/mesh/prv-beacon.h @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +struct mesh_node; + +#define PRV_BEACON_SRV_MODEL SET_ID(SIG_VENDOR, 0x0008) +#define PRV_BEACON_CLI_MODEL SET_ID(SIG_VENDOR, 0x0009) + +/* Private Beacon opcodes */ +#define OP_PRIVATE_BEACON_GET 0x8060 +#define OP_PRIVATE_BEACON_SET 0x8061 +#define OP_PRIVATE_BEACON_STATUS 0x8062 +#define OP_PRIVATE_GATT_PROXY_GET 0x8063 +#define OP_PRIVATE_GATT_PROXY_SET 0x8064 +#define OP_PRIVATE_GATT_PROXY_STATUS 0x8065 +#define OP_PRIVATE_NODE_ID_GET 0x8066 +#define OP_PRIVATE_NODE_ID_SET 0x8067 +#define OP_PRIVATE_NODE_ID_STATUS 0x8068 + +void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx); diff --git a/mesh/prvbeac-server.c b/mesh/prvbeac-server.c new file mode 100644 index 000000000..f3a6eaa82 --- /dev/null +++ b/mesh/prvbeac-server.c @@ -0,0 +1,123 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "mesh/mesh-defs.h" +#include "mesh/node.h" +#include "mesh/net.h" +#include "mesh/appkey.h" +#include "mesh/model.h" +#include "mesh/mesh-config.h" +#include "mesh/prv-beacon.h" + +#define NOT_SUPPORTED 0x02 + +static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, + uint16_t net_idx, const uint8_t *data, + uint16_t size, const void *user_data) +{ + struct mesh_node *node = (struct mesh_node *) user_data; + const uint8_t *pkt = data; + uint32_t opcode; + uint8_t msg[5]; + uint16_t n; + uint8_t period = 0; + + if (app_idx != APP_IDX_DEV_LOCAL) + return false; + + if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { + size -= n; + pkt += n; + } else + return false; + + l_debug("PRV-BEAC-SRV-opcode 0x%x size %u idx %3.3x", opcode, size, + net_idx); + + n = 0; + + switch (opcode) { + default: + return false; + + case OP_PRIVATE_BEACON_SET: + if (size == 1) + period = 0xff; + else if (size == 2) + period = pkt[1]; + else + return true; + + /* fallthrough */ + + case OP_PRIVATE_BEACON_GET: + n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg); + + msg[n++] = NOT_SUPPORTED; + msg[n++] = period; + + l_debug("Get/Set Private Beacon (%d)", msg[n-2]); + break; + + case OP_PRIVATE_GATT_PROXY_SET: + /* fallthrough */ + case OP_PRIVATE_GATT_PROXY_GET: + n = mesh_model_opcode_set(OP_PRIVATE_GATT_PROXY_STATUS, msg); + msg[n++] = NOT_SUPPORTED; + break; + + case OP_PRIVATE_NODE_ID_SET: + /* fallthrough */ + case OP_PRIVATE_NODE_ID_GET: + n = mesh_model_opcode_set(OP_PRIVATE_NODE_ID_STATUS, msg); + msg[n++] = NOT_SUPPORTED; + break; + } + + if (n) + mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx, + DEFAULT_TTL, false, n, msg); + + return true; +} + +static void prvbec_srv_unregister(void *user_data) +{ +} + +static const struct mesh_model_ops ops = { + .unregister = prvbec_srv_unregister, + .recv = prvbec_srv_pkt, + .bind = NULL, + .sub = NULL, + .pub = NULL +}; + +void prv_beacon_server_init(struct mesh_node *node, uint8_t ele_idx) +{ + l_debug("%2.2x", ele_idx); + mesh_model_register(node, ele_idx, PRV_BEACON_SRV_MODEL, &ops, node); +} From patchwork Mon Jan 23 19:48:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112874 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DAFEAC05027 for ; Mon, 23 Jan 2023 19:49:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231915AbjAWTtP (ORCPT ); Mon, 23 Jan 2023 14:49:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53720 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232882AbjAWTtG (ORCPT ); Mon, 23 Jan 2023 14:49:06 -0500 Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5FF4A366B3 for ; Mon, 23 Jan 2023 11:48:51 -0800 (PST) Received: by mail-pj1-x102a.google.com with SMTP id s13-20020a17090a6e4d00b0022900843652so16315042pjm.1 for ; Mon, 23 Jan 2023 11:48:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=407Z2w0BBlNKEhCLrOumCSQPsx6l5mvADFbiNUhctXE=; b=qWr/S1X+JHOZ/wYcc9I22LtcIXaNo5oY8S8rZtj6xsaGJ8FlEyp5/VNL5xqgw71uL6 urzNEHZIhizB6MDmo7XEbfwfzWiaoIgZmb5E7LR1ewQ/tBwpl167CU5DL03W6bt7/nGd Dmo4KGg416XdmkPpHzOJ5mUqgYGxO1rbSPPWXoQkqhAaDIqoRLZbF8FVgQPI8vlB2gmQ 917RGGV4AEbd1Z6STwehS0orJ0Jlun/bEk1Frb8YukKaBKKEwfJFKMt3Z+O5J4Lodgp9 067ogX25usU6uzFVFie98XgnCcwwitD8XLq1cjaI11xf9iXH2PFGk2oocIeB/g43NQT4 whRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=407Z2w0BBlNKEhCLrOumCSQPsx6l5mvADFbiNUhctXE=; b=Yvl8qSS/0goydldluabx8esBTzZsaEIeA1bx49D5rLgvyFnIASQtl5j82z7VyKKzzk RAo3uNgYFD9VeR23gEOAA1oa1RIalQkdRDT6okonlNhhrQomP/oVUvqia5u7eFu4qgKQ l1gMUrwpjjKN3zy7NwCULeC/+CwZ97qGnucITVZ/T2ZfqDNFb00Hj9weYHwGXV+NsNJz 4Oj6g0z1kiOt1dH4LVcq/fzKeImXPVGEYrWx3Q7bSo/WcY7mNn61QGVpGPt3CH39lYdc XVQzXvqDjrNEKCRQSB09wfMYL6Ohau6uuSkgDjl4Ct1hbEe7GYmOu4XZMCg2vKBEGGVg O0lQ== X-Gm-Message-State: AFqh2kr/c9tMQpNDqlNXSt3m6eeCVLPbTBNfHJpDwwSl+GdlnEGDSEiQ K2oEPGet4IdvZWrX806X85/uDPPRY3UIVw== X-Google-Smtp-Source: AMrXdXuNO7DtEkAb/ZAc/vXcko4yY3xytuGG4CcmpWrjNTB6bcwdBzkMpk5BU4yimXVMWisNPmvXVg== X-Received: by 2002:a17:902:b413:b0:192:6bb1:ed5a with SMTP id x19-20020a170902b41300b001926bb1ed5amr23799502plr.38.1674503329952; Mon, 23 Jan 2023 11:48:49 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:49 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 08/11] mesh: Add Tx/Rx support of Mesh Private Beacons Date: Mon, 23 Jan 2023 11:48:21 -0800 Message-Id: <20230123194824.257351-9-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix With this change, we start evaluating received Mesh Private Beacons in addition to the legacy Secure Network Beacons. We also add the ability to request Tx of Mesh Private Beacons, which are regenerated with new Random Nonce a minimum of every 0 - 2550 seconds. --- mesh/net-keys.c | 501 +++++++++++++++++++++++++++++++++++++----------- mesh/net-keys.h | 10 +- mesh/net.c | 172 ++++++++++++----- mesh/net.h | 6 +- mesh/node.c | 4 +- 5 files changed, 519 insertions(+), 174 deletions(-) diff --git a/mesh/net-keys.c b/mesh/net-keys.c index ee7bbf0c0..0ba051d79 100644 --- a/mesh/net-keys.c +++ b/mesh/net-keys.c @@ -21,39 +21,55 @@ #include "mesh/net.h" #include "mesh/net-keys.h" -#define BEACON_TYPE_SNB 0x01 -#define KEY_REFRESH 0x01 -#define IV_INDEX_UPDATE 0x02 - #define BEACON_INTERVAL_MIN 10 #define BEACON_INTERVAL_MAX 600 -struct net_beacon { +/* This allows daemon to skip decryption on recently seen beacons */ +#define BEACON_CACHE_MAX 10 + +struct beacon_rx { + uint8_t data[28]; + uint32_t id; + uint32_t ivi; + bool kr; + bool ivu; +}; + +struct beacon_observe { struct l_timeout *timeout; uint32_t ts; - uint16_t observe_period; - uint16_t observed; + uint16_t period; + uint16_t seen; uint16_t expected; bool half_period; - uint8_t beacon[23]; }; struct net_key { uint32_t id; - struct net_beacon snb; + struct l_timeout *mpb_to; + uint8_t *mpb; + uint8_t *snb; + struct beacon_observe observe; + uint32_t ivi; uint16_t ref_cnt; - uint16_t beacon_enables; + uint16_t mpb_enables; + uint16_t snb_enables; + uint8_t mpb_refresh; uint8_t friend_key; uint8_t nid; uint8_t flooding[16]; - uint8_t encrypt[16]; - uint8_t privacy[16]; - uint8_t beacon[16]; - uint8_t network[8]; + uint8_t enc_key[16]; + uint8_t prv_key[16]; + uint8_t snb_key[16]; + uint8_t pvt_key[16]; + uint8_t net_id[8]; + bool kr; + bool ivu; }; -static struct l_queue *keys = NULL; -static uint32_t last_flooding_id = 0; +static struct l_queue *beacons; +static struct l_queue *keys; +static uint32_t last_flooding_id; /* To avoid re-decrypting same packet for multiple nodes, cache and check */ static uint8_t cache_pkt[29]; @@ -81,9 +97,9 @@ static bool match_id(const void *a, const void *b) static bool match_network(const void *a, const void *b) { const struct net_key *key = a; - const uint8_t *network = b; + const uint8_t *net_id = b; - return memcmp(key->network, network, sizeof(key->network)) == 0; + return memcmp(key->net_id, net_id, sizeof(key->net_id)) == 0; } /* Key added from Provisioning, NetKey Add or NetKey update */ @@ -101,19 +117,26 @@ uint32_t net_key_add(const uint8_t flooding[16]) if (!keys) keys = l_queue_new(); + if (!beacons) + beacons = l_queue_new(); + key = l_new(struct net_key, 1); memcpy(key->flooding, flooding, 16); key->ref_cnt++; - result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->encrypt, - key->privacy); + result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key, + key->prv_key); if (!result) goto fail; - result = mesh_crypto_k3(flooding, key->network); + result = mesh_crypto_k3(flooding, key->net_id); if (!result) goto fail; - result = mesh_crypto_nkbk(flooding, key->beacon); + result = mesh_crypto_nkbk(flooding, key->snb_key); + if (!result) + goto fail; + + result = mesh_crypto_nkpk(flooding, key->pvt_key); if (!result) goto fail; @@ -146,7 +169,7 @@ uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd, l_put_be16(fn_cnt, p + 7); result = mesh_crypto_k2(key->flooding, p, sizeof(p), &frnd_key->nid, - frnd_key->encrypt, frnd_key->privacy); + frnd_key->enc_key, frnd_key->prv_key); if (!result) { l_free(frnd_key); @@ -167,7 +190,7 @@ void net_key_unref(uint32_t id) if (key && key->ref_cnt) { if (--key->ref_cnt == 0) { - l_timeout_remove(key->snb.timeout); + l_timeout_remove(key->observe.timeout); l_queue_remove(keys, key); l_free(key); } @@ -206,7 +229,7 @@ static void decrypt_net_pkt(void *a, void *b) result = mesh_crypto_packet_decode(cache_pkt, cache_len, false, cache_plain, cache_iv_index, - key->encrypt, key->privacy); + key->enc_key, key->prv_key); if (result) { cache_id = key->id; @@ -254,8 +277,8 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len) if (!key) return false; - result = mesh_crypto_packet_encode(pkt, len, iv_index, key->encrypt, - key->privacy); + result = mesh_crypto_packet_encode(pkt, len, iv_index, key->enc_key, + key->prv_key); if (!result) return false; @@ -265,9 +288,9 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len) return result; } -uint32_t net_key_network_id(const uint8_t network[8]) +uint32_t net_key_network_id(const uint8_t net_id[8]) { - struct net_key *key = l_queue_find(keys, match_network, network); + struct net_key *key = l_queue_find(keys, match_network, net_id); if (!key) return 0; @@ -275,6 +298,55 @@ uint32_t net_key_network_id(const uint8_t network[8]) return key->id; } +struct auth_check { + const uint8_t *data; + uint32_t id; + uint32_t ivi; + bool ivu; + bool kr; +}; + +static void check_auth(void *a, void *b) +{ + struct net_key *key = a; + struct auth_check *auth = b; + uint8_t out[5]; + + + /* Stop checking if already found */ + if (auth->id) + return; + + if (mesh_crypto_aes_ccm_decrypt(auth->data + 1, key->pvt_key, NULL, 0, + auth->data + 14, 13, + out, NULL, 8)) { + auth->id = key->id; + auth->ivi = l_get_be32(out + 1); + auth->ivu = !!(out[0] & 0x02); + auth->kr = !!(out[0] & 0x01); + } +} + +static uint32_t private_beacon_check(const void *beacon, uint32_t *ivi, + bool *ivu, bool *kr) +{ + struct auth_check auth = { + .data = beacon, + .id = 0, + }; + + auth.id = 0; + l_queue_foreach(keys, check_auth, &auth); + + if (auth.id) { + *ivi = auth.ivi; + *ivu = auth.ivu; + *kr = auth.kr; + } + + return auth.id; +} + bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint64_t cmac) { @@ -285,7 +357,7 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, return false; /* Any behavioral changes must pass CMAC test */ - if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr, + if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, iv_index, kr, ivu, &cmac_check)) { l_error("mesh_crypto_beacon_cmac failed"); return false; @@ -300,39 +372,142 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, return true; } -bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu, - uint8_t *snb) +static bool mpb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) +{ + uint8_t b_data[5 + 8]; + uint8_t random[13]; + + if (!key) + return false; + + b_data[0] = 0; + l_put_be32(ivi, b_data + 1); + + if (kr) + b_data[0] |= KEY_REFRESH; + + if (ivu) + b_data[0] |= IV_INDEX_UPDATE; + + l_getrandom(random, sizeof(random)); + if (!mesh_crypto_aes_ccm_encrypt(random, key->pvt_key, NULL, 0, + b_data, 5, b_data, NULL, 8)) + return false; + + key->mpb[0] = MESH_AD_TYPE_BEACON; + key->mpb[1] = BEACON_TYPE_MPB; + memcpy(key->mpb + 2, random, 13); + memcpy(key->mpb + 15, b_data, 13); + + return true; +} + +static bool snb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) { - struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); uint64_t cmac; if (!key) return false; /* Any behavioral changes must pass CMAC test */ - if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr, + if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, ivi, kr, ivu, &cmac)) { l_error("mesh_crypto_beacon_cmac failed"); return false; } - snb[0] = MESH_AD_TYPE_BEACON; - snb[1] = BEACON_TYPE_SNB; - snb[2] = 0; + key->snb[0] = MESH_AD_TYPE_BEACON; + key->snb[1] = BEACON_TYPE_SNB; + key->snb[2] = 0; if (kr) - snb[2] |= KEY_REFRESH; + key->snb[2] |= KEY_REFRESH; if (ivu) - snb[2] |= IV_INDEX_UPDATE; + key->snb[2] |= IV_INDEX_UPDATE; - memcpy(snb + 3, key->network, 8); - l_put_be32(iv_index, snb + 11); - l_put_be64(cmac, snb + 15); + memcpy(key->snb + 3, key->net_id, 8); + l_put_be32(ivi, key->snb + 11); + l_put_be64(cmac, key->snb + 15); return true; } +static bool match_beacon(const void *a, const void *b) +{ + const struct beacon_rx *cached = a; + const uint8_t *incoming = b; + + if (incoming[0] == BEACON_TYPE_MPB) + return !memcmp(cached->data, incoming, 27); + + if (incoming[0] == BEACON_TYPE_SNB) + return !memcmp(cached->data, incoming, 22); + + return false; +} + +uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, + bool *ivu, bool *kr) +{ + struct net_key *key; + struct beacon_rx *beacon; + uint32_t b_id, b_ivi; + bool b_ivu, b_kr; + + if (data[1] == BEACON_TYPE_SNB && len != 23) + return 0; + + if (data[1] == BEACON_TYPE_MPB && len != 28) + return 0; + + beacon = l_queue_remove_if(beacons, match_beacon, data + 1); + + if (beacon) + goto accept; + + /* Validate beacon data */ + if (data[1] == BEACON_TYPE_SNB) { + key = l_queue_find(keys, match_network, data + 3); + + if (!key) + return 0; + + b_id = key->id; + b_ivu = !!(data[2] & 0x02); + b_kr = !!(data[2] & 0x01); + b_ivi = l_get_be32(data + 11); + + if (!net_key_snb_check(b_id, b_ivi, b_kr, b_ivu, + l_get_be64(data + 15))) + return 0; + + } else if (data[1] == BEACON_TYPE_MPB) { + b_id = private_beacon_check(data + 1, &b_ivi, &b_ivu, &b_kr); + + if (!b_id) + return 0; + + } else + return 0; + + beacon = l_new(struct beacon_rx, 1); + memcpy(beacon->data, data + 1, len - 1); + beacon->id = b_id; + beacon->ivi = b_ivi; + beacon->ivu = b_ivu; + beacon->kr = b_kr; + +accept: + *ivi = beacon->ivi; + *ivu = beacon->ivu; + *kr = beacon->kr; + + l_queue_push_head(beacons, beacon); + + return beacon->id; +} + static void send_network_beacon(struct net_key *key) { struct mesh_io_send_info info = { @@ -343,10 +518,26 @@ static void send_network_beacon(struct net_key *key) .u.gen.max_delay = DEFAULT_MAX_DELAY }; - mesh_io_send(NULL, &info, key->snb.beacon, sizeof(key->snb.beacon)); + if (key->mpb_enables) { + /* If Interval steps == 0, refresh key every time */ + if (!key->mpb_refresh || !key->mpb || !key->mpb[0]) + net_key_beacon_refresh(key->id, key->ivi, key->kr, + key->ivu, true); + + mesh_io_send(NULL, &info, key->mpb, 28); + } + + if (key->snb_enables) { + if (!key->snb || !key->snb[0]) { + net_key_beacon_refresh(key->id, key->ivi, key->kr, + key->ivu, true); + } + + mesh_io_send(NULL, &info, key->snb, 23); + } } -static void snb_timeout(struct l_timeout *timeout, void *user_data) +static void beacon_timeout(struct l_timeout *timeout, void *user_data) { struct net_key *key = user_data; uint32_t interval, scale_factor; @@ -355,29 +546,29 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data) send_network_beacon(key); /* Count our own beacons towards the vicinity total */ - key->snb.observed++; + key->observe.seen++; - if (!key->snb.half_period) { + if (!key->observe.half_period) { l_debug("beacon %d for %d nodes, period %d, obs %d, exp %d", - key->id, - key->beacon_enables, - key->snb.observe_period, - key->snb.observed, - key->snb.expected); + key->id, + key->snb_enables + key->mpb_enables, + key->observe.period, + key->observe.seen, + key->observe.expected); - interval = (key->snb.observe_period * key->snb.observed) - / key->snb.expected; + interval = (key->observe.period * key->observe.seen) + / key->observe.expected; /* Limit Increases and Decreases by 10 seconds Up and * 20 seconds down each step, to avoid going nearly silent * in highly populated environments. */ - if (interval - 10 > key->snb.observe_period) - interval = key->snb.observe_period + 10; - else if (interval + 20 < key->snb.observe_period) - interval = key->snb.observe_period - 20; + if (interval - 10 > key->observe.period) + interval = key->observe.period + 10; + else if (interval + 20 < key->observe.period) + interval = key->observe.period - 20; /* Beaconing must be no *slower* than once every 10 minutes, * and no *faster* than once every 10 seconds, per spec. @@ -388,26 +579,26 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data) else if (interval > BEACON_INTERVAL_MAX * 2) interval = BEACON_INTERVAL_MAX * 2; - key->snb.observe_period = interval; - key->snb.observed = 0; + key->observe.period = interval; + key->observe.seen = 0; /* To prevent "over slowing" of the beaconing frequency, * require more significant "over observing" the slower * our own beaconing frequency. */ - key->snb.expected = interval / 10; + key->observe.expected = interval / 10; scale_factor = interval / 60; - key->snb.expected += scale_factor * 3; + key->observe.expected += scale_factor * 3; } - interval = key->snb.observe_period / 2; - key->snb.half_period = !key->snb.half_period; + interval = key->observe.period / 2; + key->observe.half_period = !key->observe.half_period; - if (key->beacon_enables) + if (key->mpb_enables || key->snb_enables) l_timeout_modify(timeout, interval); else { l_timeout_remove(timeout); - key->snb.timeout = NULL; + key->observe.timeout = NULL; } } @@ -416,8 +607,8 @@ void net_key_beacon_seen(uint32_t id) struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) { - key->snb.observed++; - key->snb.ts = get_timestamp_secs(); + key->observe.seen++; + key->observe.ts = get_timestamp_secs(); } } @@ -426,12 +617,83 @@ uint32_t net_key_beacon_last_seen(uint32_t id) struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) - return key->snb.ts; + return key->observe.ts; return 0; } -void net_key_beacon_enable(uint32_t id) +bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu, + bool force) +{ + struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); + bool refresh = force; + uint32_t rand_ms; + + if (!key) + return false; + + if (key->snb_enables && !key->snb) { + key->snb = l_new(uint8_t, 23); + refresh = true; + } + + if (key->mpb_enables && !key->mpb) { + key->mpb = l_new(uint8_t, 28); + refresh = true; + } + + if (key->ivi != ivi || key->ivu != ivu || key->kr != kr) + refresh = true; + + if (!refresh) + return true; + + if (key->mpb) { + if (!mpb_compose(key, ivi, kr, ivu)) + return false; + + print_packet("Set MPB to", key->mpb, 28); + } + + if (key->snb) { + if (!snb_compose(key, ivi, kr, ivu)) + return false; + + print_packet("Set SNB to", key->snb, 23); + } + + l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr); + + /* Propagate changes to all local nodes */ + net_local_beacon(id, ivi, ivu, kr); + + /* Send one new SNB soon, after all nodes have seen it */ + l_getrandom(&rand_ms, sizeof(rand_ms)); + rand_ms %= 1000; + key->observe.expected++; + + if (key->observe.timeout) + l_timeout_modify_ms(key->observe.timeout, 500 + rand_ms); + else + key->observe.timeout = l_timeout_create_ms(500 + rand_ms, + beacon_timeout, key, NULL); + + return true; +} + +static void mpb_timeout(struct l_timeout *timeout, void *user_data) +{ + struct net_key *key = user_data; + + if (key->mpb_refresh) { + l_debug("Refresh in %d seconds", key->mpb_refresh * 10); + l_timeout_modify(timeout, key->mpb_refresh * 10); + } + + net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); +} + +void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool enabled; @@ -440,8 +702,19 @@ void net_key_beacon_enable(uint32_t id) if (!key) return; - enabled = !!key->beacon_enables; - key->beacon_enables++; + enabled = !!key->snb_enables || !!key->mpb_enables; + + if (mpb) { + key->mpb_enables++; + key->mpb_refresh = refresh_count; + l_timeout_remove(key->mpb_to); + if (refresh_count) + key->mpb_to = l_timeout_create(refresh_count * 10, + mpb_timeout, key, NULL); + else + key->mpb_to = NULL; + } else + key->snb_enables++; /* If already Enabled, do nothing */ if (enabled) @@ -453,70 +726,68 @@ void net_key_beacon_enable(uint32_t id) rand_ms++; /* Enable Periodic Beaconing on this key */ - key->snb.observe_period = BEACON_INTERVAL_MIN * 2; - key->snb.expected = 2; - key->snb.observed = 0; - key->snb.half_period = true; - l_timeout_remove(key->snb.timeout); - key->snb.timeout = l_timeout_create_ms(rand_ms, snb_timeout, key, NULL); + key->observe.period = BEACON_INTERVAL_MIN * 2; + key->observe.expected = 2; + key->observe.seen = 0; + key->observe.half_period = true; + l_timeout_remove(key->observe.timeout); + key->observe.timeout = l_timeout_create_ms(rand_ms, beacon_timeout, + key, NULL); } -bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu) +void net_key_beacon_disable(uint32_t id, bool mpb) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); - uint8_t beacon[23]; - uint32_t rand_ms; if (!key) - return false; + return; - if (!net_key_snb_compose(id, iv_index, kr, ivu, beacon)) - return false; + if (mpb) { + if (!key->mpb_enables) + return; - if (memcmp(key->snb.beacon, beacon, sizeof(beacon))) - memcpy(key->snb.beacon, beacon, sizeof(beacon)); - else - return false; + key->mpb_enables--; - l_debug("Setting SNB: IVI: %8.8x, IVU: %d, KR: %d", iv_index, ivu, kr); - print_packet("Set SNB Beacon to", beacon, sizeof(beacon)); + if (!key->mpb_enables) { + l_free(key->mpb); + key->mpb = NULL; + l_timeout_remove(key->mpb_to); + key->mpb_to = NULL; + } + } else { + if (!key->snb_enables) + return; - /* Propagate changes to all local nodes */ - net_local_beacon(id, beacon); + key->snb_enables--; - /* Send one new SNB soon, after all nodes have seen it */ - l_getrandom(&rand_ms, sizeof(rand_ms)); - rand_ms %= 1000; - key->snb.expected++; + if (!key->snb_enables) { + l_free(key->snb); + key->snb = NULL; + } + } - if (key->snb.timeout) - l_timeout_modify_ms(key->snb.timeout, 500 + rand_ms); - else - key->snb.timeout = l_timeout_create_ms(500 + rand_ms, - snb_timeout, key, NULL); + if (key->snb_enables || key->mpb_enables) + return; - return true; + /* Disable periodic Beaconing on this key */ + l_timeout_remove(key->observe.timeout); + key->observe.timeout = NULL; } -void net_key_beacon_disable(uint32_t id) +static void free_key(void *data) { - struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); - - if (!key || !key->beacon_enables) - return; - - key->beacon_enables--; + struct net_key *key = data; - if (key->beacon_enables) - return; - - /* Disable periodic Beaconing on this key */ - l_timeout_remove(key->snb.timeout); - key->snb.timeout = NULL; + l_timeout_remove(key->mpb_to); + l_free(key->snb); + l_free(key->mpb); + l_free(key); } void net_key_cleanup(void) { - l_queue_destroy(keys, l_free); + l_queue_destroy(keys, free_key); keys = NULL; + l_queue_destroy(beacons, l_free); + beacons = NULL; } diff --git a/mesh/net-keys.h b/mesh/net-keys.h index 420618f71..a3909448c 100644 --- a/mesh/net-keys.h +++ b/mesh/net-keys.h @@ -9,6 +9,7 @@ */ #define BEACON_TYPE_SNB 0x01 +#define BEACON_TYPE_MPB 0x02 #define KEY_REFRESH 0x01 #define IV_INDEX_UPDATE 0x02 @@ -23,12 +24,15 @@ uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len); bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len); uint32_t net_key_network_id(const uint8_t network[8]); +uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, + bool *ivu, bool *kr); bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint64_t cmac); bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint8_t *snb); void net_key_beacon_seen(uint32_t id); -void net_key_beacon_enable(uint32_t id); -bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu); -void net_key_beacon_disable(uint32_t id); +bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu, + bool force); +void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count); +void net_key_beacon_disable(uint32_t id, bool mpb); uint32_t net_key_beacon_last_seen(uint32_t id); diff --git a/mesh/net.c b/mesh/net.c index 1d27289bf..81f1e57ee 100644 --- a/mesh/net.c +++ b/mesh/net.c @@ -102,7 +102,8 @@ struct mesh_net { unsigned int sar_id_next; bool friend_enable; - bool beacon_enable; + bool snb_enable; + bool mpb_enable; bool proxy_enable; bool friend_seq; struct l_timeout *iv_update_timeout; @@ -119,6 +120,7 @@ struct mesh_net { uint8_t chan; /* Channel of recent Rx */ uint8_t default_ttl; uint8_t tid; + uint8_t mpb_period; struct { bool enable; @@ -217,6 +219,7 @@ struct net_beacon_data { bool ivu; bool kr; bool processed; + bool local; }; static struct l_queue *fast_cache; @@ -526,6 +529,13 @@ static void mesh_sar_free(void *data) static void subnet_free(void *data) { struct mesh_subnet *subnet = data; + struct mesh_net *net = subnet->net; + + if (net->snb_enable) + net_key_beacon_disable(subnet->net_key_tx, false); + + if (net->mpb_enable) + net_key_beacon_disable(subnet->net_key_tx, true); net_key_unref(subnet->net_key_cur); net_key_unref(subnet->net_key_upd); @@ -545,15 +555,27 @@ static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx) return subnet; } -static void enable_beacon(void *a, void *b) +static void enable_snb(void *a, void *b) { struct mesh_subnet *subnet = a; struct mesh_net *net = b; - if (net->beacon_enable) - net_key_beacon_enable(subnet->net_key_tx); + if (net->snb_enable) + net_key_beacon_enable(subnet->net_key_tx, false, 0); else - net_key_beacon_disable(subnet->net_key_tx); + net_key_beacon_disable(subnet->net_key_tx, false); +} + +static void enable_mpb(void *a, void *b) +{ + struct mesh_subnet *subnet = a; + struct mesh_net *net = b; + + if (net->mpb_enable) + net_key_beacon_enable(subnet->net_key_tx, true, + net->mpb_period); + else + net_key_beacon_disable(subnet->net_key_tx, true); } static void enqueue_update(void *a, void *b); @@ -602,7 +624,8 @@ static void refresh_beacon(void *a, void *b) struct mesh_net *net = b; net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, - !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update); + !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update, + false); } struct mesh_net *mesh_net_new(struct mesh_node *node) @@ -826,7 +849,7 @@ int mesh_net_del_key(struct mesh_net *net, uint16_t idx) if (idx == net->hb_pub.net_idx) net->hb_pub.dst = UNASSIGNED_ADDRESS; - /* TODO: cancel beacon_enable on this subnet */ + /* TODO: cancel snb_enable on this subnet */ l_queue_remove(net->subnets, subnet); subnet_free(subnet); @@ -853,10 +876,14 @@ static struct mesh_subnet *add_key(struct mesh_net *net, uint16_t idx, } net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, - false, net->iv_update); + false, net->iv_update, false); - if (net->beacon_enable) - net_key_beacon_enable(subnet->net_key_tx); + if (net->snb_enable) + net_key_beacon_enable(subnet->net_key_tx, false, 0); + + if (net->mpb_enable) + net_key_beacon_enable(subnet->net_key_tx, true, + net->mpb_period); l_queue_push_tail(net->subnets, subnet); @@ -2794,83 +2821,111 @@ static void process_beacon(void *net_ptr, void *user_data) beacon_data->processed = true; /* - * Ignore the beacon if it doesn't change anything, unless we're - * doing IV Recovery + * Ignore local beacons and beacons that don't change anything, + * unless we're doing IV Recovery */ - if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index || + if (!beacon_data->local) { + if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index || ivu != net->iv_update) - updated |= update_iv_ivu_state(net, ivi, ivu); + updated |= update_iv_ivu_state(net, ivi, ivu); + + if (kr != local_kr) + updated |= update_kr_state(subnet, kr, + beacon_data->net_key_id); - if (kr != local_kr || beacon_data->net_key_id != subnet->net_key_cur) - updated |= update_kr_state(subnet, kr, beacon_data->net_key_id); - if (updated) - net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, + if (updated) + net_key_beacon_refresh(beacon_data->net_key_id, + net->iv_index, !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), - net->iv_update); + net->iv_update, false); + } } static void beacon_recv(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { struct net_beacon_data beacon_data = { + .local = false, .processed = false, }; - if (len != 23 || data[1] != 0x01) - return; + beacon_data.net_key_id = net_key_beacon(data, len, &beacon_data.ivi, + &beacon_data.ivu, &beacon_data.kr); - /* Ignore Network IDs unknown to this daemon */ - beacon_data.net_key_id = net_key_network_id(data + 3); if (!beacon_data.net_key_id) return; - /* Get data bits from beacon */ - beacon_data.ivu = !!(data[2] & 0x02); - beacon_data.kr = !!(data[2] & 0x01); - beacon_data.ivi = l_get_be32(data + 11); - - /* Validate beacon before accepting */ - if (!net_key_snb_check(beacon_data.net_key_id, beacon_data.ivi, - beacon_data.kr, beacon_data.ivu, - l_get_be64(data + 15))) { - l_error("mesh_crypto_beacon verify failed"); - return; - } - l_queue_foreach(nets, process_beacon, &beacon_data); if (beacon_data.processed) net_key_beacon_seen(beacon_data.net_key_id); } -void net_local_beacon(uint32_t net_key_id, uint8_t *beacon) +void net_local_beacon(uint32_t net_key_id, uint32_t ivi, bool ivu, bool kr) { struct net_beacon_data beacon_data = { + .local = true, + .processed = false, .net_key_id = net_key_id, - .ivu = !!(beacon[2] & 0x02), - .kr = !!(beacon[2] & 0x01), - .ivi = l_get_be32(beacon + 11), + .ivu = ivu, + .kr = kr, + .ivi = ivi, }; /* Deliver locally generated beacons to all nodes */ l_queue_foreach(nets, process_beacon, &beacon_data); } -bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable) +bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable) { if (!net) return false; - if (net->beacon_enable == enable) + if (net->snb_enable == enable) return true; - net->beacon_enable = enable; + net->snb_enable = enable; if (enable) l_queue_foreach(net->subnets, refresh_beacon, net); - l_queue_foreach(net->subnets, enable_beacon, net); + l_queue_foreach(net->subnets, enable_snb, net); + queue_friend_update(net); + + return true; +} + +bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enable, uint8_t period, + bool initialize) +{ + uint8_t old_period; + bool old_enable; + + if (!net) + return false; + + old_enable = net->mpb_enable; + old_period = net->mpb_period; + + if (enable) + net->mpb_period = period; + + if (old_enable == enable && old_period == net->mpb_period) + return true; + + if (enable && !initialize) { + /* If enable with different period, disable and re-enable */ + net->mpb_enable = false; + l_queue_foreach(net->subnets, enable_mpb, net); + } + + net->mpb_enable = enable; + + if (enable) + l_queue_foreach(net->subnets, refresh_beacon, net); + + l_queue_foreach(net->subnets, enable_mpb, net); queue_friend_update(net); return true; @@ -2908,17 +2963,25 @@ bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key, subnet->key_refresh = 1; subnet->net_key_tx = subnet->net_key_upd; - if (net->beacon_enable) { + if (net->snb_enable) { /* Switch beaconing key */ - net_key_beacon_disable(subnet->net_key_cur); - net_key_beacon_enable(subnet->net_key_upd); + net_key_beacon_disable(subnet->net_key_cur, false); + net_key_beacon_enable(subnet->net_key_upd, false, 0); + } + + if (net->mpb_enable) { + /* Switch beaconing key */ + net_key_beacon_disable(subnet->net_key_cur, true); + net_key_beacon_enable(subnet->net_key_upd, true, + net->mpb_period); } } subnet->kr_phase = phase; net_key_beacon_refresh(subnet->net_key_tx, net->iv_index, - !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update); + !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update, + false); return true; @@ -2933,8 +2996,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io) first = l_queue_isempty(nets); if (first) { - uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01}; - uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; + const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1}; + const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2}; + const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; if (!nets) nets = l_queue_new(); @@ -2944,6 +3008,8 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io) mesh_io_register_recv_cb(io, snb, sizeof(snb), beacon_recv, NULL); + mesh_io_register_recv_cb(io, mpb, sizeof(mpb), + beacon_recv, NULL); mesh_io_register_recv_cb(io, pkt, sizeof(pkt), net_msg_recv, NULL); } @@ -2960,8 +3026,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io) struct mesh_io *mesh_net_detach(struct mesh_net *net) { - uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01}; - uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; + const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1}; + const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2}; + const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK}; struct mesh_io *io; uint8_t type = 0; @@ -2975,6 +3042,7 @@ struct mesh_io *mesh_net_detach(struct mesh_net *net) /* Only deregister io if this is the last network detached.*/ if (l_queue_length(nets) < 2) { mesh_io_deregister_recv_cb(io, snb, sizeof(snb)); + mesh_io_deregister_recv_cb(io, mpb, sizeof(mpb)); mesh_io_deregister_recv_cb(io, pkt, sizeof(pkt)); } diff --git a/mesh/net.h b/mesh/net.h index 0bacbbbbf..d385ba16e 100644 --- a/mesh/net.h +++ b/mesh/net.h @@ -236,8 +236,10 @@ void mesh_net_set_frnd_seq(struct mesh_net *net, bool seq); uint16_t mesh_net_get_address(struct mesh_net *net); bool mesh_net_register_unicast(struct mesh_net *net, uint16_t unicast, uint8_t num_ele); -void net_local_beacon(uint32_t net_key_id, uint8_t *beacon); -bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable); +void net_local_beacon(uint32_t key_id, uint32_t ivi, bool ivu, bool kr); +bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable); +bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enabla, uint8_t period, + bool init); bool mesh_net_set_proxy_mode(struct mesh_net *net, bool enable); bool mesh_net_set_relay_mode(struct mesh_net *net, bool enable, uint8_t cnt, uint8_t interval); diff --git a/mesh/node.c b/mesh/node.c index 5150a085a..a2a330518 100644 --- a/mesh/node.c +++ b/mesh/node.c @@ -414,7 +414,7 @@ static void update_net_settings(struct mesh_node *node) mesh_net_set_relay_mode(net, node->relay.mode == MESH_MODE_ENABLED, node->relay.cnt, node->relay.interval); - mesh_net_set_beacon_mode(net, node->beacon == MESH_MODE_ENABLED); + mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED); } static bool init_from_storage(struct mesh_config_node *db_node, @@ -825,7 +825,7 @@ bool node_beacon_mode_set(struct mesh_node *node, bool enable) if (res) { node->beacon = beacon; - mesh_net_set_beacon_mode(node->net, enable); + mesh_net_set_snb_mode(node->net, enable); } return res; From patchwork Mon Jan 23 19:48:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112873 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F761C54EED for ; Mon, 23 Jan 2023 19:49:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230040AbjAWTtP (ORCPT ); Mon, 23 Jan 2023 14:49:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53716 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230088AbjAWTtG (ORCPT ); Mon, 23 Jan 2023 14:49:06 -0500 Received: from mail-pj1-x102d.google.com (mail-pj1-x102d.google.com [IPv6:2607:f8b0:4864:20::102d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CA04C366BF for ; Mon, 23 Jan 2023 11:48:51 -0800 (PST) Received: by mail-pj1-x102d.google.com with SMTP id x24-20020a17090ab01800b00229f43b506fso11259366pjq.5 for ; Mon, 23 Jan 2023 11:48:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UsNi4+xXo9Re2YmLXhdvMFlGFeCBOiSSV9tBKSsL3Qc=; b=BhQK4p5b4V8RpqTSTo++c4gsNI+ucP7hXd5q0T+fooyeibcbasHQ8WANERGxRJrF4U 9xBUHelujrif4YS6C7GJj1zZCdag9i7hM/AT6zOcN2cWJ7UYBD8DHQf5WLUXS/b5tdVy BQuljejnThhay13v+Cnk14+VJPA3Z6f+LZTEPWbnEa63dlztaCsacDJq/vDtd/Ej/I/G bpckO9tDwgAbbtuDlwXqjsrTRSTbLI/ySW1t1CspZXKvtX1ocMrIr67skwxIOILFe4rE NF9TzWFIhRuOCHPtpgWrjzugIfmkdLoFFO3ZBWekymJtG88QBgUfjNRI5PurbxTO5h3j N8rA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UsNi4+xXo9Re2YmLXhdvMFlGFeCBOiSSV9tBKSsL3Qc=; b=rne59xoDunVbDX2a3SVNdX3rx/YBabDi8qfQdqJ35ipKJ59kgMqwcgDC7IHMK1yC0K /acDW7UbzK1Qys6F1DjO7BQJmoBOTaft/4rCqtvN+CSH2x2O3FAakiMrafxESv18FSU9 hQuZCkTaJItNHU8H7jeJ+zZE121TPb6F9B+B4qdvQFdCAxN/f/LNEMyM+8RdsyZQa9tV cGJIY+nPEOKiOu2/HyziGrqDTGDfDetLOyqdWeUFAPVPKBAsCp6cB+T+7Wt7MpzQbcXW 5Zb82vD1xTfLayT2InD4FNUYcJsCBaUaFceJgv9Cz7A0edUGYw8SMyJnsRczJno/3tiU CvYQ== X-Gm-Message-State: AFqh2kpQyW6J2ER0IoHATCNkzG1YwXwNGsUxnnmBrhQsBuExtV02RP4N BJ/INKkkHU+eFD2aFtYToSj4vxrabBXrQw== X-Google-Smtp-Source: AMrXdXtgd/B8J3ykpfijzl13IscvYBVgyohOvi6HDaBzSREcWEcnk3/+egs1tF4ohtPl/bAwLR16dQ== X-Received: by 2002:a17:902:db0d:b0:195:6065:afa3 with SMTP id m13-20020a170902db0d00b001956065afa3mr18475694plx.44.1674503330678; Mon, 23 Jan 2023 11:48:50 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:50 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 09/11] mesh: Add internal Mesh Private Beacon model Date: Mon, 23 Jan 2023 11:48:22 -0800 Message-Id: <20230123194824.257351-10-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix Adds recgnition that the Mesh Private Beacon model is internal and foundational, without bindings. --- mesh/model.c | 10 ++++++- mesh/net-keys.c | 5 ++++ mesh/net-keys.h | 1 + mesh/node.c | 67 +++++++++++++++++++++++++++++++++++++++---- mesh/node.h | 2 ++ mesh/prvbeac-server.c | 11 +++++-- 6 files changed, 86 insertions(+), 10 deletions(-) diff --git a/mesh/model.c b/mesh/model.c index e2babea10..4ccafa17e 100644 --- a/mesh/model.c +++ b/mesh/model.c @@ -26,6 +26,7 @@ #include "mesh/cfgmod.h" #include "mesh/prov.h" #include "mesh/remprv.h" +#include "mesh/prv-beacon.h" #include "mesh/error.h" #include "mesh/dbus.h" #include "mesh/util.h" @@ -81,6 +82,9 @@ static bool is_internal(uint32_t id) if (id == REM_PROV_SRV_MODEL || id == REM_PROV_CLI_MODEL) return true; + if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL) + return true; + return false; } @@ -647,6 +651,9 @@ static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id, if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL) return MESH_STATUS_INVALID_MODEL; + if (id == PRV_BEACON_SRV_MODEL || id == PRV_BEACON_CLI_MODEL) + return MESH_STATUS_INVALID_MODEL; + if (!appkey_have_key(node_get_net(node), app_idx)) return MESH_STATUS_INVALID_APPKEY; @@ -1655,7 +1662,8 @@ static struct mesh_model *model_setup(struct mesh_net *net, uint8_t ele_idx, SET_ID(SIG_VENDOR, db_mod->id)); /* Implicitly bind config server model to device key */ - if (db_mod->id == CONFIG_SRV_MODEL) { + if (db_mod->id == CONFIG_SRV_MODEL || + db_mod->id == PRV_BEACON_SRV_MODEL) { if (ele_idx != PRIMARY_ELE_IDX) { l_free(mod); diff --git a/mesh/net-keys.c b/mesh/net-keys.c index 0ba051d79..57a9df04a 100644 --- a/mesh/net-keys.c +++ b/mesh/net-keys.c @@ -123,6 +123,7 @@ uint32_t net_key_add(const uint8_t flooding[16]) key = l_new(struct net_key, 1); memcpy(key->flooding, flooding, 16); key->ref_cnt++; + key->mpb_refresh = NET_MPB_REFRESH_DEFAULT; result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key, key->prv_key); if (!result) @@ -664,6 +665,10 @@ bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu, l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr); + key->ivi = ivi; + key->ivu = ivu; + key->kr = kr; + /* Propagate changes to all local nodes */ net_local_beacon(id, ivi, ivu, kr); diff --git a/mesh/net-keys.h b/mesh/net-keys.h index a3909448c..e73812481 100644 --- a/mesh/net-keys.h +++ b/mesh/net-keys.h @@ -12,6 +12,7 @@ #define BEACON_TYPE_MPB 0x02 #define KEY_REFRESH 0x01 #define IV_INDEX_UPDATE 0x02 +#define NET_MPB_REFRESH_DEFAULT 60 void net_key_cleanup(void); bool net_key_confirm(uint32_t id, const uint8_t flooding[16]); diff --git a/mesh/node.c b/mesh/node.c index a2a330518..ed3212685 100644 --- a/mesh/node.c +++ b/mesh/node.c @@ -32,6 +32,7 @@ #include "mesh/model.h" #include "mesh/cfgmod.h" #include "mesh/remprv.h" +#include "mesh/prv-beacon.h" #include "mesh/util.h" #include "mesh/error.h" #include "mesh/dbus.h" @@ -100,6 +101,8 @@ struct mesh_node { uint8_t proxy; uint8_t friend; uint8_t beacon; + uint8_t mpb; + uint8_t mpb_period; }; struct node_import { @@ -206,6 +209,8 @@ static void set_defaults(struct mesh_node *node) { node->lpn = MESH_MODE_UNSUPPORTED; node->proxy = MESH_MODE_UNSUPPORTED; + node->mpb = MESH_MODE_DISABLED; + node->mpb_period = NET_MPB_REFRESH_DEFAULT; node->friend = (mesh_friendship_supported()) ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED; node->beacon = (mesh_beacon_enabled()) ? MESH_MODE_ENABLED : @@ -403,7 +408,7 @@ static bool init_storage_dir(struct mesh_node *node) return rpl_init(node->storage_dir); } -static void update_net_settings(struct mesh_node *node) +static void init_net_settings(struct mesh_node *node) { struct mesh_net *net = node->net; @@ -415,6 +420,8 @@ static void update_net_settings(struct mesh_node *node) node->relay.cnt, node->relay.interval); mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED); + mesh_net_set_mpb_mode(net, node->mpb == MESH_MODE_ENABLED, + node->mpb_period, true); } static bool init_from_storage(struct mesh_config_node *db_node, @@ -442,6 +449,8 @@ static bool init_from_storage(struct mesh_config_node *db_node, node->relay.cnt = db_node->modes.relay.cnt; node->relay.interval = db_node->modes.relay.interval; node->beacon = db_node->modes.beacon; + node->mpb = db_node->modes.mpb; + node->mpb_period = db_node->modes.mpb_period; l_debug("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x", node->relay.mode, node->proxy, node->lpn, node->friend); @@ -495,7 +504,7 @@ static bool init_from_storage(struct mesh_config_node *db_node, mesh_net_set_seq_num(node->net, node->seq_number); mesh_net_set_default_ttl(node->net, node->ttl); - update_net_settings(node); + init_net_settings(node); /* Initialize configuration server model */ cfgmod_server_init(node, PRIMARY_ELE_IDX); @@ -504,6 +513,9 @@ static bool init_from_storage(struct mesh_config_node *db_node, remote_prov_server_init(node, PRIMARY_ELE_IDX); remote_prov_client_init(node, PRIMARY_ELE_IDX); + /* Initialize Private Beacon server model */ + prv_beacon_server_init(node, PRIMARY_ELE_IDX); + node->cfg = cfg; return true; @@ -839,6 +851,36 @@ uint8_t node_beacon_mode_get(struct mesh_node *node) return node->beacon; } +bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period) +{ + bool res; + uint8_t beacon; + + if (!node) + return false; + + beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED; + res = mesh_config_write_mpb(node->cfg, beacon, period); + + if (res) { + node->mpb = beacon; + node->mpb_period = period; + mesh_net_set_mpb_mode(node->net, enable, period, false); + } + + return res; +} + +uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period) +{ + if (!node) + return MESH_MODE_DISABLED; + + *period = node->mpb_period; + + return node->mpb; +} + bool node_friend_mode_set(struct mesh_node *node, bool enable) { bool res; @@ -951,6 +993,8 @@ static void convert_node_to_storage(struct mesh_node *node, db_node->modes.relay.cnt = node->relay.cnt; db_node->modes.relay.interval = node->relay.interval; db_node->modes.beacon = node->beacon; + db_node->modes.mpb = node->mpb; + db_node->modes.mpb_period = node->mpb_period; db_node->ttl = node->ttl; db_node->seq_number = node->seq_number; @@ -1173,9 +1217,16 @@ static bool get_sig_models_from_properties(struct mesh_node *node, while (l_dbus_message_iter_next_entry(&mods, &m_id, &var)) { uint32_t id = SET_ID(SIG_VENDOR, m_id); - /* Allow Config Server Model only on the primary element */ - if (ele->idx != PRIMARY_ELE_IDX && id == CONFIG_SRV_MODEL) - return false; + /* + * Allow Config Server & Private Beacon Models only on + * the primary element + */ + if (ele->idx != PRIMARY_ELE_IDX) { + if (id == CONFIG_SRV_MODEL) + return false; + if (id == PRV_BEACON_SRV_MODEL) + return false; + } if (!mesh_model_add(node, ele->models, id, &var)) return false; @@ -1278,6 +1329,7 @@ static bool get_element_properties(struct mesh_node *node, const char *path, */ if (ele->idx == PRIMARY_ELE_IDX) { mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL); + mesh_model_add(node, ele->models, PRV_BEACON_SRV_MODEL, NULL); mesh_model_add(node, ele->models, REM_PROV_SRV_MODEL, NULL); if (node->provisioner) mesh_model_add(node, ele->models, REM_PROV_CLI_MODEL, @@ -1397,13 +1449,16 @@ static bool add_local_node(struct mesh_node *node, uint16_t unicast, bool kr, l_queue_foreach(node->pages, save_pages, node); - update_net_settings(node); + init_net_settings(node); /* Initialize internal server models */ cfgmod_server_init(node, PRIMARY_ELE_IDX); remote_prov_server_init(node, PRIMARY_ELE_IDX); remote_prov_client_init(node, PRIMARY_ELE_IDX); + /* Initialize Private Beacon server model */ + prv_beacon_server_init(node, PRIMARY_ELE_IDX); + node->busy = true; return true; diff --git a/mesh/node.h b/mesh/node.h index a98945223..4f31c5056 100644 --- a/mesh/node.h +++ b/mesh/node.h @@ -63,6 +63,8 @@ uint8_t node_relay_mode_get(struct mesh_node *node, uint8_t *cnt, bool node_proxy_mode_set(struct mesh_node *node, bool enable); uint8_t node_proxy_mode_get(struct mesh_node *node); bool node_beacon_mode_set(struct mesh_node *node, bool enable); +bool node_mpb_mode_set(struct mesh_node *node, bool enable, uint8_t period); +uint8_t node_mpb_mode_get(struct mesh_node *node, uint8_t *period); uint8_t node_beacon_mode_get(struct mesh_node *node); bool node_friend_mode_set(struct mesh_node *node, bool enable); uint8_t node_friend_mode_get(struct mesh_node *node); diff --git a/mesh/prvbeac-server.c b/mesh/prvbeac-server.c index f3a6eaa82..a55e130d2 100644 --- a/mesh/prvbeac-server.c +++ b/mesh/prvbeac-server.c @@ -43,7 +43,7 @@ static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, uint32_t opcode; uint8_t msg[5]; uint16_t n; - uint8_t period = 0; + uint8_t period; if (app_idx != APP_IDX_DEV_LOCAL) return false; @@ -65,18 +65,23 @@ static bool prvbec_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, case OP_PRIVATE_BEACON_SET: if (size == 1) - period = 0xff; + node_mpb_mode_get(node, &period); else if (size == 2) period = pkt[1]; else return true; + if (pkt[0] > 1) + return true; + + node_mpb_mode_set(node, !!pkt[0], period); + /* fallthrough */ case OP_PRIVATE_BEACON_GET: n = mesh_model_opcode_set(OP_PRIVATE_BEACON_STATUS, msg); - msg[n++] = NOT_SUPPORTED; + msg[n++] = node_mpb_mode_get(node, &period); msg[n++] = period; l_debug("Get/Set Private Beacon (%d)", msg[n-2]); From patchwork Mon Jan 23 19:48:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112876 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43BAFC25B50 for ; Mon, 23 Jan 2023 19:49:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232762AbjAWTtQ (ORCPT ); Mon, 23 Jan 2023 14:49:16 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53654 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231760AbjAWTtI (ORCPT ); Mon, 23 Jan 2023 14:49:08 -0500 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5CDFD367C0 for ; Mon, 23 Jan 2023 11:48:52 -0800 (PST) Received: by mail-pj1-x1034.google.com with SMTP id m3-20020a17090a414300b00229ef93c5b0so11296575pjg.2 for ; Mon, 23 Jan 2023 11:48:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TH46ocFCk8kezNiIY8wxOx17wU4aswIas0eNpONaepE=; b=ZRZCqsOG+7JlGMEClpfnq7zT8ppU36owyE2WTa4zq3UT8CB0zdRR5Nq7VIJfRnooBh cgCeWLvmDO+VYKNaLmk2FN5TgiuuhZ59uRnDi9tFzgCB/lEpC+JMjSU7w/+NyV61QCyX 9VNpxiHAYR4lsx7PdM9rGeo7ekBUt7G7Y0yKViVTzFG2uePY+cZdUaWRB/59kqspYb/7 uqT2fVqXpUjctnWqlY7dA4zmQ8T/tP/zkmIK51xpv+GZjYjBV9MubY+1S6GYGjmRzKnp 9fhoiEvPok9pvBjypLc1honkfZaYmC0+CD294Ds8Xfz9xMOOAbHEEdljO4ZzXsgI352M mMEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TH46ocFCk8kezNiIY8wxOx17wU4aswIas0eNpONaepE=; b=1OoyMcykaHM/y5NrC7vsmwkxqpviw5Nh78AM/zl+3UiXRDW897/RgqkkzCYrk55Sbn pAk6EZHmgyFNZktqDcnxcKLVngkHSdZv9NUjDVGrIVYiY1VJcs5WojHKEIZhHXV4Jxbt jsytoQ4zBZT+ODrKKxpUvurzQHOBvdOpM69Ij2q+W6GX0VzKqAOUz5RcZX6PY/RzJULG oMgB/+voUtdFnTG9lIfF2yetHc4q9huAmlsQEAY5mxcMdDs36r1KLRwChI/5ofMczBBs t9za5Zqw3nu44Pd0hJcjuVyWk4soHtwzoA7AuDdxBTUcKCisGz1gU5vPT5ARsjrX2pkc 6SEA== X-Gm-Message-State: AFqh2kpbuJJBHI4eD7y6PxJq/0RZNG9sEHvT6JhRooRbt22c/kcg3GnN v3LMo/Dm/Nx0sf/SI0iJR3dP2KLGcZo5vA== X-Google-Smtp-Source: AMrXdXtyGGdwFdzvBah1Hh79uh2H4PjXMjV1C4zPefStLcnLrVxsy1LoppCkrzaVfTSR/syJ131WgA== X-Received: by 2002:a17:902:b702:b0:186:8568:be7e with SMTP id d2-20020a170902b70200b001868568be7emr27276156pls.15.1674503331404; Mon, 23 Jan 2023 11:48:51 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:51 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 10/11] tools/mesh: Add support for Mesh Private Beacons Date: Mon, 23 Jan 2023 11:48:23 -0800 Message-Id: <20230123194824.257351-11-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix This allows generation of Mesh Private Beacon server commands, and recognition of the resulting Status messages. --- tools/mesh-cfgclient.c | 9 ++++-- tools/mesh/cfgcli.c | 62 +++++++++++++++++++++++++++++++++++------- tools/mesh/util.c | 3 ++ 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/tools/mesh-cfgclient.c b/tools/mesh-cfgclient.c index f3e9af8fb..50be82bcf 100644 --- a/tools/mesh-cfgclient.c +++ b/tools/mesh-cfgclient.c @@ -44,6 +44,8 @@ #define CFG_SRV_MODEL 0x0000 #define CFG_CLI_MODEL 0x0001 #define RPR_SVR_MODEL 0xFFFF0004 +#define PRV_BEACON_SVR 0x0008 +#define PRV_BEACON_CLI 0x0009 #define UNPROV_SCAN_MAX_SECS 300 @@ -58,7 +60,7 @@ struct meshcfg_el { const char *path; uint8_t index; - uint16_t mods[2]; + uint16_t mods[4]; }; struct meshcfg_app { @@ -143,7 +145,8 @@ static struct meshcfg_app app = { .ele = { .path = "/mesh/cfgclient/ele0", .index = 0, - .mods = {CFG_SRV_MODEL, CFG_CLI_MODEL} + .mods = {CFG_SRV_MODEL, CFG_CLI_MODEL, + PRV_BEACON_SVR, PRV_BEACON_CLI} } }; @@ -1889,6 +1892,8 @@ static bool mod_getter(struct l_dbus *dbus, l_dbus_message_builder_enter_array(builder, "(qa{sv})"); build_model(builder, app.ele.mods[0], false, false); build_model(builder, app.ele.mods[1], false, false); + build_model(builder, app.ele.mods[2], false, false); + build_model(builder, app.ele.mods[3], false, false); l_dbus_message_builder_leave_array(builder); return true; diff --git a/tools/mesh/cfgcli.c b/tools/mesh/cfgcli.c index ad572f694..4f6248e48 100644 --- a/tools/mesh/cfgcli.c +++ b/tools/mesh/cfgcli.c @@ -21,6 +21,7 @@ #include "src/shared/util.h" #include "mesh/mesh-defs.h" +#include "mesh/prv-beacon.h" #include "mesh/util.h" #include "mesh/crypto.h" @@ -73,9 +74,12 @@ static struct cfg_cmd cmds[] = { { OP_APPKEY_UPDATE, OP_APPKEY_STATUS, "AppKeyUpdate" }, { OP_DEV_COMP_GET, OP_DEV_COMP_STATUS, "DeviceCompositionGet" }, { OP_DEV_COMP_STATUS, NO_RESPONSE, "DeviceCompositionStatus" }, - { OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "BeaconGet" }, - { OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "BeaconSet" }, - { OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "BeaconStatus" }, + { OP_CONFIG_BEACON_GET, OP_CONFIG_BEACON_STATUS, "SNBGet" }, + { OP_CONFIG_BEACON_SET, OP_CONFIG_BEACON_STATUS, "SNBSet" }, + { OP_CONFIG_BEACON_STATUS, NO_RESPONSE, "SNBStatus" }, + { OP_PRIVATE_BEACON_GET, OP_PRIVATE_BEACON_STATUS, "MPBGet" }, + { OP_PRIVATE_BEACON_SET, OP_PRIVATE_BEACON_STATUS, "MPBSet" }, + { OP_PRIVATE_BEACON_STATUS, NO_RESPONSE, "MPBStatus" }, { OP_CONFIG_DEFAULT_TTL_GET, OP_CONFIG_DEFAULT_TTL_STATUS, "DefaultTTLGet" }, { OP_CONFIG_DEFAULT_TTL_SET, OP_CONFIG_DEFAULT_TTL_STATUS, @@ -617,12 +621,20 @@ static bool msg_recvd(uint16_t src, uint16_t idx, uint8_t *data, if (len != 1) return true; - bt_shell_printf("Node %4.4x: Config Beacon Status 0x%02x\n", + bt_shell_printf("Node %4.4x: SecBeacon Status 0x%02x\n", src, data[0]); saved = mesh_db_node_set_beacon(src, data[0] != 0); break; + case OP_PRIVATE_BEACON_STATUS: + if (len != 2) + return true; + + bt_shell_printf("Node %4.4x: PrivBeacon Status 0x%02x 0x%02x\n", + src, data[0], data[1]); + break; + case OP_CONFIG_RELAY_STATUS: if (len != 2) return true; @@ -1345,7 +1357,7 @@ static void cmd_del_binding(int argc, char *argv[]) cmd_bind(OP_MODEL_APP_UNBIND, argc, argv); } -static void cmd_beacon_set(int argc, char *argv[]) +static void cmd_snb_set(int argc, char *argv[]) { uint16_t n; uint8_t msg[2 + 1]; @@ -1367,11 +1379,41 @@ static void cmd_beacon_set(int argc, char *argv[]) return bt_shell_noninteractive_quit(EXIT_SUCCESS); } -static void cmd_beacon_get(int argc, char *argv[]) +static void cmd_mpb_set(int argc, char *argv[]) +{ + uint16_t n; + uint8_t msg[2 + 2]; + uint32_t parm_cnt; + + n = mesh_opcode_set(OP_PRIVATE_BEACON_SET, msg); + + parm_cnt = read_input_parameters(argc, argv); + if (parm_cnt != 1 && parm_cnt != 2) { + bt_shell_printf("bad arguments\n"); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + + msg[n++] = parms[0]; + + if (parm_cnt == 2) + msg[n++] = parms[1]; + + if (!config_send(msg, n, OP_PRIVATE_BEACON_SET)) + return bt_shell_noninteractive_quit(EXIT_FAILURE); + + return bt_shell_noninteractive_quit(EXIT_SUCCESS); +} + +static void cmd_snb_get(int argc, char *argv[]) { cmd_default(OP_CONFIG_BEACON_GET); } +static void cmd_mpb_get(int argc, char *argv[]) +{ + cmd_default(OP_PRIVATE_BEACON_GET); +} + static void cmd_ident_set(int argc, char *argv[]) { uint16_t n; @@ -2077,10 +2119,10 @@ static const struct bt_shell_menu cfg_menu = { "Set node identity state"}, {"ident-get", "", cmd_ident_get, "Get node identity state"}, - {"beacon-set", "", cmd_beacon_set, - "Set node identity state"}, - {"beacon-get", NULL, cmd_beacon_get, - "Get node beacon state"}, + {"snb-set", "", cmd_snb_set, "Set node SNB state"}, + {"snb-get", NULL, cmd_snb_get, "Get node SNB state"}, + {"mpb-set", " ", cmd_mpb_set, "Set node MPB state"}, + {"mpb-get", NULL, cmd_mpb_get, "Get node MPB state"}, {"relay-set", " ", cmd_relay_set, "Set relay"}, {"relay-get", NULL, cmd_relay_get, diff --git a/tools/mesh/util.c b/tools/mesh/util.c index 29641439f..d8c47c0e9 100644 --- a/tools/mesh/util.c +++ b/tools/mesh/util.c @@ -20,6 +20,7 @@ #include "src/shared/util.h" #include "mesh/mesh-defs.h" +#include "mesh/prv-beacon.h" #include "tools/mesh/util.h" @@ -137,6 +138,8 @@ const char *sig_model_string(uint16_t sig_model_id) case 0x0001: return "Configuration Client"; case 0x0002: return "Health Server"; case 0x0003: return "Health Client"; + case 0x0008: return "Private Beacon Server"; + case 0x0009: return "Private Beacon Client"; case 0x1000: return "Generic OnOff Server"; case 0x1001: return "Generic OnOff Client"; case 0x1002: return "Generic Level Server"; From patchwork Mon Jan 23 19:48:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brian Gix X-Patchwork-Id: 13112875 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D011BC38142 for ; Mon, 23 Jan 2023 19:49:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232322AbjAWTtS (ORCPT ); Mon, 23 Jan 2023 14:49:18 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232283AbjAWTtJ (ORCPT ); Mon, 23 Jan 2023 14:49:09 -0500 Received: from mail-pl1-x62e.google.com (mail-pl1-x62e.google.com [IPv6:2607:f8b0:4864:20::62e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 40BAF36476 for ; Mon, 23 Jan 2023 11:48:53 -0800 (PST) Received: by mail-pl1-x62e.google.com with SMTP id jl3so12504942plb.8 for ; Mon, 23 Jan 2023 11:48:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DtMvIzjwni9e7pW6GiJL02ZOc+4ONpLeTG01hI6pPpI=; b=oxoxfFvmOxwXKw6t9QBTXMBKDUQQcHnQuFvKsgWMAt+00syjgXL76P+7FXcKIfBt5u dsl52iuYQ/TQHmprD7kL0G4RdT4e514LbVzriMo25rE71etAxsSblIDYksdmFq+o0iVE +ierEHOB+gCxsx7qaOvNKhrqO/Lv9o+sySaeZC0mUsxn+DWIIvQ2Ckh3ydKFk9SU/Tjw oGyUFU9kwy+aFF0HRr5U+yRZ5QBWw29We8CevyrMBJV7jbAHiMY46rJOHcuqh3usba1V pg+iQ37AIpt9Q9W/dtnx17O9sXPyQ8c7PySgxlyr3uCd2I2DHT7sMGFbbttTkZaqFNwI SCyg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DtMvIzjwni9e7pW6GiJL02ZOc+4ONpLeTG01hI6pPpI=; b=nPTjs/G/oCl1npCsJT6ZAFlcLgDbSVBKHhdfCGTgCCUCnyGlJ+8wBU6b8oSqIVYTS/ KWVZqVqrNWMe12bvMKiaPv5lR47pdhe71jigTPFeXjiF0yX1ghfhTmKg1JBz1kBKBLU/ CcgvHIv0KJBe6NgFr/4sDr6a6GxnavTtP+yO3P1dWslVkVY3I2Z+kQzwQ3JBS/+WK3kJ wt/ytT/KgUA06lheWt6ZPAUnv3igj4McSrQdypQhMfJBBL7//KslvUti3uAfdMdPblph D8tJGXPwl8ZTl2XDKY6Gmb9dKci0FAqDPrK5lSfB062mwOQo9VNGT1bpyd3JN/cqzWfV nFPQ== X-Gm-Message-State: AFqh2ko24DI1lUmc5U1cN6u1nWsfSQlOpMjfantPELpmLJitWTyVBRJp XtUPFpDPlV61W96PRqCcNFNZIFhPfsBWug== X-Google-Smtp-Source: AMrXdXvpAGQXjRGDYD3C0c+2gUdS+eZnrvMAWwtfJYFoATPr+tgEO+esHGw2f8cT2UYbRwcJZapkCQ== X-Received: by 2002:a17:902:aa05:b0:193:335a:989e with SMTP id be5-20020a170902aa0500b00193335a989emr26833067plb.25.1674503332275; Mon, 23 Jan 2023 11:48:52 -0800 (PST) Received: from fedora.. (97-126-124-199.tukw.qwest.net. [97.126.124.199]) by smtp.gmail.com with ESMTPSA id jj5-20020a170903048500b0017d97d13b18sm97068plb.65.2023.01.23.11.48.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Jan 2023 11:48:51 -0800 (PST) From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: brian.gix@intel.com Subject: [PATCH BlueZ v3 11/11] mesh: Switch beaconing net key Date: Mon, 23 Jan 2023 11:48:24 -0800 Message-Id: <20230123194824.257351-12-brian.gix@gmail.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230123194824.257351-1-brian.gix@gmail.com> References: <20230123194824.257351-1-brian.gix@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Brian Gix When transitioning from Phase 1 to Phase 2 of a network key and we are beaconing, we need to halt the beaconing on the old key version, and begin beaconing on the new key version. --- mesh/net.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mesh/net.c b/mesh/net.c index 81f1e57ee..05ca48326 100644 --- a/mesh/net.c +++ b/mesh/net.c @@ -2625,6 +2625,13 @@ static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx) if (subnet->kr_phase == KEY_REFRESH_PHASE_TWO) return MESH_STATUS_SUCCESS; + /* Stop beaconing on old key */ + if (net->snb_enable) + net_key_beacon_disable(subnet->net_key_tx, false); + + if (net->mpb_enable) + net_key_beacon_disable(subnet->net_key_tx, true); + subnet->key_refresh = 1; subnet->net_key_tx = subnet->net_key_upd; /* @@ -2632,6 +2639,15 @@ static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx) * it hears beacons from all the nodes */ subnet->kr_phase = KEY_REFRESH_PHASE_TWO; + + /* Start beaconing on new key */ + if (net->snb_enable) + net_key_beacon_enable(subnet->net_key_tx, false, 0); + + if (net->mpb_enable) + net_key_beacon_enable(subnet->net_key_tx, true, + net->mpb_period); + refresh_beacon(subnet, net); queue_friend_update(net);