Bluetooth: LE_ADV_IND filtering for GFRM200
Change-Id: I915332328f17629af5236359c45d49375fe8dc63
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a056c2b..4ce220d 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -199,6 +199,7 @@
bdaddr_t public_addr;
bdaddr_t random_addr;
bdaddr_t static_addr;
+ bdaddr_t le_adv_ind_filter;
__u8 adv_addr_type;
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
@@ -1225,6 +1226,41 @@
return false;
}
+static inline bool eir_has_data(u8 *data, size_t data_len, u8 type, u8 *mdata,
+ size_t mdata_len)
+{
+ size_t parsed = 0;
+
+ if (data_len < 2)
+ return false;
+
+ while (parsed < data_len - 1) {
+ u8 field_len = data[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > data_len)
+ break;
+
+ if (data[1] != type)
+ goto next;
+
+ if ((field_len - 1) != mdata_len)
+ goto next;
+
+ if (!memcmp(&data[2], mdata, mdata_len))
+ return true;
+
+ next:
+ data += field_len + 1;
+ }
+
+ return false;
+}
+
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
{
if (addr_type != ADDR_LE_DEV_RANDOM)
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index 7db4220..df84060 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -22,6 +22,8 @@
*/
#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -793,6 +795,87 @@
.llseek = default_llseek,
};
+static int bachk(const char *str)
+{
+ if (!str)
+ return -1;
+
+ if (strlen(str) != 17)
+ return -1;
+
+ while (*str) {
+ if (!isxdigit(*str++))
+ return -1;
+
+ if (!isxdigit(*str++))
+ return -1;
+
+ if (*str == 0)
+ break;
+
+ if (*str++ != ':')
+ return -1;
+ }
+
+ return 0;
+}
+
+static int str2ba(const char *str, bdaddr_t *ba)
+{
+ int i;
+
+ if (bachk(str) < 0) {
+ memset(ba, 0, sizeof(*ba));
+ return -1;
+ }
+
+ for (i = 5; i >= 0; i--, str += 3)
+ ba->b[i] = simple_strtol(str, NULL, 16);
+
+ return 0;
+}
+
+static ssize_t le_adv_ind_filter_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%pMR\n", &hdev->le_adv_ind_filter);
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 18);
+}
+
+static ssize_t le_adv_ind_filter_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[20];
+
+ if (count != 17 && count != 18)
+ return -EINVAL;
+
+ if (copy_from_user(buf, user_buf, 17))
+ return -EFAULT;
+
+ buf[17] = '\0';
+
+ if (str2ba(buf, &hdev->le_adv_ind_filter) < 0)
+ return -EINVAL;
+
+ BT_INFO("%s: le_adv_ind_filter=%pMR", hdev->name, &hdev->le_adv_ind_filter);
+
+ return count;
+}
+
+static const struct file_operations le_adv_ind_filter_fops = {
+ .open = simple_open,
+ .read = le_adv_ind_filter_read,
+ .write = le_adv_ind_filter_write,
+ .llseek = default_llseek,
+};
+
static int white_list_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
@@ -1127,6 +1210,9 @@
debugfs_create_file("quirk_simultaneous_discovery", 0644,
hdev->debugfs, hdev,
&quirk_simultaneous_discovery_fops);
+
+ debugfs_create_file("le_adv_ind_filter", 0644, hdev->debugfs, hdev,
+ &le_adv_ind_filter_fops);
}
void hci_debugfs_create_conn(struct hci_conn *conn)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 7b61be7..677f604 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -4712,6 +4712,19 @@
bool match;
u32 flags;
+ /*
+ * GFRM200: Drop connectable undirected advertisements to the floor
+ * except when required for pairing. When BlueZ initiates pairing,
+ * it expects to receive LE_ADV_IND from the peripheral device in
+ * order to establish the LE connection for pairing.
+ */
+ if (type == LE_ADV_IND &&
+ eir_has_data(data, len, EIR_NAME_SHORT, "GFRM200", 7) &&
+ bacmp(bdaddr, &hdev->le_adv_ind_filter)) {
+ BT_INFO("%s: Drop LE_ADV_IND from GFRM200 [%pMR]", hdev->name, bdaddr);
+ return;
+ }
+
/* If the direct address is present, then this report is from
* a LE Direct Advertising Report event. In that case it is
* important to see if the address is matching the local