📄 l2cap.c
字号:
/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> 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.*//* Bluetooth L2CAP core and sockets. */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/socket.h>#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/list.h>#include <net/sock.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/unaligned.h>#include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h>#include <net/bluetooth/l2cap.h>#ifndef CONFIG_BT_L2CAP_DEBUG#undef BT_DBG#define BT_DBG(D...)#endif#define VERSION "2.3"static struct proto_ops l2cap_sock_ops;struct bt_sock_list l2cap_sk_list = { .lock = RW_LOCK_UNLOCKED};static int l2cap_conn_del(struct hci_conn *conn, int err);static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);static void l2cap_chan_del(struct sock *sk, int err);static void __l2cap_sock_close(struct sock *sk, int reason);static void l2cap_sock_close(struct sock *sk);static void l2cap_sock_kill(struct sock *sk);static int l2cap_send_req(struct l2cap_conn *conn, u8 code, u16 len, void *data);static int l2cap_send_rsp(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data);/* ---- L2CAP timers ---- */static void l2cap_sock_timeout(unsigned long arg){ struct sock *sk = (struct sock *) arg; BT_DBG("sock %p state %d", sk, sk->sk_state); bh_lock_sock(sk); __l2cap_sock_close(sk, ETIMEDOUT); bh_unlock_sock(sk); l2cap_sock_kill(sk); sock_put(sk);}static void l2cap_sock_set_timer(struct sock *sk, long timeout){ BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout);}static void l2cap_sock_clear_timer(struct sock *sk){ BT_DBG("sock %p state %d", sk, sk->sk_state); sk_stop_timer(sk, &sk->sk_timer);}static void l2cap_sock_init_timer(struct sock *sk){ init_timer(&sk->sk_timer); sk->sk_timer.function = l2cap_sock_timeout; sk->sk_timer.data = (unsigned long)sk;}/* ---- L2CAP connections ---- */static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status){ struct l2cap_conn *conn; if ((conn = hcon->l2cap_data)) return conn; if (status) return conn; if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC))) return NULL; memset(conn, 0, sizeof(struct l2cap_conn)); hcon->l2cap_data = conn; conn->hcon = hcon; conn->mtu = hcon->hdev->acl_mtu; conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; spin_lock_init(&conn->lock); conn->chan_list.lock = RW_LOCK_UNLOCKED; BT_DBG("hcon %p conn %p", hcon, conn); return conn;}static int l2cap_conn_del(struct hci_conn *hcon, int err){ struct l2cap_conn *conn; struct sock *sk; if (!(conn = hcon->l2cap_data)) return 0; BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); if (conn->rx_skb) kfree_skb(conn->rx_skb); /* Kill channels */ while ((sk = conn->chan_list.head)) { bh_lock_sock(sk); l2cap_chan_del(sk, err); bh_unlock_sock(sk); l2cap_sock_kill(sk); } hcon->l2cap_data = NULL; kfree(conn); return 0;}static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent){ struct l2cap_chan_list *l = &conn->chan_list; write_lock(&l->lock); __l2cap_chan_add(conn, sk, parent); write_unlock(&l->lock);}/* ---- Socket interface ---- */static struct sock *__l2cap_get_sock_by_addr(u16 psm, bdaddr_t *src){ struct sock *sk; struct hlist_node *node; sk_for_each(sk, node, &l2cap_sk_list.head) if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) goto found; sk = NULL;found: return sk;}/* Find socket with psm and source bdaddr. * Returns closest match. */static struct sock *__l2cap_get_sock_by_psm(int state, u16 psm, bdaddr_t *src){ struct sock *sk = NULL, *sk1 = NULL; struct hlist_node *node; sk_for_each(sk, node, &l2cap_sk_list.head) { if (state && sk->sk_state != state) continue; if (l2cap_pi(sk)->psm == psm) { /* Exact match. */ if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } } return node ? sk : sk1;}/* Find socket with given address (psm, src). * Returns locked socket */static inline struct sock *l2cap_get_sock_by_psm(int state, u16 psm, bdaddr_t *src){ struct sock *s; read_lock(&l2cap_sk_list.lock); s = __l2cap_get_sock_by_psm(state, psm, src); if (s) bh_lock_sock(s); read_unlock(&l2cap_sk_list.lock); return s;}static void l2cap_sock_destruct(struct sock *sk){ BT_DBG("sk %p", sk); skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); if (sk->sk_protinfo) kfree(sk->sk_protinfo);}static void l2cap_sock_cleanup_listen(struct sock *parent){ struct sock *sk; BT_DBG("parent %p", parent); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) l2cap_sock_close(sk); parent->sk_state = BT_CLOSED; parent->sk_zapped = 1;}/* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */static void l2cap_sock_kill(struct sock *sk){ if (!sk->sk_zapped || sk->sk_socket) return; BT_DBG("sk %p state %d", sk, sk->sk_state); /* Kill poor orphan */ bt_sock_unlink(&l2cap_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); sock_put(sk);}static void __l2cap_sock_close(struct sock *sk, int reason){ 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: case BT_CONNECT2: if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_disconn_req req; sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, 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, sizeof(req), &req); } else { l2cap_chan_del(sk, reason); } break; case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(sk, reason); break; default: sk->sk_zapped = 1; break; }}/* Must be called on unlocked socket. */static void l2cap_sock_close(struct sock *sk){ l2cap_sock_clear_timer(sk); lock_sock(sk); __l2cap_sock_close(sk, ECONNRESET); release_sock(sk); l2cap_sock_kill(sk);}static void l2cap_sock_init(struct sock *sk, struct sock *parent){ struct l2cap_pinfo *pi = l2cap_pi(sk); BT_DBG("sk %p", sk); if (parent) { sk->sk_type = parent->sk_type; pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; pi->link_mode = l2cap_pi(parent)->link_mode; } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; pi->link_mode = 0; } /* Default config options */ pi->conf_mtu = L2CAP_DEFAULT_MTU; pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;}static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio){ struct sock *sk; sk = bt_sock_alloc(sock, proto, sizeof(struct l2cap_pinfo), prio); if (!sk) return NULL; sk_set_owner(sk, THIS_MODULE); sk->sk_destruct = l2cap_sock_destruct; sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sk->sk_protocol = proto; sk->sk_state = BT_OPEN; l2cap_sock_init_timer(sk); bt_sock_link(&l2cap_sk_list, sk); return sk;}static int l2cap_sock_create(struct socket *sock, int protocol){ struct sock *sk; BT_DBG("sock %p", sock); sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) return -EPERM; sock->ops = &l2cap_sock_ops; sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL); if (!sk) return -ENOMEM; l2cap_sock_init(sk, NULL); return 0;}static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len){ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } write_lock_bh(&l2cap_sk_list.lock); 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; sk->sk_state = BT_BOUND; } write_unlock_bh(&l2cap_sk_list.lock);done: release_sock(sk); return err;}static int l2cap_do_connect(struct sock *sk){ bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(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->sk_state = BT_CONNECT; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (sk->sk_type == SOCK_SEQPACKET) { struct 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, sizeof(req), &req); } else { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; } }done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err;}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); BT_DBG("sk %p", sk); if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { err = -EINVAL; goto done; } if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) { err = -EINVAL; goto done; } switch(sk->sk_state) { case BT_CONNECT: case BT_CONNECT2: case BT_CONFIG: /* Already connecting */ goto wait; case BT_CONNECTED: /* Already connected */ goto done; case BT_OPEN: case BT_BOUND: /* Can connect */ break; default: err = -EBADFD; goto done; } /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr); l2cap_pi(sk)->psm = la->l2_psm; if ((err = l2cap_do_connect(sk))) goto done;wait: err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK));done: release_sock(sk); return err;}static int l2cap_sock_listen(struct socket *sock, int backlog){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { err = -EBADFD; goto done; } if (!l2cap_pi(sk)->psm) { bdaddr_t *src = &bt_sk(sk)->src; u16 psm; err = -EINVAL; write_lock_bh(&l2cap_sk_list.lock); for (psm = 0x1001; psm < 0x1100; psm += 2) if (!__l2cap_get_sock_by_addr(psm, src)) { l2cap_pi(sk)->psm = htobs(psm); l2cap_pi(sk)->sport = htobs(psm); err = 0; break; } write_unlock_bh(&l2cap_sk_list.lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -