diff options
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 70bef3d5db57..782e2bb10881 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -81,6 +81,7 @@ static const u16 mgmt_commands[] = { | |||
81 | MGMT_OP_SET_SCAN_PARAMS, | 81 | MGMT_OP_SET_SCAN_PARAMS, |
82 | MGMT_OP_SET_SECURE_CONN, | 82 | MGMT_OP_SET_SECURE_CONN, |
83 | MGMT_OP_SET_DEBUG_KEYS, | 83 | MGMT_OP_SET_DEBUG_KEYS, |
84 | MGMT_OP_LOAD_IRKS, | ||
84 | }; | 85 | }; |
85 | 86 | ||
86 | static const u16 mgmt_events[] = { | 87 | static const u16 mgmt_events[] = { |
@@ -4158,6 +4159,82 @@ unlock: | |||
4158 | return err; | 4159 | return err; |
4159 | } | 4160 | } |
4160 | 4161 | ||
4162 | static bool irk_is_valid(struct mgmt_irk_info *irk) | ||
4163 | { | ||
4164 | switch (irk->addr.type) { | ||
4165 | case BDADDR_LE_PUBLIC: | ||
4166 | return true; | ||
4167 | |||
4168 | case BDADDR_LE_RANDOM: | ||
4169 | /* Two most significant bits shall be set */ | ||
4170 | if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0) | ||
4171 | return false; | ||
4172 | return true; | ||
4173 | } | ||
4174 | |||
4175 | return false; | ||
4176 | } | ||
4177 | |||
4178 | static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, | ||
4179 | u16 len) | ||
4180 | { | ||
4181 | struct mgmt_cp_load_irks *cp = cp_data; | ||
4182 | u16 irk_count, expected_len; | ||
4183 | int i, err; | ||
4184 | |||
4185 | BT_DBG("request for %s", hdev->name); | ||
4186 | |||
4187 | if (!lmp_le_capable(hdev)) | ||
4188 | return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, | ||
4189 | MGMT_STATUS_NOT_SUPPORTED); | ||
4190 | |||
4191 | irk_count = __le16_to_cpu(cp->irk_count); | ||
4192 | |||
4193 | expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info); | ||
4194 | if (expected_len != len) { | ||
4195 | BT_ERR("load_irks: expected %u bytes, got %u bytes", | ||
4196 | len, expected_len); | ||
4197 | return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, | ||
4198 | MGMT_STATUS_INVALID_PARAMS); | ||
4199 | } | ||
4200 | |||
4201 | BT_DBG("%s irk_count %u", hdev->name, irk_count); | ||
4202 | |||
4203 | for (i = 0; i < irk_count; i++) { | ||
4204 | struct mgmt_irk_info *key = &cp->irks[i]; | ||
4205 | |||
4206 | if (!irk_is_valid(key)) | ||
4207 | return cmd_status(sk, hdev->id, | ||
4208 | MGMT_OP_LOAD_IRKS, | ||
4209 | MGMT_STATUS_INVALID_PARAMS); | ||
4210 | } | ||
4211 | |||
4212 | hci_dev_lock(hdev); | ||
4213 | |||
4214 | hci_smp_irks_clear(hdev); | ||
4215 | |||
4216 | for (i = 0; i < irk_count; i++) { | ||
4217 | struct mgmt_irk_info *irk = &cp->irks[i]; | ||
4218 | u8 addr_type; | ||
4219 | |||
4220 | if (irk->addr.type == BDADDR_LE_PUBLIC) | ||
4221 | addr_type = ADDR_LE_DEV_PUBLIC; | ||
4222 | else | ||
4223 | addr_type = ADDR_LE_DEV_RANDOM; | ||
4224 | |||
4225 | hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val, | ||
4226 | BDADDR_ANY); | ||
4227 | } | ||
4228 | |||
4229 | set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags); | ||
4230 | |||
4231 | err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0); | ||
4232 | |||
4233 | hci_dev_unlock(hdev); | ||
4234 | |||
4235 | return err; | ||
4236 | } | ||
4237 | |||
4161 | static bool ltk_is_valid(struct mgmt_ltk_info *key) | 4238 | static bool ltk_is_valid(struct mgmt_ltk_info *key) |
4162 | { | 4239 | { |
4163 | if (key->master != 0x00 && key->master != 0x01) | 4240 | if (key->master != 0x00 && key->master != 0x01) |
@@ -4296,6 +4373,8 @@ static const struct mgmt_handler { | |||
4296 | { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, | 4373 | { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, |
4297 | { set_secure_conn, false, MGMT_SETTING_SIZE }, | 4374 | { set_secure_conn, false, MGMT_SETTING_SIZE }, |
4298 | { set_debug_keys, false, MGMT_SETTING_SIZE }, | 4375 | { set_debug_keys, false, MGMT_SETTING_SIZE }, |
4376 | { }, | ||
4377 | { load_irks, true, MGMT_LOAD_IRKS_SIZE }, | ||
4299 | }; | 4378 | }; |
4300 | 4379 | ||
4301 | 4380 | ||