Bluetooth: Add support for limited privacy mode
authorJohan Hedberg <johan.hedberg@intel.com>
Wed, 9 Mar 2016 15:30:34 +0000 (17:30 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Thu, 10 Mar 2016 18:51:30 +0000 (19:51 +0100)
Introduce a limited privacy mode indicated by value 0x02 to the mgmt
Set Privacy command.

With value 0x02 the kernel will use privacy mode with a resolvable
private address. In case the controller is bondable and discoverable
the identity address will be used.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_request.c
net/bluetooth/hci_request.h
net/bluetooth/mgmt.c

index 339ea57be4230c97fb51fa902eb43dc2d21fc13c..5d38d980b89ddd512de9547f0e7aff10b58446f2 100644 (file)
@@ -233,6 +233,7 @@ enum {
        HCI_SC_ENABLED,
        HCI_SC_ONLY,
        HCI_PRIVACY,
+       HCI_LIMITED_PRIVACY,
        HCI_RPA_EXPIRED,
        HCI_RPA_RESOLVING,
        HCI_HS_ENABLED,
index 7264025dc78130d1665a15c584e57425edf61b3d..bf9f8a801a2e998bfe69bb5ca4daef061fbf83b1 100644 (file)
@@ -719,6 +719,13 @@ done:
        hci_dev_unlock(hdev);
 }
 
+static bool conn_use_rpa(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       return hci_dev_test_flag(hdev, HCI_PRIVACY);
+}
+
 static void hci_req_add_le_create_conn(struct hci_request *req,
                                       struct hci_conn *conn)
 {
@@ -729,7 +736,8 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
        /* Update random address, but set require_privacy to false so
         * that we never connect with an non-resolvable address.
         */
-       if (hci_update_random_address(req, false, &own_addr_type))
+       if (hci_update_random_address(req, false, conn_use_rpa(conn),
+                                     &own_addr_type))
                return;
 
        memset(&cp, 0, sizeof(cp));
@@ -774,7 +782,8 @@ static void hci_req_directed_advertising(struct hci_request *req,
        /* Set require_privacy to false so that the remote device has a
         * chance of identifying us.
         */
-       if (hci_update_random_address(req, false, &own_addr_type) < 0)
+       if (hci_update_random_address(req, false, conn_use_rpa(conn),
+                                     &own_addr_type) < 0)
                return;
 
        memset(&cp, 0, sizeof(cp));
index 77be344efd18190b612953d44381adbbf5d78b01..95a545ca9dbccb7faa8e9c3771a652382a3c670a 100644 (file)
@@ -771,6 +771,11 @@ static u8 update_white_list(struct hci_request *req)
        return 0x01;
 }
 
+static bool scan_use_rpa(struct hci_dev *hdev)
+{
+       return hci_dev_test_flag(hdev, HCI_PRIVACY);
+}
+
 void hci_req_add_le_passive_scan(struct hci_request *req)
 {
        struct hci_cp_le_set_scan_param param_cp;
@@ -785,7 +790,8 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
         * advertising with our address will be correctly reported
         * by the controller.
         */
-       if (hci_update_random_address(req, false, &own_addr_type))
+       if (hci_update_random_address(req, false, scan_use_rpa(hdev),
+                                     &own_addr_type))
                return;
 
        /* Adding or removing entries from the white list must
@@ -881,6 +887,29 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
        return adv_instance->flags;
 }
 
+static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags)
+{
+       /* If privacy is not enabled don't use RPA */
+       if (!hci_dev_test_flag(hdev, HCI_PRIVACY))
+               return false;
+
+       /* If basic privacy mode is enabled use RPA */
+       if (!hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
+               return true;
+
+       /* If limited privacy mode is enabled don't use RPA if we're
+        * both discoverable and bondable.
+        */
+       if ((flags & MGMT_ADV_FLAG_DISCOV) &&
+           hci_dev_test_flag(hdev, HCI_BONDABLE))
+               return false;
+
+       /* We're neither bondable nor discoverable in the limited
+        * privacy mode, therefore use RPA.
+        */
+       return true;
+}
+
 void __hci_req_enable_advertising(struct hci_request *req)
 {
        struct hci_dev *hdev = req->hdev;
@@ -914,7 +943,9 @@ void __hci_req_enable_advertising(struct hci_request *req)
         * advertising is used. In that case it is fine to use a
         * non-resolvable private address.
         */
-       if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
+       if (hci_update_random_address(req, !connectable,
+                                     adv_use_rpa(hdev, flags),
+                                     &own_addr_type) < 0)
                return;
 
        memset(&cp, 0, sizeof(cp));
@@ -1328,7 +1359,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
 }
 
 int hci_update_random_address(struct hci_request *req, bool require_privacy,
-                             u8 *own_addr_type)
+                             bool use_rpa, u8 *own_addr_type)
 {
        struct hci_dev *hdev = req->hdev;
        int err;
@@ -1337,7 +1368,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
         * current RPA has expired or there is something else than
         * the current RPA in use, then generate a new one.
         */
-       if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
+       if (use_rpa) {
                int to;
 
                *own_addr_type = ADDR_LE_DEV_RANDOM;
@@ -1599,9 +1630,16 @@ static int discoverable_update(struct hci_request *req, unsigned long opt)
        /* Advertising instances don't use the global discoverable setting, so
         * only update AD if advertising was enabled using Set Advertising.
         */
-       if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+       if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
                __hci_req_update_adv_data(req, 0x00);
 
+               /* Discoverable mode affects the local advertising
+                * address in limited privacy mode.
+                */
+               if (hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
+                       __hci_req_enable_advertising(req);
+       }
+
        hci_dev_unlock(hdev);
 
        return 0;
@@ -1944,7 +1982,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
         * address (when privacy feature has been enabled) or non-resolvable
         * private address.
         */
-       err = hci_update_random_address(req, true, &own_addr_type);
+       err = hci_update_random_address(req, true, scan_use_rpa(hdev),
+                                       &own_addr_type);
        if (err < 0)
                own_addr_type = ADDR_LE_DEV_PUBLIC;
 
index 64ff8c040d50f469144df19134c53f99e9ea4d66..b2d044bdc732f9ba47f22557459e69e1ec6e5654 100644 (file)
@@ -89,7 +89,7 @@ static inline void hci_req_update_scan(struct hci_dev *hdev)
 void __hci_req_update_scan(struct hci_request *req);
 
 int hci_update_random_address(struct hci_request *req, bool require_privacy,
-                             u8 *own_addr_type);
+                             bool use_rpa, u8 *own_addr_type);
 
 int hci_abort_conn(struct hci_conn *conn, u8 reason);
 void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
index 5a5089cb6570a33d0adf91565a536172ae84301e..2ca355519d797d23c26bee3efeed0b800417ec1e 100644 (file)
@@ -1382,8 +1382,19 @@ static int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (err < 0)
                goto unlock;
 
-       if (changed)
+       if (changed) {
+               /* In limited privacy mode the change of bondable mode
+                * may affect the local advertising address.
+                */
+               if (hdev_is_powered(hdev) &&
+                   hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+                   hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
+                   hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY))
+                       queue_work(hdev->req_workqueue,
+                                  &hdev->discoverable_update);
+
                err = new_settings(hdev, sk);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -4423,7 +4434,7 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
                                       MGMT_STATUS_NOT_SUPPORTED);
 
-       if (cp->privacy != 0x00 && cp->privacy != 0x01)
+       if (cp->privacy != 0x00 && cp->privacy != 0x01 && cp->privacy != 0x02)
                return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY,
                                       MGMT_STATUS_INVALID_PARAMS);
 
@@ -4442,10 +4453,15 @@ static int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                changed = !hci_dev_test_and_set_flag(hdev, HCI_PRIVACY);
                memcpy(hdev->irk, cp->irk, sizeof(hdev->irk));
                hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
+               if (cp->privacy == 0x02)
+                       hci_dev_set_flag(hdev, HCI_LIMITED_PRIVACY);
+               else
+                       hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY);
        } else {
                changed = hci_dev_test_and_clear_flag(hdev, HCI_PRIVACY);
                memset(hdev->irk, 0, sizeof(hdev->irk));
                hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED);
+               hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY);
        }
 
        err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev);
This page took 0.0865 seconds and 5 git commands to generate.