📄 l2cap.c
字号:
.name = "L2CAP", .owner = THIS_MODULE, .obj_size = sizeof(struct l2cap_pinfo)};static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio){ struct sock *sk; sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); if (!sk) return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); sock_reset_flag(sk, SOCK_ZAPPED); 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 net *net, 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(net, sock, protocol, GFP_ATOMIC); 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; } 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)) { 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 (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) { l2cap_conn_ready(conn); goto done; } if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); req.scid = cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, 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(htobs(psm), src)) { l2cap_pi(sk)->psm = htobs(psm); l2cap_pi(sk)->sport = htobs(psm); err = 0; break; } write_unlock_bh(&l2cap_sk_list.lock); if (err < 0) goto done; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN;done: release_sock(sk); return err;}static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags){ DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; long timeo; int err = 0; lock_sock_nested(sk, SINGLE_DEPTH_NESTING); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; goto done; } timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sk_sleep, &wait); while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock_nested(sk, SINGLE_DEPTH_NESTING); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } } set_current_state(TASK_RUNNING); remove_wait_queue(sk->sk_sleep, &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", nsk);done: release_sock(sk); return err;}static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer){ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); if (peer) bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); else bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; return 0;}static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len){ struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb, **frag; int err, hlen, count, sent=0; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, len); /* First fragment (with L2CAP header) */ if (sk->sk_type == SOCK_DGRAM) hlen = L2CAP_HDR_SIZE + 2; else hlen = L2CAP_HDR_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); if (sk->sk_type == SOCK_DGRAM) put_unaligned(l2cap_pi(sk)->psm, (__le16 *) skb_put(skb, 2)); if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) goto fail; if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; frag = &(*frag)->next; } if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0) goto fail; return sent;fail: kfree_skb(skb); return err;}static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); err = sock_error(sk); if (err) return err; if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; /* Check outgoing MTU */ if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu) return -EINVAL; lock_sock(sk); if (sk->sk_state == BT_CONNECTED) err = l2cap_do_send(sk, msg, len); else err = -ENOTCONN; release_sock(sk); return err;}static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen){ struct sock *sk = sock->sk; struct l2cap_options opts; int err = 0, len; u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = L2CAP_MODE_BASIC; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { err = -EFAULT; break; } l2cap_pi(sk)->imtu = opts.imtu; l2cap_pi(sk)->omtu = opts.omtu; break; case L2CAP_LM: if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } l2cap_pi(sk)->link_mode = opt; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err;}static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ struct sock *sk = sock->sk; struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = L2CAP_MODE_BASIC; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) err = -EFAULT; break; case L2CAP_LM: if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval)) err = -EFAULT; break; case L2CAP_CONNINFO: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err;}static int l2cap_sock_shutdown(struct socket *sock, int how){ struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); __l2cap_sock_close(sk, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } release_sock(sk); return err;}static int l2cap_sock_release(struct socket *sock){ struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err;}static void l2cap_chan_ready(struct sock *sk){ struct sock *parent = bt_sk(sk)->parent; BT_DBG("sk %p, parent %p", sk, parent); l2cap_pi(sk)->conf_state = 0; l2cap_sock_clear_timer(sk); if (!parent) { /* Outgoing channel. * Wake up socket sleeping on connect. */ sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else { /* Incoming channel. * Wake up socket sleeping on accept. */ parent->sk_data_ready(parent, 0); }}/* Copy frame to all raw sockets on that connection */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -