Bluetooth: Implement mgmt_load_irks command
authorJohan Hedberg <johan.hedberg@intel.com>
Tue, 18 Feb 2014 08:19:35 +0000 (10:19 +0200)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 18 Feb 2014 08:47:03 +0000 (00:47 -0800)
This patch implements the Load IRKs command for the management
interface. The command is used to load the kernel with the initial set
of IRKs. It also sets a HCI_RPA_RESOLVING flag to indicate that we can
start requesting devices to distribute their IRK to us.

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

index 352d3d7d06bb54d95bce32871c9ffa30f9bf913b..d3a8fff50f6947111864e6d691c40f50dabbc7b8 100644 (file)
@@ -125,6 +125,7 @@ enum {
        HCI_SSP_ENABLED,
        HCI_SC_ENABLED,
        HCI_SC_ONLY,
+       HCI_RPA_RESOLVING,
        HCI_HS_ENABLED,
        HCI_LE_ENABLED,
        HCI_ADVERTISING,
index 4303fa90b7c111d2c0efc4c046b8bf093a6b1ef5..e4fa13e559e2f91fd98b609b9ad70cba15bf74db 100644 (file)
@@ -389,6 +389,18 @@ struct mgmt_cp_set_scan_params {
 
 #define MGMT_OP_SET_DEBUG_KEYS         0x002E
 
+struct mgmt_irk_info {
+       struct mgmt_addr_info addr;
+       __u8 val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_IRKS              0x0030
+struct mgmt_cp_load_irks {
+       __le16 irk_count;
+       struct mgmt_irk_info irks[0];
+} __packed;
+#define MGMT_LOAD_IRKS_SIZE            2
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index 70bef3d5db579a741e1d2fbd11e39657245ae25f..782e2bb1088196926e835c5055fee37996576383 100644 (file)
@@ -81,6 +81,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_SCAN_PARAMS,
        MGMT_OP_SET_SECURE_CONN,
        MGMT_OP_SET_DEBUG_KEYS,
+       MGMT_OP_LOAD_IRKS,
 };
 
 static const u16 mgmt_events[] = {
@@ -4158,6 +4159,82 @@ unlock:
        return err;
 }
 
+static bool irk_is_valid(struct mgmt_irk_info *irk)
+{
+       switch (irk->addr.type) {
+       case BDADDR_LE_PUBLIC:
+               return true;
+
+       case BDADDR_LE_RANDOM:
+               /* Two most significant bits shall be set */
+               if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+                       return false;
+               return true;
+       }
+
+       return false;
+}
+
+static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
+                    u16 len)
+{
+       struct mgmt_cp_load_irks *cp = cp_data;
+       u16 irk_count, expected_len;
+       int i, err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       irk_count = __le16_to_cpu(cp->irk_count);
+
+       expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
+       if (expected_len != len) {
+               BT_ERR("load_irks: expected %u bytes, got %u bytes",
+                      len, expected_len);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       BT_DBG("%s irk_count %u", hdev->name, irk_count);
+
+       for (i = 0; i < irk_count; i++) {
+               struct mgmt_irk_info *key = &cp->irks[i];
+
+               if (!irk_is_valid(key))
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_LOAD_IRKS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       hci_dev_lock(hdev);
+
+       hci_smp_irks_clear(hdev);
+
+       for (i = 0; i < irk_count; i++) {
+               struct mgmt_irk_info *irk = &cp->irks[i];
+               u8 addr_type;
+
+               if (irk->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+
+               hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val,
+                           BDADDR_ANY);
+       }
+
+       set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
 static bool ltk_is_valid(struct mgmt_ltk_info *key)
 {
        if (key->master != 0x00 && key->master != 0x01)
@@ -4296,6 +4373,8 @@ static const struct mgmt_handler {
        { set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
        { set_secure_conn,        false, MGMT_SETTING_SIZE },
        { set_debug_keys,         false, MGMT_SETTING_SIZE },
+       { },
+       { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
 };