Re: Filter/Parse NETLINK_KOBJECT_UEVENT Messages

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I understand this is slightly off-topic, but I'm completely new to BPF. Analyzing libudev source and Internet I understand the general idea. However, I don't understand how information/what information is passed to the filter from the socket. For example, in my case the socket payload, i.e. buf_str = add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0/input/input38/event14
1. How do I pass this string to the sock_filter/sock_fprog structures?
2. Is a correct way of filtering these to implement string parsing to check for '/event' sub-string in EPF bytecode?
I completely agree that using libudev is the way to go (and *a lot* more sane), however this is for my own understanding. Just by investigating this I have found out more about how udev works and now BPF.
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Monday, September 13th, 2021 at 7:57 PM, Mantas Mikulėnas <grawity@xxxxxxxxx> wrote:
On Mon, Sep 13, 2021 at 12:29 PM Ryan McClue <re.mcclue@xxxxxxxxxxxxxx> wrote:
Currently, I'm listening to NETLINK_KOBJECT_UEVENT messages with the following code:

union UeventBuffer {
  struct nlmsghdr netlink_header;
  char raw[8192];
};
  int sock = socket(PF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);

  struct sockaddr_nl addr = {};
  addr.nl_family = AF_NETLINK;
  addr.nl_groups = 1 << 0;
  bind(sock, (struct sockaddr *)&addr, sizeof(addr));

  UeventBuffer buf = {};
  struct iovec iov = {};
  iov.iov_base = &buf;
  iov.iov_len = sizeof(buf);

  struct msghdr msg = {};
  struct sockaddr_nl src_addr = {};
  msg.msg_name = &src_addr;
  msg.msg_namelen = sizeof(src_addr);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

  int bytes = recvmsg(sock, &msg, 0);
  char *buf_str = buf.raw;
  // parse this buf_str ...

I have a few questions to clarify my understanding and to make this more robust:
1. If I add an Xbox One controller, which evtest shows to be /dev/input/event14 I get a whole host of messages, e.g:
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0/input/input38
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0/input/input38/event14
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0/input/input38/js0
bind@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.0
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.1
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4/1-2.4:1.2
bind@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4
add@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2
change@/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.2
Why so many?

It represents the actual device and subsystem layering in Linux – first there's a USB device (on hub 1-2 port 4), then USB interfaces on that device (1.0 to 1.2), then an input-layer device (input38), then two different /dev nodes exposed by it (generic evdev and what I *think* is legacy joydev? Not sure if it's "legacy" or "still current".)

They are necessary; for example, the 1st event that informs systemd-udevd about the low-level USB device is what causes the correct device driver module to be loaded in the first place.
 
Can I filter them to just get the ones ending with /input/inputXX/eventXX?

It seems you're supposed to use BPF filters via SO_ATTACH_FILTER; at least that's what libudev does.
 
2. Currently this only picks up input devices. How would I listen to /snd devices, /hid devices, etc.? I assume some change to nl_groups, however what should this be and where is this documented?

There's just one group for all events.

(If I remember correctly, group 1 has all events straight from the kernel, group 2 is the same events augmented and retransmitted by udev. Normally programs want the latter. Both have the same events and include all devices, though.)
 
3. Currently, I'm manually parsing the buf_str to extract the command and device. Are there some supplied macros that parse this information in a more robust way? (as is the case for RTNETLINK)

Well, systemd supplies a whole libudev library, so I would generally recommend using that – *instead of* manually working with netlink... (Or more recently sd-device, but I don't think libudev is likely to be going *poof* anytime soon, especially if you're looking for compatibility with older systems or eudev-using distros.)

Start with udev_monitor_new_from_netlink() and use udev_monitor_filter_add_match_*() to filter by subsystem, devtype, or some arbitrary property, then you'll receive events already parsed. That's 3 lines of code in comparison to your 30.

--
Mantas Mikulėnas


[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux