|
@@ -906,6 +906,18 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
|
|
atomic_inc(&monitor_promisc);
|
|
|
break;
|
|
|
|
|
|
+ case HCI_CHANNEL_LOGGING:
|
|
|
+ if (haddr.hci_dev != HCI_DEV_NONE) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!capable(CAP_NET_ADMIN)) {
|
|
|
+ err = -EPERM;
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
if (!hci_mgmt_chan_find(haddr.hci_channel)) {
|
|
|
err = -EINVAL;
|
|
@@ -1033,6 +1045,9 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
|
|
if (flags & MSG_OOB)
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
+ if (hci_pi(sk)->channel == HCI_CHANNEL_LOGGING)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (sk->sk_state == BT_CLOSED)
|
|
|
return 0;
|
|
|
|
|
@@ -1179,6 +1194,90 @@ done:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
|
|
|
+{
|
|
|
+ struct hci_mon_hdr *hdr;
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct hci_dev *hdev;
|
|
|
+ u16 index;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* The logging frame consists at minimum of the standard header,
|
|
|
+ * the priority byte, the ident length byte and at least one string
|
|
|
+ * terminator NUL byte. Anything shorter are invalid packets.
|
|
|
+ */
|
|
|
+ if (len < sizeof(*hdr) + 3)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
|
|
|
+ if (!skb)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
|
|
|
+ err = -EFAULT;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+
|
|
|
+ hdr = (void *)skb->data;
|
|
|
+
|
|
|
+ if (__le16_to_cpu(hdr->len) != len - sizeof(*hdr)) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (__le16_to_cpu(hdr->opcode) == 0x0000) {
|
|
|
+ __u8 priority = skb->data[sizeof(*hdr)];
|
|
|
+ __u8 ident_len = skb->data[sizeof(*hdr) + 1];
|
|
|
+
|
|
|
+ /* Only the priorities 0-7 are valid and with that any other
|
|
|
+ * value results in an invalid packet.
|
|
|
+ *
|
|
|
+ * The priority byte is followed by an ident length byte and
|
|
|
+ * the NUL terminated ident string. Check that the ident
|
|
|
+ * length is not overflowing the packet and also that the
|
|
|
+ * ident string itself is NUL terminated. In case the ident
|
|
|
+ * length is zero, the length value actually doubles as NUL
|
|
|
+ * terminator identifier.
|
|
|
+ *
|
|
|
+ * The message follows the ident string (if present) and
|
|
|
+ * must be NUL terminated. Otherwise it is not a valid packet.
|
|
|
+ */
|
|
|
+ if (priority > 7 || skb->data[len - 1] != 0x00 ||
|
|
|
+ ident_len > len - sizeof(*hdr) - 3 ||
|
|
|
+ skb->data[sizeof(*hdr) + ident_len + 1] != 0x00) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+
|
|
|
+ index = __le16_to_cpu(hdr->index);
|
|
|
+
|
|
|
+ if (index != MGMT_INDEX_NONE) {
|
|
|
+ hdev = hci_dev_get(index);
|
|
|
+ if (!hdev) {
|
|
|
+ err = -ENODEV;
|
|
|
+ goto drop;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ hdev = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ hdr->opcode = cpu_to_le16(HCI_MON_USER_LOGGING);
|
|
|
+
|
|
|
+ hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, HCI_SOCK_TRUSTED, NULL);
|
|
|
+ err = len;
|
|
|
+
|
|
|
+ if (hdev)
|
|
|
+ hci_dev_put(hdev);
|
|
|
+
|
|
|
+drop:
|
|
|
+ kfree_skb(skb);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
|
size_t len)
|
|
|
{
|
|
@@ -1208,6 +1307,9 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
|
case HCI_CHANNEL_MONITOR:
|
|
|
err = -EOPNOTSUPP;
|
|
|
goto done;
|
|
|
+ case HCI_CHANNEL_LOGGING:
|
|
|
+ err = hci_logging_frame(sk, msg, len);
|
|
|
+ goto done;
|
|
|
default:
|
|
|
mutex_lock(&mgmt_chan_list_lock);
|
|
|
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
|