#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#define VERSION "2.12"
+#define VERSION "2.13"
-static u32 l2cap_feat_mask = 0x0000;
+static u32 l2cap_feat_mask = 0x0080;
+static u8 l2cap_fixed_chan[8] = { 0x02, };
static const struct proto_ops l2cap_sock_ops;
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+ conn->disc_reason = 0x13;
+
l2cap_pi(sk)->conn = conn;
if (sk->sk_type == SOCK_SEQPACKET) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
__u8 auth_type;
- switch (l2cap_pi(sk)->sec_level) {
- case BT_SECURITY_HIGH:
- auth_type = HCI_AT_GENERAL_BONDING_MITM;
- break;
- case BT_SECURITY_MEDIUM:
- auth_type = HCI_AT_GENERAL_BONDING;
- break;
- default:
- auth_type = HCI_AT_NO_BONDING;
- break;
+ if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
+ auth_type = HCI_AT_NO_BONDING_MITM;
+ else
+ auth_type = HCI_AT_NO_BONDING;
+
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
+ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
+ } else {
+ switch (l2cap_pi(sk)->sec_level) {
+ case BT_SECURITY_HIGH:
+ auth_type = HCI_AT_GENERAL_BONDING_MITM;
+ break;
+ case BT_SECURITY_MEDIUM:
+ auth_type = HCI_AT_GENERAL_BONDING;
+ break;
+ default:
+ auth_type = HCI_AT_NO_BONDING;
+ break;
+ }
}
return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+ return;
+
if (l2cap_check_security(sk)) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
{
struct l2cap_conn *conn = (void *) arg;
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
spin_lock_init(&conn->lock);
rwlock_init(&conn->chan_list.lock);
+ conn->disc_reason = 0x13;
+
return conn;
}
return 0;
}
-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
- int err = 0;
+ struct sockaddr_l2 la;
+ int len, err = 0;
- BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+ BT_DBG("sk %p", sk);
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
+ memset(&la, 0, sizeof(la));
+ len = min_t(unsigned int, sizeof(la), alen);
+ memcpy(&la, addr, len);
+
+ if (la.l2_cid)
+ return -EINVAL;
+
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
goto done;
}
- if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&
+ if (la.l2_psm && btohs(la.l2_psm) < 0x1001 &&
!capable(CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto done;
write_lock_bh(&l2cap_sk_list.lock);
- if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
+ if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
err = -EADDRINUSE;
} else {
/* Save source address */
- bacpy(&bt_sk(sk)->src, &la->l2_bdaddr);
- l2cap_pi(sk)->psm = la->l2_psm;
- l2cap_pi(sk)->sport = la->l2_psm;
+ bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+ l2cap_pi(sk)->psm = la.l2_psm;
+ l2cap_pi(sk)->sport = la.l2_psm;
sk->sk_state = BT_BOUND;
- if (btohs(la->l2_psm) == 0x0001)
+ if (btohs(la.l2_psm) == 0x0001 || btohs(la.l2_psm) == 0x0003)
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
}
__u8 auth_type;
int err = 0;
- BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
+ l2cap_pi(sk)->psm);
if (!(hdev = hci_get_route(dst, src)))
return -EHOSTUNREACH;
auth_type = HCI_AT_NO_BONDING_MITM;
else
auth_type = HCI_AT_NO_BONDING;
+
+ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
+ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
} else {
switch (l2cap_pi(sk)->sec_level) {
case BT_SECURITY_HIGH:
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
{
- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
- int err = 0;
-
- lock_sock(sk);
+ struct sockaddr_l2 la;
+ int len, err = 0;
BT_DBG("sk %p", sk);
- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
- err = -EINVAL;
- goto done;
- }
+ if (!addr || addr->sa_family != AF_BLUETOOTH)
+ return -EINVAL;
- if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {
+ memset(&la, 0, sizeof(la));
+ len = min_t(unsigned int, sizeof(la), alen);
+ memcpy(&la, addr, len);
+
+ if (la.l2_cid)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) {
err = -EINVAL;
goto done;
}
}
/* Set destination address and psm */
- bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr);
- l2cap_pi(sk)->psm = la->l2_psm;
+ bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
+ l2cap_pi(sk)->psm = la.l2_psm;
if ((err = l2cap_do_connect(sk)))
goto done;
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
- if (peer)
+ if (peer) {
+ la->l2_psm = l2cap_pi(sk)->psm;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
- else
+ la->l2_cid = htobs(l2cap_pi(sk)->dcid);
+ } else {
+ la->l2_psm = l2cap_pi(sk)->sport;
bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
+ la->l2_cid = htobs(l2cap_pi(sk)->scid);
+ }
- la->l2_psm = l2cap_pi(sk)->psm;
return 0;
}
{
struct sock *sk = sock->sk;
struct l2cap_options opts;
- int err = 0, len;
+ int len, err = 0;
u32 opt;
BT_DBG("sk %p", sk);
switch (optname) {
case BT_SECURITY:
- if (sk->sk_type != SOCK_SEQPACKET) {
+ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
err = -EINVAL;
break;
}
switch (optname) {
case BT_SECURITY:
- if (sk->sk_type != SOCK_SEQPACKET) {
+ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
err = -EINVAL;
break;
}
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) {
- conn->info_ident = 0;
del_timer(&conn->info_timer);
+
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
l2cap_conn_start(conn);
}
/* Check if the ACL is secure enough (if not SDP) */
if (psm != cpu_to_le16(0x0001) &&
!hci_conn_check_link_mode(conn->hcon)) {
+ conn->disc_reason = 0x05;
result = L2CAP_CR_SEC_BLOCK;
goto response;
}
l2cap_pi(sk)->ident = cmd->ident;
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(sk)) {
if (bt_sk(sk)->defer_setup) {
sk->sk_state = BT_CONNECT2;
l2cap_pi(sk)->dcid = dcid;
l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+ l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(sk, req), req);
break;
case L2CAP_CR_PEND:
+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
break;
default:
put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
+ } else if (type == L2CAP_IT_FIXED_CHAN) {
+ u8 buf[12];
+ struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
+ rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+ rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
+ memcpy(buf + 4, l2cap_fixed_chan, 8);
+ l2cap_send_cmd(conn, cmd->ident,
+ L2CAP_INFO_RSP, sizeof(buf), buf);
} else {
struct l2cap_info_rsp rsp;
rsp.type = cpu_to_le16(type);
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
- conn->info_ident = 0;
-
del_timer(&conn->info_timer);
- if (type == L2CAP_IT_FEAT_MASK)
+ if (type == L2CAP_IT_FEAT_MASK) {
conn->feat_mask = get_unaligned_le32(rsp->data);
- l2cap_conn_start(conn);
+ if (conn->feat_mask & 0x0080) {
+ struct l2cap_info_req req;
+ req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
+
+ conn->info_ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, conn->info_ident,
+ L2CAP_INFO_REQ, sizeof(req), &req);
+ } else {
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+ }
+ } else if (type == L2CAP_IT_FIXED_CHAN) {
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+ }
return 0;
}
return 0;
}
-static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
+static int l2cap_disconn_ind(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+
+ BT_DBG("hcon %p", hcon);
+
+ if (hcon->type != ACL_LINK || !conn)
+ return 0x13;
+
+ return conn->disc_reason;
+}
+
+static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
{
BT_DBG("hcon %p reason %d", hcon, reason);
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
bh_lock_sock(sk);
+ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
+ bh_unlock_sock(sk);
+ continue;
+ }
+
if (!status && (sk->sk_state == BT_CONNECTED ||
sk->sk_state == BT_CONFIG)) {
l2cap_check_encryption(sk, encrypt);
goto drop;
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len = len - skb->len;
} else {
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
}
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len -= skb->len;
if (!conn->rx_len) {
.connect_ind = l2cap_connect_ind,
.connect_cfm = l2cap_connect_cfm,
.disconn_ind = l2cap_disconn_ind,
+ .disconn_cfm = l2cap_disconn_cfm,
.security_cfm = l2cap_security_cfm,
.recv_acldata = l2cap_recv_acldata
};