static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; struct sock *sk2 = NULL; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; /* If this is an ATT socket, find it's matching server/client */ if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA) sk2 = l2cap_find_sock_by_fixed_cid_and_dir(L2CAP_CID_LE_DATA, &bt_sk(sk)->src, &bt_sk(sk)->dst, l2cap_pi(sk)->incoming ? 0 : 1); /* If matching socket found, request tear down */ BT_DBG("sock:%p companion:%p", sk, sk2); if (sk2) l2cap_sock_set_timer(sk2, 1); err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err; }
/* ---- L2CAP timers ---- */ static void l2cap_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; int reason; BT_DBG("sock %p state %d", sk, sk->sk_state); bh_lock_sock(sk); if (sock_owned_by_user(sk)) { /* sk is owned by user. Try again later */ l2cap_sock_set_timer(sk, HZ / 5); bh_unlock_sock(sk); sock_put(sk); return; } if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) reason = ECONNREFUSED; else if (sk->sk_state == BT_CONNECT && l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; __l2cap_sock_close(sk, reason); bh_unlock_sock(sk); l2cap_sock_kill(sk); sock_put(sk); }
static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; struct sock *srv_sk = NULL; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; /* If this is an ATT Client socket, find the matching Server */ if (l2cap_pi(sk)->scid == L2CAP_CID_LE_DATA && !l2cap_pi(sk)->incoming) srv_sk = l2cap_find_sock_by_fixed_cid_and_dir(L2CAP_CID_LE_DATA, &bt_sk(sk)->src, &bt_sk(sk)->dst, 1); /* If server socket found, request tear down */ BT_DBG("client:%p server:%p", sk, srv_sk); if (srv_sk) l2cap_sock_set_timer(srv_sk, 1); err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err; }
void __l2cap_sock_close(struct sock *sk, int reason) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); switch (sk->sk_state) { case BT_LISTEN: l2cap_sock_cleanup_listen(sk); break; case BT_CONNECTED: case BT_CONFIG: if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) && conn->hcon->type == ACL_LINK) { l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, sk, reason); } else l2cap_chan_del(sk, reason); break; case BT_CONNECT2: if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) && conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; if (bt_sk(sk)->defer_setup) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; sk->sk_state = BT_DISCONN; rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); } l2cap_chan_del(sk, reason); break; case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(sk, reason); break; default: sock_set_flag(sk, SOCK_ZAPPED); break; } }
static int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bluez_pi(sk)->src; bdaddr_t *dst = &bluez_pi(sk)->dst; struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; int err = 0; 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; hci_dev_lock_bh(hdev); err = -ENOMEM; hcon = hci_connect(hdev, ACL_LINK, dst); if (!hcon) goto done; conn = l2cap_conn_add(hcon, 0); if (!conn) { hci_conn_put(hcon); goto done; } err = 0; /* Update source addr of the socket */ bacpy(src, conn->src); l2cap_chan_add(conn, sk, NULL); sk->state = BT_CONNECT; l2cap_sock_set_timer(sk, sk->sndtimeo); if (hcon->state == BT_CONNECTED) { if (sk->type == SOCK_SEQPACKET) { l2cap_conn_req req; req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); } else { l2cap_sock_clear_timer(sk); sk->state = BT_CONNECTED; } } done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; }
static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status) { struct l2cap_chan_list *l; struct l2cap_conn *conn; l2cap_conn_rsp rsp; struct sock *sk; int result; if (!(conn = hcon->l2cap_data)) return 0; l = &conn->chan_list; BT_DBG("conn %p", conn); read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); if (sk->state != BT_CONNECT2 || (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) { bh_unlock_sock(sk); continue; } if (!status) { sk->state = BT_CONFIG; result = 0; } else { sk->state = BT_DISCONN; l2cap_sock_set_timer(sk, HZ/10); result = L2CAP_CR_SEC_BLOCK; } rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); rsp.result = __cpu_to_le16(result); rsp.status = __cpu_to_le16(0); l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp); bh_unlock_sock(sk); } read_unlock(&l->lock); return 0; }
/* Close socket. */ static void __l2cap_sock_close(struct sock *sk, int reason) { BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); switch (sk->state) { case BT_LISTEN: l2cap_sock_cleanup_listen(sk); break; case BT_CONNECTED: case BT_CONFIG: case BT_CONNECT2: if (sk->type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; l2cap_disconn_req req; sk->state = BT_DISCONN; l2cap_sock_set_timer(sk, sk->sndtimeo); req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); } else { l2cap_chan_del(sk, reason); } break; case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(sk, reason); break; default: sk->zapped = 1; break; }; }
static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) { l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data; __u16 scid, flags, result; struct sock *sk; int err = 0; scid = __le16_to_cpu(rsp->scid); flags = __le16_to_cpu(rsp->flags); result = __le16_to_cpu(rsp->result); BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result); if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) return -ENOENT; switch (result) { case L2CAP_CONF_SUCCESS: break; case L2CAP_CONF_UNACCEPT: if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) { char req[128]; /* It does not make sense to adjust L2CAP parameters that are currently defined in the spec. We simply resend config request that we sent earlier. It is stupid :) but it helps qualification testing which expects at least some response from us. */ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); goto done; } default: sk->state = BT_DISCONN; sk->err = ECONNRESET; l2cap_sock_set_timer(sk, HZ * 5); { l2cap_disconn_req req; req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); } goto done; } if (flags & 0x01) goto done; /* Input config done */ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { sk->state = BT_CONNECTED; l2cap_chan_ready(sk); } done: bh_unlock_sock(sk); return err; }
static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) { struct l2cap_chan_list *list = &conn->chan_list; l2cap_conn_req *req = (l2cap_conn_req *) data; l2cap_conn_rsp rsp; struct sock *sk, *parent; int result = 0, status = 0; __u16 dcid = 0, scid = __le16_to_cpu(req->scid); __u16 psm = req->psm; BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); /* Check if we have socket listening on psm */ parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src); if (!parent) { result = L2CAP_CR_BAD_PSM; goto sendresp; } result = L2CAP_CR_NO_MEM; /* Check for backlog size */ if (parent->ack_backlog > parent->max_ack_backlog) { BT_DBG("backlog full %d", parent->ack_backlog); goto response; } sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC); if (!sk) goto response; write_lock(&list->lock); /* Check if we already have channel with that dcid */ if (__l2cap_get_chan_by_dcid(list, scid)) { write_unlock(&list->lock); sk->zapped = 1; l2cap_sock_kill(sk); goto response; } hci_conn_hold(conn->hcon); l2cap_sock_init(sk, parent); bacpy(&bluez_pi(sk)->src, conn->src); bacpy(&bluez_pi(sk)->dst, conn->dst); l2cap_pi(sk)->psm = psm; l2cap_pi(sk)->dcid = scid; __l2cap_chan_add(conn, sk, parent); dcid = l2cap_pi(sk)->scid; l2cap_sock_set_timer(sk, sk->sndtimeo); /* Service level security */ result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHEN_PEND; sk->state = BT_CONNECT2; l2cap_pi(sk)->ident = cmd->ident; if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) { if (!hci_conn_encrypt(conn->hcon)) goto done; } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) { if (!hci_conn_auth(conn->hcon)) goto done; } sk->state = BT_CONFIG; result = status = 0; done: write_unlock(&list->lock); response: bh_unlock_sock(parent); sendresp: rsp.scid = __cpu_to_le16(scid); rsp.dcid = __cpu_to_le16(dcid); rsp.result = __cpu_to_le16(result); rsp.status = __cpu_to_le16(status); l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp); return 0; }