Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
This commit is contained in:
commit
1c1236e3af
@ -745,6 +745,33 @@ struct hci_cp_le_conn_update {
|
|||||||
__le16 max_ce_len;
|
__le16 max_ce_len;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_LE_START_ENC 0x2019
|
||||||
|
struct hci_cp_le_start_enc {
|
||||||
|
__le16 handle;
|
||||||
|
__u8 rand[8];
|
||||||
|
__le16 ediv;
|
||||||
|
__u8 ltk[16];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_LE_LTK_REPLY 0x201a
|
||||||
|
struct hci_cp_le_ltk_reply {
|
||||||
|
__le16 handle;
|
||||||
|
__u8 ltk[16];
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_le_ltk_reply {
|
||||||
|
__u8 status;
|
||||||
|
__le16 handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_LE_LTK_NEG_REPLY 0x201b
|
||||||
|
struct hci_cp_le_ltk_neg_reply {
|
||||||
|
__le16 handle;
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_le_ltk_neg_reply {
|
||||||
|
__u8 status;
|
||||||
|
__le16 handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* ---- HCI Events ---- */
|
/* ---- HCI Events ---- */
|
||||||
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
||||||
|
|
||||||
@ -1035,6 +1062,13 @@ struct hci_ev_le_conn_complete {
|
|||||||
__u8 clk_accurancy;
|
__u8 clk_accurancy;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_EV_LE_LTK_REQ 0x05
|
||||||
|
struct hci_ev_le_ltk_req {
|
||||||
|
__le16 handle;
|
||||||
|
__u8 random[8];
|
||||||
|
__le16 ediv;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Advertising report event types */
|
/* Advertising report event types */
|
||||||
#define ADV_IND 0x00
|
#define ADV_IND 0x00
|
||||||
#define ADV_DIRECT_IND 0x01
|
#define ADV_DIRECT_IND 0x01
|
||||||
|
@ -177,6 +177,8 @@ struct hci_dev {
|
|||||||
|
|
||||||
__u16 init_last_cmd;
|
__u16 init_last_cmd;
|
||||||
|
|
||||||
|
struct crypto_blkcipher *tfm;
|
||||||
|
|
||||||
struct inquiry_cache inq_cache;
|
struct inquiry_cache inq_cache;
|
||||||
struct hci_conn_hash conn_hash;
|
struct hci_conn_hash conn_hash;
|
||||||
struct list_head blacklist;
|
struct list_head blacklist;
|
||||||
@ -247,6 +249,7 @@ struct hci_conn {
|
|||||||
__u8 power_save;
|
__u8 power_save;
|
||||||
__u16 disc_timeout;
|
__u16 disc_timeout;
|
||||||
unsigned long pend;
|
unsigned long pend;
|
||||||
|
__u8 ltk[16];
|
||||||
|
|
||||||
__u8 remote_cap;
|
__u8 remote_cap;
|
||||||
__u8 remote_oob;
|
__u8 remote_oob;
|
||||||
@ -526,6 +529,8 @@ int hci_inquiry(void __user *arg);
|
|||||||
|
|
||||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||||
int hci_blacklist_clear(struct hci_dev *hdev);
|
int hci_blacklist_clear(struct hci_dev *hdev);
|
||||||
|
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||||
|
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||||
|
|
||||||
int hci_uuids_clear(struct hci_dev *hdev);
|
int hci_uuids_clear(struct hci_dev *hdev);
|
||||||
|
|
||||||
@ -742,6 +747,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
|||||||
if (conn->sec_level == BT_SECURITY_SDP)
|
if (conn->sec_level == BT_SECURITY_SDP)
|
||||||
conn->sec_level = BT_SECURITY_LOW;
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
|
|
||||||
|
if (conn->pending_sec_level > conn->sec_level)
|
||||||
|
conn->sec_level = conn->pending_sec_level;
|
||||||
|
|
||||||
hci_proto_encrypt_cfm(conn, status, encrypt);
|
hci_proto_encrypt_cfm(conn, status, encrypt);
|
||||||
|
|
||||||
read_lock_bh(&hci_cb_list_lock);
|
read_lock_bh(&hci_cb_list_lock);
|
||||||
@ -859,4 +867,9 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result);
|
|||||||
|
|
||||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||||
u16 latency, u16 to_multiplier);
|
u16 latency, u16 to_multiplier);
|
||||||
|
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||||
|
__u8 ltk[16]);
|
||||||
|
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
|
||||||
|
void hci_le_ltk_neg_reply(struct hci_conn *conn);
|
||||||
|
|
||||||
#endif /* __HCI_CORE_H */
|
#endif /* __HCI_CORE_H */
|
||||||
|
@ -287,6 +287,10 @@ struct l2cap_chan {
|
|||||||
|
|
||||||
struct l2cap_conn *conn;
|
struct l2cap_conn *conn;
|
||||||
|
|
||||||
|
__u8 state;
|
||||||
|
|
||||||
|
atomic_t refcnt;
|
||||||
|
|
||||||
__le16 psm;
|
__le16 psm;
|
||||||
__u16 dcid;
|
__u16 dcid;
|
||||||
__u16 scid;
|
__u16 scid;
|
||||||
@ -320,8 +324,8 @@ struct l2cap_chan {
|
|||||||
__u16 monitor_timeout;
|
__u16 monitor_timeout;
|
||||||
__u16 mps;
|
__u16 mps;
|
||||||
|
|
||||||
__u8 conf_state;
|
unsigned long conf_state;
|
||||||
__u16 conn_state;
|
unsigned long conn_state;
|
||||||
|
|
||||||
__u8 next_tx_seq;
|
__u8 next_tx_seq;
|
||||||
__u8 expected_ack_seq;
|
__u8 expected_ack_seq;
|
||||||
@ -354,6 +358,18 @@ struct l2cap_chan {
|
|||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct list_head global_l;
|
struct list_head global_l;
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
struct l2cap_ops *ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct l2cap_ops {
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
struct l2cap_chan *(*new_connection) (void *data);
|
||||||
|
int (*recv) (void *data, struct sk_buff *skb);
|
||||||
|
void (*close) (void *data);
|
||||||
|
void (*state_change) (void *data, int state);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct l2cap_conn {
|
struct l2cap_conn {
|
||||||
@ -379,6 +395,15 @@ struct l2cap_conn {
|
|||||||
|
|
||||||
__u8 disc_reason;
|
__u8 disc_reason;
|
||||||
|
|
||||||
|
__u8 preq[7]; /* SMP Pairing Request */
|
||||||
|
__u8 prsp[7]; /* SMP Pairing Response */
|
||||||
|
__u8 prnd[16]; /* SMP Pairing Random */
|
||||||
|
__u8 pcnf[16]; /* SMP Pairing Confirm */
|
||||||
|
__u8 tk[16]; /* SMP Temporary Key */
|
||||||
|
__u8 smp_key_size;
|
||||||
|
|
||||||
|
struct timer_list security_timer;
|
||||||
|
|
||||||
struct list_head chan_l;
|
struct list_head chan_l;
|
||||||
rwlock_t chan_lock;
|
rwlock_t chan_lock;
|
||||||
};
|
};
|
||||||
@ -399,36 +424,45 @@ struct l2cap_pinfo {
|
|||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define L2CAP_CONF_REQ_SENT 0x01
|
enum {
|
||||||
#define L2CAP_CONF_INPUT_DONE 0x02
|
CONF_REQ_SENT,
|
||||||
#define L2CAP_CONF_OUTPUT_DONE 0x04
|
CONF_INPUT_DONE,
|
||||||
#define L2CAP_CONF_MTU_DONE 0x08
|
CONF_OUTPUT_DONE,
|
||||||
#define L2CAP_CONF_MODE_DONE 0x10
|
CONF_MTU_DONE,
|
||||||
#define L2CAP_CONF_CONNECT_PEND 0x20
|
CONF_MODE_DONE,
|
||||||
#define L2CAP_CONF_NO_FCS_RECV 0x40
|
CONF_CONNECT_PEND,
|
||||||
#define L2CAP_CONF_STATE2_DEVICE 0x80
|
CONF_NO_FCS_RECV,
|
||||||
|
CONF_STATE2_DEVICE,
|
||||||
|
};
|
||||||
|
|
||||||
#define L2CAP_CONF_MAX_CONF_REQ 2
|
#define L2CAP_CONF_MAX_CONF_REQ 2
|
||||||
#define L2CAP_CONF_MAX_CONF_RSP 2
|
#define L2CAP_CONF_MAX_CONF_RSP 2
|
||||||
|
|
||||||
#define L2CAP_CONN_SAR_SDU 0x0001
|
enum {
|
||||||
#define L2CAP_CONN_SREJ_SENT 0x0002
|
CONN_SAR_SDU,
|
||||||
#define L2CAP_CONN_WAIT_F 0x0004
|
CONN_SREJ_SENT,
|
||||||
#define L2CAP_CONN_SREJ_ACT 0x0008
|
CONN_WAIT_F,
|
||||||
#define L2CAP_CONN_SEND_PBIT 0x0010
|
CONN_SREJ_ACT,
|
||||||
#define L2CAP_CONN_REMOTE_BUSY 0x0020
|
CONN_SEND_PBIT,
|
||||||
#define L2CAP_CONN_LOCAL_BUSY 0x0040
|
CONN_REMOTE_BUSY,
|
||||||
#define L2CAP_CONN_REJ_ACT 0x0080
|
CONN_LOCAL_BUSY,
|
||||||
#define L2CAP_CONN_SEND_FBIT 0x0100
|
CONN_REJ_ACT,
|
||||||
#define L2CAP_CONN_RNR_SENT 0x0200
|
CONN_SEND_FBIT,
|
||||||
#define L2CAP_CONN_SAR_RETRY 0x0400
|
CONN_RNR_SENT,
|
||||||
|
CONN_SAR_RETRY,
|
||||||
|
};
|
||||||
|
|
||||||
#define __mod_retrans_timer() mod_timer(&chan->retrans_timer, \
|
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
|
||||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
|
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
|
||||||
#define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \
|
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
|
||||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
|
L2CAP_DEFAULT_RETRANS_TO);
|
||||||
#define __mod_ack_timer() mod_timer(&chan->ack_timer, \
|
#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer)
|
||||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO));
|
#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
|
||||||
|
L2CAP_DEFAULT_MONITOR_TO);
|
||||||
|
#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer)
|
||||||
|
#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
|
||||||
|
L2CAP_DEFAULT_ACK_TO);
|
||||||
|
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
|
||||||
|
|
||||||
static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
||||||
{
|
{
|
||||||
@ -459,11 +493,6 @@ int __l2cap_wait_ack(struct sock *sk);
|
|||||||
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
|
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
|
||||||
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
|
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
|
||||||
|
|
||||||
void l2cap_sock_kill(struct sock *sk);
|
|
||||||
void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
|
||||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
|
||||||
int proto, gfp_t prio);
|
|
||||||
|
|
||||||
struct l2cap_chan *l2cap_chan_create(struct sock *sk);
|
struct l2cap_chan *l2cap_chan_create(struct sock *sk);
|
||||||
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
||||||
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
||||||
|
@ -199,6 +199,16 @@ struct mgmt_cp_remove_remote_oob_data {
|
|||||||
|
|
||||||
#define MGMT_OP_STOP_DISCOVERY 0x001C
|
#define MGMT_OP_STOP_DISCOVERY 0x001C
|
||||||
|
|
||||||
|
#define MGMT_OP_BLOCK_DEVICE 0x001D
|
||||||
|
struct mgmt_cp_block_device {
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define MGMT_OP_UNBLOCK_DEVICE 0x001E
|
||||||
|
struct mgmt_cp_unblock_device {
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||||
struct mgmt_ev_cmd_complete {
|
struct mgmt_ev_cmd_complete {
|
||||||
__le16 opcode;
|
__le16 opcode;
|
||||||
|
@ -1,3 +1,25 @@
|
|||||||
|
/*
|
||||||
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
|
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 2 as
|
||||||
|
published by the Free Software Foundation;
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||||
|
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||||
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||||
|
SOFTWARE IS DISCLAIMED.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef __SMP_H
|
#ifndef __SMP_H
|
||||||
#define __SMP_H
|
#define __SMP_H
|
||||||
|
|
||||||
@ -16,6 +38,23 @@ struct smp_cmd_pairing {
|
|||||||
__u8 resp_key_dist;
|
__u8 resp_key_dist;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define SMP_IO_DISPLAY_ONLY 0x00
|
||||||
|
#define SMP_IO_DISPLAY_YESNO 0x01
|
||||||
|
#define SMP_IO_KEYBOARD_ONLY 0x02
|
||||||
|
#define SMP_IO_NO_INPUT_OUTPUT 0x03
|
||||||
|
#define SMP_IO_KEYBOARD_DISPLAY 0x04
|
||||||
|
|
||||||
|
#define SMP_OOB_NOT_PRESENT 0x00
|
||||||
|
#define SMP_OOB_PRESENT 0x01
|
||||||
|
|
||||||
|
#define SMP_DIST_ENC_KEY 0x01
|
||||||
|
#define SMP_DIST_ID_KEY 0x02
|
||||||
|
#define SMP_DIST_SIGN 0x04
|
||||||
|
|
||||||
|
#define SMP_AUTH_NONE 0x00
|
||||||
|
#define SMP_AUTH_BONDING 0x01
|
||||||
|
#define SMP_AUTH_MITM 0x04
|
||||||
|
|
||||||
#define SMP_CMD_PAIRING_CONFIRM 0x03
|
#define SMP_CMD_PAIRING_CONFIRM 0x03
|
||||||
struct smp_cmd_pairing_confirm {
|
struct smp_cmd_pairing_confirm {
|
||||||
__u8 confirm_val[16];
|
__u8 confirm_val[16];
|
||||||
@ -73,4 +112,11 @@ struct smp_cmd_security_req {
|
|||||||
#define SMP_UNSPECIFIED 0x08
|
#define SMP_UNSPECIFIED 0x08
|
||||||
#define SMP_REPEATED_ATTEMPTS 0x09
|
#define SMP_REPEATED_ATTEMPTS 0x09
|
||||||
|
|
||||||
|
#define SMP_MIN_ENC_KEY_SIZE 7
|
||||||
|
#define SMP_MAX_ENC_KEY_SIZE 16
|
||||||
|
|
||||||
|
/* SMP Commands */
|
||||||
|
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
|
||||||
|
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
||||||
|
|
||||||
#endif /* __SMP_H */
|
#endif /* __SMP_H */
|
||||||
|
@ -22,6 +22,7 @@ menuconfig BT
|
|||||||
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
||||||
CMTP Module (CAPI Message Transport Protocol)
|
CMTP Module (CAPI Message Transport Protocol)
|
||||||
HIDP Module (Human Interface Device Protocol)
|
HIDP Module (Human Interface Device Protocol)
|
||||||
|
SMP Module (Security Manager Protocol)
|
||||||
|
|
||||||
Say Y here to compile Bluetooth support into the kernel or say M to
|
Say Y here to compile Bluetooth support into the kernel or say M to
|
||||||
compile it as module (bluetooth).
|
compile it as module (bluetooth).
|
||||||
@ -36,11 +37,18 @@ if BT != n
|
|||||||
config BT_L2CAP
|
config BT_L2CAP
|
||||||
bool "L2CAP protocol support"
|
bool "L2CAP protocol support"
|
||||||
select CRC16
|
select CRC16
|
||||||
|
select CRYPTO
|
||||||
|
select CRYPTO_BLKCIPHER
|
||||||
|
select CRYPTO_AES
|
||||||
|
select CRYPTO_ECB
|
||||||
help
|
help
|
||||||
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
||||||
connection oriented and connection-less data transport. L2CAP
|
connection oriented and connection-less data transport. L2CAP
|
||||||
support is required for most Bluetooth applications.
|
support is required for most Bluetooth applications.
|
||||||
|
|
||||||
|
Also included is support for SMP (Security Manager Protocol) which
|
||||||
|
is the security layer on top of LE (Low Energy) links.
|
||||||
|
|
||||||
config BT_SCO
|
config BT_SCO
|
||||||
bool "SCO links support"
|
bool "SCO links support"
|
||||||
help
|
help
|
||||||
|
@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
|
|||||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||||
|
|
||||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o
|
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o
|
||||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||||
|
@ -53,6 +53,7 @@ static void hci_le_connect(struct hci_conn *conn)
|
|||||||
conn->state = BT_CONNECT;
|
conn->state = BT_CONNECT;
|
||||||
conn->out = 1;
|
conn->out = 1;
|
||||||
conn->link_mode |= HCI_LM_MASTER;
|
conn->link_mode |= HCI_LM_MASTER;
|
||||||
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
cp.scan_interval = cpu_to_le16(0x0004);
|
cp.scan_interval = cpu_to_le16(0x0004);
|
||||||
@ -204,6 +205,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(hci_le_conn_update);
|
EXPORT_SYMBOL(hci_le_conn_update);
|
||||||
|
|
||||||
|
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||||
|
__u8 ltk[16])
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
struct hci_cp_le_start_enc cp;
|
||||||
|
|
||||||
|
BT_DBG("%p", conn);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
|
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||||
|
cp.ediv = ediv;
|
||||||
|
memcpy(cp.rand, rand, sizeof(rand));
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(hci_le_start_enc);
|
||||||
|
|
||||||
|
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16])
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
struct hci_cp_le_ltk_reply cp;
|
||||||
|
|
||||||
|
BT_DBG("%p", conn);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
|
memcpy(cp.ltk, ltk, sizeof(ltk));
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(hci_le_ltk_reply);
|
||||||
|
|
||||||
|
void hci_le_ltk_neg_reply(struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
struct hci_cp_le_ltk_neg_reply cp;
|
||||||
|
|
||||||
|
BT_DBG("%p", conn);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
/* Device _must_ be locked */
|
/* Device _must_ be locked */
|
||||||
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
||||||
{
|
{
|
||||||
@ -620,11 +670,11 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
|||||||
goto encrypt;
|
goto encrypt;
|
||||||
|
|
||||||
auth:
|
auth:
|
||||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
hci_conn_auth(conn, sec_level, auth_type);
|
if (!hci_conn_auth(conn, sec_level, auth_type))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
encrypt:
|
encrypt:
|
||||||
if (conn->link_mode & HCI_LM_ENCRYPT)
|
if (conn->link_mode & HCI_LM_ENCRYPT)
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/rfkill.h>
|
#include <linux/rfkill.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
#include <linux/crypto.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
|
||||||
#include <asm/system.h>
|
#include <asm/system.h>
|
||||||
@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg);
|
|||||||
|
|
||||||
static DEFINE_RWLOCK(hci_task_lock);
|
static DEFINE_RWLOCK(hci_task_lock);
|
||||||
|
|
||||||
|
static int enable_smp;
|
||||||
|
|
||||||
/* HCI device list */
|
/* HCI device list */
|
||||||
LIST_HEAD(hci_dev_list);
|
LIST_HEAD(hci_dev_list);
|
||||||
DEFINE_RWLOCK(hci_dev_list_lock);
|
DEFINE_RWLOCK(hci_dev_list_lock);
|
||||||
@ -1202,6 +1205,97 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
|
||||||
|
bdaddr_t *bdaddr)
|
||||||
|
{
|
||||||
|
struct list_head *p;
|
||||||
|
|
||||||
|
list_for_each(p, &hdev->blacklist) {
|
||||||
|
struct bdaddr_list *b;
|
||||||
|
|
||||||
|
b = list_entry(p, struct bdaddr_list, list);
|
||||||
|
|
||||||
|
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_blacklist_clear(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct list_head *p, *n;
|
||||||
|
|
||||||
|
list_for_each_safe(p, n, &hdev->blacklist) {
|
||||||
|
struct bdaddr_list *b;
|
||||||
|
|
||||||
|
b = list_entry(p, struct bdaddr_list, list);
|
||||||
|
|
||||||
|
list_del(p);
|
||||||
|
kfree(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||||
|
{
|
||||||
|
struct bdaddr_list *entry;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
||||||
|
return -EBADF;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (hci_blacklist_lookup(hdev, bdaddr)) {
|
||||||
|
err = -EEXIST;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||||
|
if (!entry) {
|
||||||
|
return -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bacpy(&entry->bdaddr, bdaddr);
|
||||||
|
|
||||||
|
list_add(&entry->list, &hdev->blacklist);
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||||
|
{
|
||||||
|
struct bdaddr_list *entry;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
||||||
|
hci_blacklist_clear(hdev);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = hci_blacklist_lookup(hdev, bdaddr);
|
||||||
|
if (!entry) {
|
||||||
|
err = -ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
|
||||||
|
done:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_clear_adv_cache(unsigned long arg)
|
static void hci_clear_adv_cache(unsigned long arg)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = (void *) arg;
|
struct hci_dev *hdev = (void *) arg;
|
||||||
@ -1274,6 +1368,14 @@ int hci_add_adv_entry(struct hci_dev *hdev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct crypto_blkcipher *alloc_cypher(void)
|
||||||
|
{
|
||||||
|
if (enable_smp)
|
||||||
|
return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||||
|
|
||||||
|
return ERR_PTR(-ENOTSUPP);
|
||||||
|
}
|
||||||
|
|
||||||
/* Register HCI device */
|
/* Register HCI device */
|
||||||
int hci_register_dev(struct hci_dev *hdev)
|
int hci_register_dev(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
@ -1358,6 +1460,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||||||
if (!hdev->workqueue)
|
if (!hdev->workqueue)
|
||||||
goto nomem;
|
goto nomem;
|
||||||
|
|
||||||
|
hdev->tfm = alloc_cypher();
|
||||||
|
if (IS_ERR(hdev->tfm))
|
||||||
|
BT_INFO("Failed to load transform for ecb(aes): %ld",
|
||||||
|
PTR_ERR(hdev->tfm));
|
||||||
|
|
||||||
hci_register_sysfs(hdev);
|
hci_register_sysfs(hdev);
|
||||||
|
|
||||||
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
||||||
@ -1406,6 +1513,9 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||||||
!test_bit(HCI_SETUP, &hdev->flags))
|
!test_bit(HCI_SETUP, &hdev->flags))
|
||||||
mgmt_index_removed(hdev->id);
|
mgmt_index_removed(hdev->id);
|
||||||
|
|
||||||
|
if (!IS_ERR(hdev->tfm))
|
||||||
|
crypto_free_blkcipher(hdev->tfm);
|
||||||
|
|
||||||
hci_notify(hdev, HCI_DEV_UNREG);
|
hci_notify(hdev, HCI_DEV_UNREG);
|
||||||
|
|
||||||
if (hdev->rfkill) {
|
if (hdev->rfkill) {
|
||||||
@ -2242,3 +2352,6 @@ static void hci_cmd_task(unsigned long arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module_param(enable_smp, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)");
|
||||||
|
@ -868,6 +868,30 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||||
|
|
||||||
|
if (rp->status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
|
||||||
|
|
||||||
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||||
|
|
||||||
|
if (rp->status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||||
{
|
{
|
||||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||||
@ -1248,6 +1272,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
__u8 status = *((__u8 *) skb->data);
|
__u8 status = *((__u8 *) skb->data);
|
||||||
@ -1593,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
|
|||||||
/* Encryption implies authentication */
|
/* Encryption implies authentication */
|
||||||
conn->link_mode |= HCI_LM_AUTH;
|
conn->link_mode |= HCI_LM_AUTH;
|
||||||
conn->link_mode |= HCI_LM_ENCRYPT;
|
conn->link_mode |= HCI_LM_ENCRYPT;
|
||||||
|
conn->sec_level = conn->pending_sec_level;
|
||||||
} else
|
} else
|
||||||
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
||||||
}
|
}
|
||||||
@ -1856,6 +1886,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||||||
hci_cc_le_set_scan_enable(hdev, skb);
|
hci_cc_le_set_scan_enable(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_LE_LTK_REPLY:
|
||||||
|
hci_cc_le_ltk_reply(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_LE_LTK_NEG_REPLY:
|
||||||
|
hci_cc_le_ltk_neg_reply(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||||
break;
|
break;
|
||||||
@ -1934,6 +1972,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_cs_le_create_conn(hdev, ev->status);
|
hci_cs_le_create_conn(hdev, ev->status);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_OP_LE_START_ENC:
|
||||||
|
hci_cs_le_start_enc(hdev, ev->status);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||||
break;
|
break;
|
||||||
@ -2712,6 +2754,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
|||||||
|
|
||||||
mgmt_connected(hdev->id, &ev->bdaddr);
|
mgmt_connected(hdev->id, &ev->bdaddr);
|
||||||
|
|
||||||
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
conn->handle = __le16_to_cpu(ev->handle);
|
conn->handle = __le16_to_cpu(ev->handle);
|
||||||
conn->state = BT_CONNECTED;
|
conn->state = BT_CONNECTED;
|
||||||
|
|
||||||
@ -2745,6 +2788,28 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
|
||||||
|
struct hci_cp_le_ltk_reply cp;
|
||||||
|
struct hci_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
|
memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
|
||||||
|
|
||||||
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||||
@ -2760,6 +2825,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_le_adv_report_evt(hdev, skb);
|
hci_le_adv_report_evt(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_EV_LE_LTK_REQ:
|
||||||
|
hci_le_ltk_request_evt(hdev, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||||
{
|
|
||||||
struct list_head *p;
|
|
||||||
|
|
||||||
list_for_each(p, &hdev->blacklist) {
|
|
||||||
struct bdaddr_list *b;
|
|
||||||
|
|
||||||
b = list_entry(p, struct bdaddr_list, list);
|
|
||||||
|
|
||||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
|
||||||
{
|
{
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
struct bdaddr_list *entry;
|
|
||||||
|
|
||||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
return hci_blacklist_add(hdev, &bdaddr);
|
||||||
return -EBADF;
|
|
||||||
|
|
||||||
if (hci_blacklist_lookup(hdev, &bdaddr))
|
|
||||||
return -EEXIST;
|
|
||||||
|
|
||||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
|
||||||
if (!entry)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
bacpy(&entry->bdaddr, &bdaddr);
|
|
||||||
|
|
||||||
list_add(&entry->list, &hdev->blacklist);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_blacklist_clear(struct hci_dev *hdev)
|
static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||||
{
|
|
||||||
struct list_head *p, *n;
|
|
||||||
|
|
||||||
list_for_each_safe(p, n, &hdev->blacklist) {
|
|
||||||
struct bdaddr_list *b;
|
|
||||||
|
|
||||||
b = list_entry(p, struct bdaddr_list, list);
|
|
||||||
|
|
||||||
list_del(p);
|
|
||||||
kfree(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
|
||||||
{
|
{
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
struct bdaddr_list *entry;
|
|
||||||
|
|
||||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
return hci_blacklist_del(hdev, &bdaddr);
|
||||||
return hci_blacklist_clear(hdev);
|
|
||||||
|
|
||||||
entry = hci_blacklist_lookup(hdev, &bdaddr);
|
|
||||||
if (!entry)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
list_del(&entry->list);
|
|
||||||
kfree(entry);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ioctls that require bound socket */
|
/* Ioctls that require bound socket */
|
||||||
@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
|
|||||||
case HCIBLOCKADDR:
|
case HCIBLOCKADDR:
|
||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
return hci_blacklist_add(hdev, (void __user *) arg);
|
return hci_sock_blacklist_add(hdev, (void __user *) arg);
|
||||||
|
|
||||||
case HCIUNBLOCKADDR:
|
case HCIUNBLOCKADDR:
|
||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
return hci_blacklist_del(hdev, (void __user *) arg);
|
return hci_sock_blacklist_del(hdev, (void __user *) arg);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (hdev->ioctl)
|
if (hdev->ioctl)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -29,8 +29,11 @@
|
|||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
#include <net/bluetooth/l2cap.h>
|
#include <net/bluetooth/l2cap.h>
|
||||||
|
#include <net/bluetooth/smp.h>
|
||||||
|
|
||||||
static const struct proto_ops l2cap_sock_ops;
|
static const struct proto_ops l2cap_sock_ops;
|
||||||
|
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||||
|
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
|
||||||
|
|
||||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||||
{
|
{
|
||||||
@ -87,6 +90,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||||||
chan->sec_level = BT_SECURITY_SDP;
|
chan->sec_level = BT_SECURITY_SDP;
|
||||||
|
|
||||||
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
||||||
|
|
||||||
|
chan->state = BT_BOUND;
|
||||||
sk->sk_state = BT_BOUND;
|
sk->sk_state = BT_BOUND;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -212,6 +217,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||||||
|
|
||||||
sk->sk_max_ack_backlog = backlog;
|
sk->sk_max_ack_backlog = backlog;
|
||||||
sk->sk_ack_backlog = 0;
|
sk->sk_ack_backlog = 0;
|
||||||
|
|
||||||
|
chan->state = BT_LISTEN;
|
||||||
sk->sk_state = BT_LISTEN;
|
sk->sk_state = BT_LISTEN;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@ -505,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
chan->mode = opts.mode;
|
chan->mode = opts.mode;
|
||||||
switch (chan->mode) {
|
switch (chan->mode) {
|
||||||
case L2CAP_MODE_BASIC:
|
case L2CAP_MODE_BASIC:
|
||||||
chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
|
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||||
break;
|
break;
|
||||||
case L2CAP_MODE_ERTM:
|
case L2CAP_MODE_ERTM:
|
||||||
case L2CAP_MODE_STREAMING:
|
case L2CAP_MODE_STREAMING:
|
||||||
@ -556,6 +563,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||||
struct bt_security sec;
|
struct bt_security sec;
|
||||||
struct bt_power pwr;
|
struct bt_power pwr;
|
||||||
|
struct l2cap_conn *conn;
|
||||||
int len, err = 0;
|
int len, err = 0;
|
||||||
u32 opt;
|
u32 opt;
|
||||||
|
|
||||||
@ -592,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
chan->sec_level = sec.level;
|
chan->sec_level = sec.level;
|
||||||
|
|
||||||
|
conn = chan->conn;
|
||||||
|
if (conn && chan->scid == L2CAP_CID_LE_DATA) {
|
||||||
|
if (!conn->hcon->out) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smp_conn_security(conn, sec.level))
|
||||||
|
break;
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
sk->sk_state = BT_CONFIG;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_DEFER_SETUP:
|
case BT_DEFER_SETUP:
|
||||||
@ -711,7 +733,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||||||
/* Kill socket (only if zapped and orphan)
|
/* Kill socket (only if zapped and orphan)
|
||||||
* Must be called on unlocked socket.
|
* Must be called on unlocked socket.
|
||||||
*/
|
*/
|
||||||
void l2cap_sock_kill(struct sock *sk)
|
static void l2cap_sock_kill(struct sock *sk)
|
||||||
{
|
{
|
||||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
|
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
|
||||||
return;
|
return;
|
||||||
@ -773,6 +795,49 @@ static int l2cap_sock_release(struct socket *sock)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
|
||||||
|
{
|
||||||
|
struct sock *sk, *parent = data;
|
||||||
|
|
||||||
|
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (!sk)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
l2cap_sock_init(sk, parent);
|
||||||
|
|
||||||
|
return l2cap_pi(sk)->chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct sock *sk = data;
|
||||||
|
|
||||||
|
return sock_queue_rcv_skb(sk, skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l2cap_sock_close_cb(void *data)
|
||||||
|
{
|
||||||
|
struct sock *sk = data;
|
||||||
|
|
||||||
|
l2cap_sock_kill(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void l2cap_sock_state_change_cb(void *data, int state)
|
||||||
|
{
|
||||||
|
struct sock *sk = data;
|
||||||
|
|
||||||
|
sk->sk_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct l2cap_ops l2cap_chan_ops = {
|
||||||
|
.name = "L2CAP Socket Interface",
|
||||||
|
.new_connection = l2cap_sock_new_connection_cb,
|
||||||
|
.recv = l2cap_sock_recv_cb,
|
||||||
|
.close = l2cap_sock_close_cb,
|
||||||
|
.state_change = l2cap_sock_state_change_cb,
|
||||||
|
};
|
||||||
|
|
||||||
static void l2cap_sock_destruct(struct sock *sk)
|
static void l2cap_sock_destruct(struct sock *sk)
|
||||||
{
|
{
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
@ -781,7 +846,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
|||||||
skb_queue_purge(&sk->sk_write_queue);
|
skb_queue_purge(&sk->sk_write_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||||
{
|
{
|
||||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||||
struct l2cap_chan *chan = pi->chan;
|
struct l2cap_chan *chan = pi->chan;
|
||||||
@ -826,7 +891,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||||||
chan->omtu = 0;
|
chan->omtu = 0;
|
||||||
if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
|
if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
|
||||||
chan->mode = L2CAP_MODE_ERTM;
|
chan->mode = L2CAP_MODE_ERTM;
|
||||||
chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
|
set_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||||
} else {
|
} else {
|
||||||
chan->mode = L2CAP_MODE_BASIC;
|
chan->mode = L2CAP_MODE_BASIC;
|
||||||
}
|
}
|
||||||
@ -838,10 +903,14 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||||||
chan->force_reliable = 0;
|
chan->force_reliable = 0;
|
||||||
chan->flushable = BT_FLUSHABLE_OFF;
|
chan->flushable = BT_FLUSHABLE_OFF;
|
||||||
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default config options */
|
/* Default config options */
|
||||||
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
||||||
|
|
||||||
|
chan->data = sk;
|
||||||
|
chan->ops = &l2cap_chan_ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct proto l2cap_proto = {
|
static struct proto l2cap_proto = {
|
||||||
@ -850,9 +919,10 @@ static struct proto l2cap_proto = {
|
|||||||
.obj_size = sizeof(struct l2cap_pinfo)
|
.obj_size = sizeof(struct l2cap_pinfo)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
|
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
|
||||||
if (!sk)
|
if (!sk)
|
||||||
@ -869,6 +939,14 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
|
|||||||
sk->sk_protocol = proto;
|
sk->sk_protocol = proto;
|
||||||
sk->sk_state = BT_OPEN;
|
sk->sk_state = BT_OPEN;
|
||||||
|
|
||||||
|
chan = l2cap_chan_create(sk);
|
||||||
|
if (!chan) {
|
||||||
|
l2cap_sock_kill(sk);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2cap_pi(sk)->chan = chan;
|
||||||
|
|
||||||
return sk;
|
return sk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,7 +954,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
int kern)
|
int kern)
|
||||||
{
|
{
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct l2cap_chan *chan;
|
|
||||||
|
|
||||||
BT_DBG("sock %p", sock);
|
BT_DBG("sock %p", sock);
|
||||||
|
|
||||||
@ -895,14 +972,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
|||||||
if (!sk)
|
if (!sk)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
chan = l2cap_chan_create(sk);
|
|
||||||
if (!chan) {
|
|
||||||
l2cap_sock_kill(sk);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
l2cap_pi(sk)->chan = chan;
|
|
||||||
|
|
||||||
l2cap_sock_init(sk, NULL);
|
l2cap_sock_init(sk, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -990,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||||||
|
|
||||||
put_unaligned_le16(conn->handle, &dc.handle);
|
put_unaligned_le16(conn->handle, &dc.handle);
|
||||||
dc.reason = 0x13; /* Remote User Terminated Connection */
|
dc.reason = 0x13; /* Remote User Terminated Connection */
|
||||||
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL);
|
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
@ -1666,6 +1666,70 @@ failed:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int block_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
|
u16 len)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
struct mgmt_cp_block_device *cp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("hci%u", index);
|
||||||
|
|
||||||
|
cp = (void *) data;
|
||||||
|
|
||||||
|
if (len != sizeof(*cp))
|
||||||
|
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
|
EINVAL);
|
||||||
|
|
||||||
|
hdev = hci_dev_get(index);
|
||||||
|
if (!hdev)
|
||||||
|
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
|
ENODEV);
|
||||||
|
|
||||||
|
err = hci_blacklist_add(hdev, &cp->bdaddr);
|
||||||
|
|
||||||
|
if (err < 0)
|
||||||
|
err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
|
||||||
|
else
|
||||||
|
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||||
|
NULL, 0);
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
|
||||||
|
u16 len)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
struct mgmt_cp_unblock_device *cp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("hci%u", index);
|
||||||
|
|
||||||
|
cp = (void *) data;
|
||||||
|
|
||||||
|
if (len != sizeof(*cp))
|
||||||
|
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
|
EINVAL);
|
||||||
|
|
||||||
|
hdev = hci_dev_get(index);
|
||||||
|
if (!hdev)
|
||||||
|
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
|
ENODEV);
|
||||||
|
|
||||||
|
err = hci_blacklist_del(hdev, &cp->bdaddr);
|
||||||
|
|
||||||
|
if (err < 0)
|
||||||
|
err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
|
||||||
|
else
|
||||||
|
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||||
|
NULL, 0);
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||||
{
|
{
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
@ -1780,6 +1844,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|||||||
case MGMT_OP_STOP_DISCOVERY:
|
case MGMT_OP_STOP_DISCOVERY:
|
||||||
err = stop_discovery(sk, index);
|
err = stop_discovery(sk, index);
|
||||||
break;
|
break;
|
||||||
|
case MGMT_OP_BLOCK_DEVICE:
|
||||||
|
err = block_device(sk, index, buf + sizeof(*hdr), len);
|
||||||
|
break;
|
||||||
|
case MGMT_OP_UNBLOCK_DEVICE:
|
||||||
|
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
BT_DBG("Unknown op %u", opcode);
|
BT_DBG("Unknown op %u", opcode);
|
||||||
err = cmd_status(sk, index, opcode, 0x01);
|
err = cmd_status(sk, index, opcode, 0x01);
|
||||||
|
533
net/bluetooth/smp.c
Normal file
533
net/bluetooth/smp.c
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
/*
|
||||||
|
BlueZ - Bluetooth protocol stack for Linux
|
||||||
|
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License version 2 as
|
||||||
|
published by the Free Software Foundation;
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||||
|
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||||
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||||
|
SOFTWARE IS DISCLAIMED.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
#include <net/bluetooth/l2cap.h>
|
||||||
|
#include <net/bluetooth/smp.h>
|
||||||
|
#include <linux/crypto.h>
|
||||||
|
#include <crypto/b128ops.h>
|
||||||
|
|
||||||
|
#define SMP_TIMEOUT 30000 /* 30 seconds */
|
||||||
|
|
||||||
|
static inline void swap128(u8 src[16], u8 dst[16])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
dst[15 - i] = src[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void swap56(u8 src[7], u8 dst[7])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
dst[6 - i] = src[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
||||||
|
{
|
||||||
|
struct blkcipher_desc desc;
|
||||||
|
struct scatterlist sg;
|
||||||
|
int err, iv_len;
|
||||||
|
unsigned char iv[128];
|
||||||
|
|
||||||
|
if (tfm == NULL) {
|
||||||
|
BT_ERR("tfm %p", tfm);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc.tfm = tfm;
|
||||||
|
desc.flags = 0;
|
||||||
|
|
||||||
|
err = crypto_blkcipher_setkey(tfm, k, 16);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("cipher setkey failed: %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_init_one(&sg, r, 16);
|
||||||
|
|
||||||
|
iv_len = crypto_blkcipher_ivsize(tfm);
|
||||||
|
if (iv_len) {
|
||||||
|
memset(&iv, 0xff, iv_len);
|
||||||
|
crypto_blkcipher_set_iv(tfm, iv, iv_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
|
||||||
|
if (err)
|
||||||
|
BT_ERR("Encrypt data error %d", err);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
|
||||||
|
u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
|
||||||
|
u8 _rat, bdaddr_t *ra, u8 res[16])
|
||||||
|
{
|
||||||
|
u8 p1[16], p2[16];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
memset(p1, 0, 16);
|
||||||
|
|
||||||
|
/* p1 = pres || preq || _rat || _iat */
|
||||||
|
swap56(pres, p1);
|
||||||
|
swap56(preq, p1 + 7);
|
||||||
|
p1[14] = _rat;
|
||||||
|
p1[15] = _iat;
|
||||||
|
|
||||||
|
memset(p2, 0, 16);
|
||||||
|
|
||||||
|
/* p2 = padding || ia || ra */
|
||||||
|
baswap((bdaddr_t *) (p2 + 4), ia);
|
||||||
|
baswap((bdaddr_t *) (p2 + 10), ra);
|
||||||
|
|
||||||
|
/* res = r XOR p1 */
|
||||||
|
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
|
||||||
|
|
||||||
|
/* res = e(k, res) */
|
||||||
|
err = smp_e(tfm, k, res);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("Encrypt data error");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* res = res XOR p2 */
|
||||||
|
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
|
||||||
|
|
||||||
|
/* res = e(k, res) */
|
||||||
|
err = smp_e(tfm, k, res);
|
||||||
|
if (err)
|
||||||
|
BT_ERR("Encrypt data error");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
|
||||||
|
u8 r1[16], u8 r2[16], u8 _r[16])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Just least significant octets from r1 and r2 are considered */
|
||||||
|
memcpy(_r, r1 + 8, 8);
|
||||||
|
memcpy(_r + 8, r2 + 8, 8);
|
||||||
|
|
||||||
|
err = smp_e(tfm, k, _r);
|
||||||
|
if (err)
|
||||||
|
BT_ERR("Encrypt data error");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int smp_rand(u8 *buf)
|
||||||
|
{
|
||||||
|
get_random_bytes(buf, 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
|
||||||
|
u16 dlen, void *data)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct l2cap_hdr *lh;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
|
||||||
|
|
||||||
|
if (len > conn->mtu)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
||||||
|
if (!skb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||||
|
lh->len = cpu_to_le16(sizeof(code) + dlen);
|
||||||
|
lh->cid = cpu_to_le16(L2CAP_CID_SMP);
|
||||||
|
|
||||||
|
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
|
||||||
|
|
||||||
|
memcpy(skb_put(skb, dlen), data, dlen);
|
||||||
|
|
||||||
|
return skb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
|
||||||
|
|
||||||
|
BT_DBG("code 0x%2.2x", code);
|
||||||
|
|
||||||
|
if (!skb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hci_send_acl(conn->hcon, skb, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __u8 seclevel_to_authreq(__u8 level)
|
||||||
|
{
|
||||||
|
switch (level) {
|
||||||
|
case BT_SECURITY_HIGH:
|
||||||
|
/* Right now we don't support bonding */
|
||||||
|
return SMP_AUTH_MITM;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SMP_AUTH_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||||
|
struct smp_cmd_pairing *cmd, __u8 authreq)
|
||||||
|
{
|
||||||
|
cmd->io_capability = conn->hcon->io_capability;
|
||||||
|
cmd->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||||
|
cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||||
|
cmd->init_key_dist = 0x00;
|
||||||
|
cmd->resp_key_dist = 0x00;
|
||||||
|
cmd->auth_req = authreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||||
|
{
|
||||||
|
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||||
|
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||||
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
|
||||||
|
conn->smp_key_size = max_key_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
|
||||||
|
u8 key_size;
|
||||||
|
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
|
memcpy(&conn->preq[1], req, sizeof(*req));
|
||||||
|
skb_pull(skb, sizeof(*req));
|
||||||
|
|
||||||
|
if (req->oob_flag)
|
||||||
|
return SMP_OOB_NOT_AVAIL;
|
||||||
|
|
||||||
|
/* We didn't start the pairing, so no requirements */
|
||||||
|
build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
|
||||||
|
|
||||||
|
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||||
|
if (check_enc_key_size(conn, key_size))
|
||||||
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
|
||||||
|
/* Just works */
|
||||||
|
memset(conn->tk, 0, sizeof(conn->tk));
|
||||||
|
|
||||||
|
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||||
|
memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
|
||||||
|
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
|
||||||
|
|
||||||
|
mod_timer(&conn->security_timer, jiffies +
|
||||||
|
msecs_to_jiffies(SMP_TIMEOUT));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
|
||||||
|
struct smp_cmd_pairing_confirm cp;
|
||||||
|
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||||
|
int ret;
|
||||||
|
u8 res[16], key_size;
|
||||||
|
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(*rsp));
|
||||||
|
|
||||||
|
req = (void *) &conn->preq[1];
|
||||||
|
|
||||||
|
key_size = min(req->max_key_size, rsp->max_key_size);
|
||||||
|
if (check_enc_key_size(conn, key_size))
|
||||||
|
return SMP_ENC_KEY_SIZE;
|
||||||
|
|
||||||
|
if (rsp->oob_flag)
|
||||||
|
return SMP_OOB_NOT_AVAIL;
|
||||||
|
|
||||||
|
/* Just works */
|
||||||
|
memset(conn->tk, 0, sizeof(conn->tk));
|
||||||
|
|
||||||
|
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||||
|
memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
|
||||||
|
|
||||||
|
ret = smp_rand(conn->prnd);
|
||||||
|
if (ret)
|
||||||
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
|
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
||||||
|
conn->src, conn->hcon->dst_type, conn->dst, res);
|
||||||
|
if (ret)
|
||||||
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
|
swap128(res, cp.confirm_val);
|
||||||
|
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||||
|
|
||||||
|
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||||
|
|
||||||
|
memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
|
||||||
|
skb_pull(skb, sizeof(conn->pcnf));
|
||||||
|
|
||||||
|
if (conn->hcon->out) {
|
||||||
|
u8 random[16];
|
||||||
|
|
||||||
|
swap128(conn->prnd, random);
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
||||||
|
random);
|
||||||
|
} else {
|
||||||
|
struct smp_cmd_pairing_confirm cp;
|
||||||
|
int ret;
|
||||||
|
u8 res[16];
|
||||||
|
|
||||||
|
ret = smp_rand(conn->prnd);
|
||||||
|
if (ret)
|
||||||
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
|
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
|
||||||
|
conn->hcon->dst_type, conn->dst,
|
||||||
|
0, conn->src, res);
|
||||||
|
if (ret)
|
||||||
|
return SMP_CONFIRM_FAILED;
|
||||||
|
|
||||||
|
swap128(res, cp.confirm_val);
|
||||||
|
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_timer(&conn->security_timer, jiffies +
|
||||||
|
msecs_to_jiffies(SMP_TIMEOUT));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
|
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
||||||
|
int ret;
|
||||||
|
u8 key[16], res[16], random[16], confirm[16];
|
||||||
|
|
||||||
|
swap128(skb->data, random);
|
||||||
|
skb_pull(skb, sizeof(random));
|
||||||
|
|
||||||
|
memset(hcon->ltk, 0, sizeof(hcon->ltk));
|
||||||
|
|
||||||
|
if (conn->hcon->out)
|
||||||
|
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
|
||||||
|
conn->src, conn->hcon->dst_type, conn->dst,
|
||||||
|
res);
|
||||||
|
else
|
||||||
|
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
|
||||||
|
conn->hcon->dst_type, conn->dst, 0, conn->src,
|
||||||
|
res);
|
||||||
|
if (ret)
|
||||||
|
return SMP_UNSPECIFIED;
|
||||||
|
|
||||||
|
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||||
|
|
||||||
|
swap128(res, confirm);
|
||||||
|
|
||||||
|
if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
|
||||||
|
BT_ERR("Pairing failed (confirmation values mismatch)");
|
||||||
|
return SMP_CONFIRM_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->hcon->out) {
|
||||||
|
__le16 ediv;
|
||||||
|
u8 rand[8];
|
||||||
|
|
||||||
|
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
||||||
|
swap128(key, hcon->ltk);
|
||||||
|
|
||||||
|
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||||
|
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||||
|
|
||||||
|
memset(rand, 0, sizeof(rand));
|
||||||
|
ediv = 0;
|
||||||
|
hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
|
||||||
|
} else {
|
||||||
|
u8 r[16];
|
||||||
|
|
||||||
|
swap128(conn->prnd, r);
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
|
||||||
|
|
||||||
|
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
||||||
|
swap128(key, hcon->ltk);
|
||||||
|
|
||||||
|
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||||
|
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct smp_cmd_security_req *rp = (void *) skb->data;
|
||||||
|
struct smp_cmd_pairing cp;
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
|
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(*rp));
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
build_pairing_cmd(conn, &cp, rp->auth_req);
|
||||||
|
|
||||||
|
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
|
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||||
|
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
mod_timer(&conn->security_timer, jiffies +
|
||||||
|
msecs_to_jiffies(SMP_TIMEOUT));
|
||||||
|
|
||||||
|
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||||
|
{
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
|
__u8 authreq;
|
||||||
|
|
||||||
|
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||||
|
|
||||||
|
if (IS_ERR(hcon->hdev->tfm))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (sec_level == BT_SECURITY_LOW)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (hcon->sec_level >= sec_level)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
authreq = seclevel_to_authreq(sec_level);
|
||||||
|
|
||||||
|
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||||
|
struct smp_cmd_pairing cp;
|
||||||
|
|
||||||
|
build_pairing_cmd(conn, &cp, authreq);
|
||||||
|
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||||
|
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||||
|
|
||||||
|
mod_timer(&conn->security_timer, jiffies +
|
||||||
|
msecs_to_jiffies(SMP_TIMEOUT));
|
||||||
|
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||||
|
} else {
|
||||||
|
struct smp_cmd_security_req cp;
|
||||||
|
cp.auth_req = authreq;
|
||||||
|
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
hcon->pending_sec_level = sec_level;
|
||||||
|
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
__u8 code = skb->data[0];
|
||||||
|
__u8 reason;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if (IS_ERR(conn->hcon->hdev->tfm)) {
|
||||||
|
err = PTR_ERR(conn->hcon->hdev->tfm);
|
||||||
|
reason = SMP_PAIRING_NOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb_pull(skb, sizeof(code));
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case SMP_CMD_PAIRING_REQ:
|
||||||
|
reason = smp_cmd_pairing_req(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_PAIRING_FAIL:
|
||||||
|
reason = 0;
|
||||||
|
err = -EPERM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_PAIRING_RSP:
|
||||||
|
reason = smp_cmd_pairing_rsp(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_SECURITY_REQ:
|
||||||
|
reason = smp_cmd_security_req(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_PAIRING_CONFIRM:
|
||||||
|
reason = smp_cmd_pairing_confirm(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_PAIRING_RANDOM:
|
||||||
|
reason = smp_cmd_pairing_random(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMP_CMD_ENCRYPT_INFO:
|
||||||
|
case SMP_CMD_MASTER_IDENT:
|
||||||
|
case SMP_CMD_IDENT_INFO:
|
||||||
|
case SMP_CMD_IDENT_ADDR_INFO:
|
||||||
|
case SMP_CMD_SIGN_INFO:
|
||||||
|
default:
|
||||||
|
BT_DBG("Unknown command code 0x%2.2x", code);
|
||||||
|
|
||||||
|
reason = SMP_CMD_NOTSUPP;
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (reason)
|
||||||
|
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
|
||||||
|
&reason);
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
return err;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user