From be3f6a2ca0e924fa37d203c1cb172216c4d1cb15 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 27 Jan 2024 00:13:39 +0100 Subject: [PATCH] udev: Add module for checking interface renaming actions --- Makefile.am | 1 + src/udev.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 src/udev.c diff --git a/Makefile.am b/Makefile.am index 5ed6ab37..0a875dce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -265,6 +265,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \ src/dpp-util.h src/dpp-util.c \ src/json.h src/json.c \ src/dpp.c \ + src/udev.c \ $(eap_sources) \ $(builtin_sources) diff --git a/src/udev.c b/src/udev.c new file mode 100644 index 00000000..8baf4724 --- /dev/null +++ b/src/udev.c @@ -0,0 +1,285 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2024 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "src/module.h" + +static void receive_props(const void *buf, uint32_t len) +{ + const char *action = NULL; + const char *interface = NULL; + const char *ifindex = NULL; + + while (len > 0) { + const char *s = buf; + size_t l = strlen(s); + const char *t; + + if (l < 2) + break; + + t = strchr(s, '='); + if (t) { + size_t p = t - s; + + if (!strncmp(s, "ACTION", p)) + action = t + 1; + else if (!strncmp(s, "INTERFACE", p)) + interface = t + 1; + else if (!strncmp(s, "IFINDEX", p)) + ifindex = t + 1; + } + + buf += l + 1; + len -= l + 1; + } + + if (action && !strcmp(action, "add")) + l_info("udev interface=%s ifindex=%s", interface, ifindex); +} + +static struct l_io *udev_io; + +static int create_socket(uint32_t *pid) +{ + struct sockaddr_nl addr; + socklen_t addrlen = sizeof(addr); + int fd, pktinfo = 1; + + fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + NETLINK_KOBJECT_UEVENT); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + return -1; + } + + if (getsockname(fd, (struct sockaddr *) &addr, &addrlen) < 0) { + close(fd); + return -1; + } + + if (setsockopt(fd, SOL_NETLINK, NETLINK_PKTINFO, + &pktinfo, sizeof(pktinfo)) < 0) { + close(fd); + return -1; + } + + if (pid) + *pid = addr.nl_pid; + + return fd; +} + +static struct sock_filter subsys_filter[] = { + { 0x20, 0, 0, 0x00000008 }, /* ldw #magic */ + { 0x15, 0, 3, 0xfeedcafe }, /* jne #feedcafe, drop */ + { 0x20, 0, 0, 0x00000018 }, /* ldw #subsys_hash */ + { 0x15, 0, 1, 0xa74d3cc8 }, /* jne #net, drop */ + { 0x06, 0, 0, 0xffffffff }, /* keep: ret #-1 */ + { 0x06, 0, 0, 0000000000 }, /* drop: ret #0 */ +}; + +static const struct sock_fprog subsys_fprog = { .len = 6, + .filter = subsys_filter }; + +static bool attach_filter(struct l_io *io) +{ + int fd; + + fd = l_io_get_fd(io); + if (fd < 0) + return false; + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, + &subsys_fprog, sizeof(subsys_fprog)) < 0) + return false; + + return true; +} + +static bool add_membership(struct l_io *io, uint32_t group) +{ + int fd, value = group; + + fd = l_io_get_fd(io); + if (fd < 0) + return false; + + if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &value, sizeof(value)) < 0) + return false; + + return true; +} + +static bool drop_membership(struct l_io *io, uint32_t group) +{ + int fd, value = group; + + fd = l_io_get_fd(io); + if (fd < 0) + return false; + + if (setsockopt(fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &value, sizeof(value)) < 0) + return false; + + return true; +} + +static const uint8_t LIBUDEV_PREFIX[8] = { "libudev" }; +static uint32_t LIBUDEV_MAGIC = 0xfeedcafe; + +struct udev_hdr { + char prefix[8]; + uint32_t magic; + uint32_t hdr_size; + uint32_t props_off; + uint32_t props_len; + uint32_t flt_subsys_hash; + uint32_t flt_devtype_hash; + uint32_t flt_tag_bloom_hi; + uint32_t flt_tag_bloom_lo; +}; + +static void receive_msg(const void *buf, uint32_t len) +{ + const struct udev_hdr *hdr = buf; + + if (len < sizeof(struct udev_hdr)) + return; + + if (memcmp(hdr->prefix, LIBUDEV_PREFIX, 8)) + return; + + if (L_BE32_TO_CPU(hdr->magic) != LIBUDEV_MAGIC) + return; + + if (hdr->hdr_size > len) + return; + + if (hdr->props_off + hdr->props_len != len) + return; + + receive_props(buf + hdr->props_off, hdr->props_len); +} + +enum { + GROUP_NONE, + GROUP_KERNEL, + GROUP_UDEV, +}; + +static bool can_read_data(struct l_io *io, void *user_data) +{ + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov; + unsigned char buffer[4096]; + unsigned char control[32]; + uint32_t group = GROUP_NONE; + ssize_t len; + int fd; + + memset(buffer, 0, sizeof(buffer)); + memset(control, 0, sizeof(control)); + + fd = l_io_get_fd(io); + + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + len = recvmsg(fd, &msg, 0); + if (len < 0) + return false; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + struct nl_pktinfo *pktinfo; + + if (cmsg->cmsg_level != SOL_NETLINK) + continue; + + if (cmsg->cmsg_type != NETLINK_PKTINFO) + continue; + + pktinfo = (void *) CMSG_DATA(cmsg); + group = pktinfo->group; + } + + if (group == GROUP_UDEV) + receive_msg(buffer, len); + + return true; +} + +static int udev_init(void) +{ + int fd; + + l_debug(""); + + fd = create_socket(NULL); + if (fd < 0) + return -EIO; + + udev_io = l_io_new(fd); + l_io_set_close_on_destroy(udev_io, true); + l_io_set_read_handler(udev_io, can_read_data, NULL, NULL); + attach_filter(udev_io); + add_membership(udev_io, GROUP_UDEV); + + return 0; +} + +static void udev_exit(void) +{ + l_debug(""); + + drop_membership(udev_io, GROUP_UDEV); + l_io_destroy(udev_io); +} + +IWD_MODULE(udev, udev_init, udev_exit); +IWD_MODULE_DEPENDS(udev, netdev);