diff options
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a08f4ce03182..bdb0e85f182e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -794,6 +794,99 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) | |||
794 | return err; | 794 | return err; |
795 | } | 795 | } |
796 | 796 | ||
797 | static int load_keys(struct sock *sk, unsigned char *data, u16 len) | ||
798 | { | ||
799 | struct hci_dev *hdev; | ||
800 | struct mgmt_cp_load_keys *cp; | ||
801 | u16 dev_id, key_count, expected_len; | ||
802 | int i; | ||
803 | |||
804 | cp = (void *) data; | ||
805 | dev_id = get_unaligned_le16(&cp->index); | ||
806 | key_count = get_unaligned_le16(&cp->key_count); | ||
807 | |||
808 | expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); | ||
809 | if (expected_len != len) { | ||
810 | BT_ERR("load_keys: expected %u bytes, got %u bytes", | ||
811 | len, expected_len); | ||
812 | return -EINVAL; | ||
813 | } | ||
814 | |||
815 | hdev = hci_dev_get(dev_id); | ||
816 | if (!hdev) | ||
817 | return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); | ||
818 | |||
819 | BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, | ||
820 | key_count); | ||
821 | |||
822 | hci_dev_lock_bh(hdev); | ||
823 | |||
824 | hci_link_keys_clear(hdev); | ||
825 | |||
826 | set_bit(HCI_LINK_KEYS, &hdev->flags); | ||
827 | |||
828 | if (cp->debug_keys) | ||
829 | set_bit(HCI_DEBUG_KEYS, &hdev->flags); | ||
830 | else | ||
831 | clear_bit(HCI_DEBUG_KEYS, &hdev->flags); | ||
832 | |||
833 | for (i = 0; i < key_count; i++) { | ||
834 | struct mgmt_key_info *key = &cp->keys[i]; | ||
835 | |||
836 | hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, | ||
837 | key->pin_len); | ||
838 | } | ||
839 | |||
840 | hci_dev_unlock_bh(hdev); | ||
841 | hci_dev_put(hdev); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | static int remove_key(struct sock *sk, unsigned char *data, u16 len) | ||
847 | { | ||
848 | struct hci_dev *hdev; | ||
849 | struct mgmt_cp_remove_key *cp; | ||
850 | struct hci_conn *conn; | ||
851 | u16 dev_id; | ||
852 | int err; | ||
853 | |||
854 | cp = (void *) data; | ||
855 | dev_id = get_unaligned_le16(&cp->index); | ||
856 | |||
857 | hdev = hci_dev_get(dev_id); | ||
858 | if (!hdev) | ||
859 | return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); | ||
860 | |||
861 | hci_dev_lock_bh(hdev); | ||
862 | |||
863 | err = hci_remove_link_key(hdev, &cp->bdaddr); | ||
864 | if (err < 0) { | ||
865 | err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); | ||
866 | goto unlock; | ||
867 | } | ||
868 | |||
869 | err = 0; | ||
870 | |||
871 | if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) | ||
872 | goto unlock; | ||
873 | |||
874 | conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); | ||
875 | if (conn) { | ||
876 | struct hci_cp_disconnect dc; | ||
877 | |||
878 | put_unaligned_le16(conn->handle, &dc.handle); | ||
879 | dc.reason = 0x13; /* Remote User Terminated Connection */ | ||
880 | err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL); | ||
881 | } | ||
882 | |||
883 | unlock: | ||
884 | hci_dev_unlock_bh(hdev); | ||
885 | hci_dev_put(hdev); | ||
886 | |||
887 | return err; | ||
888 | } | ||
889 | |||
797 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 890 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
798 | { | 891 | { |
799 | unsigned char *buf; | 892 | unsigned char *buf; |
@@ -858,6 +951,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
858 | case MGMT_OP_SET_SERVICE_CACHE: | 951 | case MGMT_OP_SET_SERVICE_CACHE: |
859 | err = set_service_cache(sk, buf + sizeof(*hdr), len); | 952 | err = set_service_cache(sk, buf + sizeof(*hdr), len); |
860 | break; | 953 | break; |
954 | case MGMT_OP_LOAD_KEYS: | ||
955 | err = load_keys(sk, buf + sizeof(*hdr), len); | ||
956 | break; | ||
957 | case MGMT_OP_REMOVE_KEY: | ||
958 | err = remove_key(sk, buf + sizeof(*hdr), len); | ||
959 | break; | ||
861 | default: | 960 | default: |
862 | BT_DBG("Unknown op %u", opcode); | 961 | BT_DBG("Unknown op %u", opcode); |
863 | err = cmd_status(sk, opcode, 0x01); | 962 | err = cmd_status(sk, opcode, 0x01); |
@@ -974,3 +1073,20 @@ int mgmt_connectable(u16 index, u8 connectable) | |||
974 | 1073 | ||
975 | return ret; | 1074 | return ret; |
976 | } | 1075 | } |
1076 | |||
1077 | int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) | ||
1078 | { | ||
1079 | struct mgmt_ev_new_key ev; | ||
1080 | |||
1081 | memset(&ev, 0, sizeof(ev)); | ||
1082 | |||
1083 | put_unaligned_le16(index, &ev.index); | ||
1084 | |||
1085 | bacpy(&ev.key.bdaddr, &key->bdaddr); | ||
1086 | ev.key.type = key->type; | ||
1087 | memcpy(ev.key.val, key->val, 16); | ||
1088 | ev.key.pin_len = key->pin_len; | ||
1089 | ev.old_key_type = old_key_type; | ||
1090 | |||
1091 | return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); | ||
1092 | } | ||