unsigned short properties_off;
unsigned short properties_len;
/*
- * crc32 of some common device properties to filter with socket filters in the client
- * used in the kernel from socket filter rules; needs to be stored in network order
+ * hashes of some common device properties strings to filter with socket filters in
+ * the client used in the kernel from socket filter rules; needs to be stored in
+ * network order
*/
unsigned int filter_subsystem;
+ unsigned int filter_devtype;
};
static struct udev_monitor *udev_monitor_new(struct udev *udev)
(*i)++;
}
-static int filter_apply(struct udev_monitor *udev_monitor)
+int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
{
static struct sock_filter ins[256];
static struct sock_fprog filter;
memset(ins, 0x00, sizeof(ins));
i = 0;
- /* load magic in accu */
+ /* load magic in A */
bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic));
/* jump if magic matches */
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
- /* wrong magic, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ /* wrong magic, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
- /* load filter_subsystem value in accu */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem));
/* add all subsystem match values */
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
- const char *subsys = udev_list_entry_get_name(list_entry);
- unsigned int crc;
+ unsigned int hash;
+
+ /* load filter_subsystem value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem));
+ hash = util_string_hash32(udev_list_entry_get_name(list_entry));
+ if (udev_list_entry_get_value(list_entry) == NULL) {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ } else {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
+
+ /* load filter_devtype value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype));
+ /* jump if value does not match */
+ hash = util_string_hash32(udev_list_entry_get_value(list_entry));
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ }
- crc = util_crc32((unsigned char *)subsys, strlen(subsys));
- /* jump if value does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, crc, 0, 1);
/* matched, pass packet */
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
err = bind(udev_monitor->sock,
(struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
} else if (udev_monitor->snl.nl_family != 0) {
- filter_apply(udev_monitor);
+ udev_monitor_filter_update(udev_monitor);
err = bind(udev_monitor->sock,
(struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
} else {
static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
{
struct udev_list_entry *list_entry;
- int pass = 0;
if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
return 1;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
- const char *subsys = udev_device_get_subsystem(udev_device);
+ const char *subsys = udev_list_entry_get_name(list_entry);
+ const char *dsubsys = udev_device_get_subsystem(udev_device);
+ const char *devtype;
+ const char *ddevtype;
- if (strcmp(udev_list_entry_get_name(list_entry), subsys) == 0) {
- pass= 1;
- break;
- }
+ if (strcmp(dsubsys, subsys) != 0)
+ continue;
+
+ devtype = udev_list_entry_get_value(list_entry);
+ if (devtype == NULL)
+ return 1;
+ ddevtype = udev_device_get_devtype(udev_device);
+ if (ddevtype == NULL)
+ continue;
+ if (strcmp(ddevtype, devtype) == 0)
+ return 1;
}
- return pass;
+ return 0;
}
/**
struct cmsghdr *cmsg;
struct sockaddr_nl snl;
struct ucred *cred;
- char buf[4096];
- size_t bufpos;
+ char buf[8192];
+ ssize_t buflen;
+ ssize_t bufpos;
struct udev_monitor_netlink_header *nlh;
int devpath_set = 0;
int subsystem_set = 0;
smsg.msg_namelen = sizeof(snl);
}
- if (recvmsg(udev_monitor->sock, &smsg, 0) < 0) {
+ buflen = recvmsg(udev_monitor->sock, &smsg, 0);
+ if (buflen < 0) {
if (errno != EINTR)
info(udev_monitor->udev, "unable to receive message\n");
return NULL;
}
+ if (buflen < 32 || (size_t)buflen >= sizeof(buf)) {
+ info(udev_monitor->udev, "invalid message length\n");
+ return NULL;
+ }
+
if (udev_monitor->snl.nl_family != 0) {
if (snl.nl_groups == 0) {
info(udev_monitor->udev, "unicast netlink message ignored\n");
return NULL;
}
- nlh = (struct udev_monitor_netlink_header *) buf;
- if (nlh->magic == ntohl(UDEV_MONITOR_MAGIC)) {
- /* udev message with version magic */
+ if (strncmp(buf, "udev-", 5) == 0) {
+ /* udev message needs proper version magic */
+ nlh = (struct udev_monitor_netlink_header *) buf;
+ if (nlh->magic != htonl(UDEV_MONITOR_MAGIC))
+ return NULL;
if (nlh->properties_off < sizeof(struct udev_monitor_netlink_header))
return NULL;
- if (nlh->properties_off+32U > sizeof(buf))
+ if (nlh->properties_off+32U > buflen)
return NULL;
bufpos = nlh->properties_off;
} else {
/* kernel message with header */
bufpos = strlen(buf) + 1;
- if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
+ if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) {
info(udev_monitor->udev, "invalid message length\n");
return NULL;
}
return NULL;
}
- while (bufpos < sizeof(buf)) {
+ while (bufpos < buflen) {
char *key;
size_t keylen;
smsg.msg_namelen = udev_monitor->addrlen;
} else if (udev_monitor->snl.nl_family != 0) {
const char *val;
- size_t vlen;
struct udev_monitor_netlink_header nlh;
util_strlcpy(nlh.version, "udev-" VERSION, sizeof(nlh.version));
nlh.magic = htonl(UDEV_MONITOR_MAGIC);
val = udev_device_get_subsystem(udev_device);
- vlen = strlen(val);
- nlh.filter_subsystem = htonl(util_crc32((unsigned char *)val, vlen));
+ nlh.filter_subsystem = htonl(util_string_hash32(val));
+ val = udev_device_get_devtype(udev_device);
+ if (val != NULL)
+ nlh.filter_devtype = htonl(util_string_hash32(val));
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(struct udev_monitor_netlink_header);
return count;
}
-int udev_monitor_filter_add_match_subsystem(struct udev_monitor *udev_monitor, const char *subsystem)
+int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
{
if (udev_monitor == NULL)
return -EINVAL;
if (subsystem == NULL)
return 0;
if (udev_list_entry_add(udev_monitor->udev,
- &udev_monitor->filter_subsystem_list, subsystem, NULL, 1, 0) == NULL)
+ &udev_monitor->filter_subsystem_list, subsystem, devtype, 0, 0) == NULL)
return -ENOMEM;
return 0;
}
+
+int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
+{
+ static struct sock_fprog filter = { 0, NULL };
+
+ udev_list_cleanup_entries(udev_monitor->udev, &udev_monitor->filter_subsystem_list);
+ return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
+}