* [PATCH 1/5] monitor: Remove unused code path
@ 2023-12-27 6:09 Denis Kenzior
2023-12-27 6:09 ` [PATCH 2/5] monitor: Remove unused PACKET_AUXDATA handling Denis Kenzior
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Denis Kenzior @ 2023-12-27 6:09 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
nlmon_print_genl invokes genl_ctrl when a generic netlink control
message is encountered. genl_ctrl() tries to filter nl80211 family
appearance messages and setup nlmon->id with the extracted family id.
However, the id is already provided inside main.c by using nlmon_open,
and no control messages are processed by nlmon in 'capture' mode (-r
command line argument not passed) since all genl messages go through
nlmon_message() path instead.
---
monitor/nlmon.c | 36 +++---------------------------------
1 file changed, 3 insertions(+), 33 deletions(-)
diff --git a/monitor/nlmon.c b/monitor/nlmon.c
index ed40264bacd6..7f8ecdaa1996 100644
--- a/monitor/nlmon.c
+++ b/monitor/nlmon.c
@@ -7359,35 +7359,6 @@ void nlmon_destroy(struct nlmon *nlmon)
l_free(nlmon);
}
-static void genl_ctrl(struct nlmon *nlmon, const void *data, uint32_t len)
-{
- const struct genlmsghdr *genlmsg = data;
- const struct nlattr *nla;
- char name[GENL_NAMSIZ];
- uint16_t id = 0;
-
- if (genlmsg->cmd != CTRL_CMD_NEWFAMILY)
- return;
-
- for (nla = data + GENL_HDRLEN; NLA_OK(nla, len);
- nla = NLA_NEXT(nla, len)) {
- switch (nla->nla_type & NLA_TYPE_MASK) {
- case CTRL_ATTR_FAMILY_ID:
- id = *((uint16_t *) NLA_DATA(nla));
- break;
- case CTRL_ATTR_FAMILY_NAME:
- strncpy(name, NLA_DATA(nla), GENL_NAMSIZ - 1);
- break;
- }
- }
-
- if (id == 0)
- return;
-
- if (!strcmp(name, NL80211_GENL_NAME))
- nlmon->id = id;
-}
-
static const char *scope_to_string(uint8_t scope)
{
switch (scope) {
@@ -8208,10 +8179,9 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
for (nlmsg = data; NLMSG_OK(nlmsg, size);
nlmsg = NLMSG_NEXT(nlmsg, size)) {
if (nlmsg->nlmsg_type == GENL_ID_CTRL)
- genl_ctrl(nlmon, NLMSG_DATA(nlmsg),
- NLMSG_PAYLOAD(nlmsg, 0));
- else
- nlmon_message(nlmon, tv, NULL, nlmsg);
+ continue;
+
+ nlmon_message(nlmon, tv, NULL, nlmsg);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/5] monitor: Remove unused PACKET_AUXDATA handling
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
@ 2023-12-27 6:09 ` Denis Kenzior
2023-12-27 6:09 ` [PATCH 3/5] monitor: Use nlmon_print_* inside nlmon_receive Denis Kenzior
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2023-12-27 6:09 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
---
monitor/nlmon.c | 13 ++-----------
1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/monitor/nlmon.c b/monitor/nlmon.c
index 7f8ecdaa1996..b4d1f54c7579 100644
--- a/monitor/nlmon.c
+++ b/monitor/nlmon.c
@@ -7240,7 +7240,6 @@ static void store_message(struct nlmon *nlmon, const struct timeval *tv,
}
static void nlmon_message(struct nlmon *nlmon, const struct timeval *tv,
- const struct tpacket_auxdata *tp,
const struct nlmsghdr *nlmsg)
{
struct nlmon_req *req;
@@ -8181,7 +8180,7 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
if (nlmsg->nlmsg_type == GENL_ID_CTRL)
continue;
- nlmon_message(nlmon, tv, NULL, nlmsg);
+ nlmon_message(nlmon, tv, nlmsg);
}
}
@@ -8194,9 +8193,7 @@ static bool nlmon_receive(struct l_io *io, void *user_data)
struct iovec iov;
struct cmsghdr *cmsg;
struct timeval copy_tv;
- struct tpacket_auxdata copy_tp;
const struct timeval *tv = NULL;
- const struct tpacket_auxdata *tp = NULL;
uint16_t proto_type;
unsigned char buf[8192];
unsigned char control[32];
@@ -8242,12 +8239,6 @@ static bool nlmon_receive(struct l_io *io, void *user_data)
memcpy(©_tv, CMSG_DATA(cmsg), sizeof(copy_tv));
tv = ©_tv;
}
-
- if (cmsg->cmsg_level == SOL_PACKET &&
- cmsg->cmsg_type != PACKET_AUXDATA) {
- memcpy(©_tp, CMSG_DATA(cmsg), sizeof(copy_tp));
- tp = ©_tp;
- }
}
nlmsg_len = bytes_read;
@@ -8261,7 +8252,7 @@ static bool nlmon_receive(struct l_io *io, void *user_data)
nlmon_print_rtnl(nlmon, tv, nlmsg, nlmsg->nlmsg_len);
break;
case NETLINK_GENERIC:
- nlmon_message(nlmon, tv, tp, nlmsg);
+ nlmon_message(nlmon, tv, nlmsg);
break;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/5] monitor: Use nlmon_print_* inside nlmon_receive
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
2023-12-27 6:09 ` [PATCH 2/5] monitor: Remove unused PACKET_AUXDATA handling Denis Kenzior
@ 2023-12-27 6:09 ` Denis Kenzior
2023-12-27 6:09 ` [PATCH 4/5] monitor: Move iwmon reading logic into main.c Denis Kenzior
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2023-12-27 6:09 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
The current implementation inside nlmon_receive is asymmetrical. RTNL
packets are printed using nlmon_print_rtnl while GENL packets are
printed using nlmon_message.
nlmon_print_genl and nlmon_print_rtnl already handle iterating over data
containing multiple messages, and are used by nlmon started in reader
mode. Use these for better symmetry inside nlmon_receive.
While here, move store_netlink() call into nlmon_print_rtnl. This makes
handling of PCAP output symmetrical for both RTNL and GENL packets.
This also fixes a possibility where only the first message of a
multi-RTNL packet would be stored.
---
monitor/nlmon.c | 44 ++++++++++++++++++--------------------------
1 file changed, 18 insertions(+), 26 deletions(-)
diff --git a/monitor/nlmon.c b/monitor/nlmon.c
index b4d1f54c7579..72cd8ca7a05c 100644
--- a/monitor/nlmon.c
+++ b/monitor/nlmon.c
@@ -7280,12 +7280,6 @@ static void nlmon_message(struct nlmon *nlmon, const struct timeval *tv,
return;
}
- if (!nlmon->read && nlmsg->nlmsg_type != nlmon->id) {
- if (nlmsg->nlmsg_type == GENL_ID_CTRL)
- store_message(nlmon, tv, nlmsg);
- return;
- }
-
if (nlmsg->nlmsg_flags & NLM_F_REQUEST) {
const struct genlmsghdr *genlmsg = NLMSG_DATA(nlmsg);
uint32_t flags = nlmsg->nlmsg_flags & ~NLM_F_REQUEST;
@@ -8137,13 +8131,15 @@ void nlmon_print_rtnl(struct nlmon *nlmon, const struct timeval *tv,
int64_t aligned_size = NLMSG_ALIGN(size);
const struct nlmsghdr *nlmsg;
- if (nlmon->nortnl)
- return;
-
update_time_offset(tv);
for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
+ store_netlink(nlmon, tv, NETLINK_ROUTE, nlmsg);
+
+ if (nlmon->nortnl)
+ continue;
+
switch (nlmsg->nlmsg_type) {
case NLMSG_NOOP:
case NLMSG_OVERRUN:
@@ -8177,7 +8173,12 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
for (nlmsg = data; NLMSG_OK(nlmsg, size);
nlmsg = NLMSG_NEXT(nlmsg, size)) {
- if (nlmsg->nlmsg_type == GENL_ID_CTRL)
+ if (nlmsg->nlmsg_type == GENL_ID_CTRL) {
+ store_message(nlmon, tv, nlmsg);
+ continue;
+ }
+
+ if (!nlmon->read && nlmsg->nlmsg_type != nlmon->id)
continue;
nlmon_message(nlmon, tv, nlmsg);
@@ -8187,7 +8188,6 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
static bool nlmon_receive(struct l_io *io, void *user_data)
{
struct nlmon *nlmon = user_data;
- struct nlmsghdr *nlmsg;
struct msghdr msg;
struct sockaddr_ll sll;
struct iovec iov;
@@ -8198,7 +8198,6 @@ static bool nlmon_receive(struct l_io *io, void *user_data)
unsigned char buf[8192];
unsigned char control[32];
ssize_t bytes_read;
- int64_t nlmsg_len;
int fd;
fd = l_io_get_fd(io);
@@ -8241,20 +8240,13 @@ static bool nlmon_receive(struct l_io *io, void *user_data)
}
}
- nlmsg_len = bytes_read;
-
- for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, nlmsg_len);
- nlmsg = NLMSG_NEXT(nlmsg, nlmsg_len)) {
- switch (proto_type) {
- case NETLINK_ROUTE:
- store_netlink(nlmon, tv, proto_type, nlmsg);
-
- nlmon_print_rtnl(nlmon, tv, nlmsg, nlmsg->nlmsg_len);
- break;
- case NETLINK_GENERIC:
- nlmon_message(nlmon, tv, nlmsg);
- break;
- }
+ switch (proto_type) {
+ case NETLINK_ROUTE:
+ nlmon_print_rtnl(nlmon, tv, iov.iov_base, bytes_read);
+ break;
+ case NETLINK_GENERIC:
+ nlmon_print_genl(nlmon, tv, iov.iov_base, bytes_read);
+ break;
}
return true;
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/5] monitor: Move iwmon reading logic into main.c
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
2023-12-27 6:09 ` [PATCH 2/5] monitor: Remove unused PACKET_AUXDATA handling Denis Kenzior
2023-12-27 6:09 ` [PATCH 3/5] monitor: Use nlmon_print_* inside nlmon_receive Denis Kenzior
@ 2023-12-27 6:09 ` Denis Kenzior
2023-12-27 6:09 ` [PATCH 5/5] RFC: Initial iwtrace utility Denis Kenzior
2024-01-02 17:01 ` [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
4 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2023-12-27 6:09 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
To support multiple nlmon sources, move the logic that reads from iwmon
device into main.c instead of nlmon. nlmon.c now becomes agnostic of
how the packets are actually obtained. Packets are fed in via
high-level APIs such as nlmon_print_rtnl, nlmon_print_genl,
nlmon_print_pae.
---
monitor/main.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++--
monitor/nlmon.c | 165 +--------------------------------------------
monitor/nlmon.h | 2 +-
3 files changed, 174 insertions(+), 168 deletions(-)
diff --git a/monitor/main.c b/monitor/main.c
index 0c5f0670c706..e9384e1b884f 100644
--- a/monitor/main.c
+++ b/monitor/main.c
@@ -32,10 +32,13 @@
#include <string.h>
#include <getopt.h>
#include <signal.h>
+#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/genetlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
+#include <linux/filter.h>
+#include <sys/ioctl.h>
#include <ell/ell.h>
#ifndef ARPHRD_NETLINK
@@ -68,11 +71,160 @@ static struct nlmon_config config;
#define NLMON_TYPE "nlmon"
#define NLMON_LEN 5
+static bool nlmon_receive(struct l_io *io, void *user_data)
+{
+ struct nlmon *nlmon = user_data;
+ struct msghdr msg;
+ struct sockaddr_ll sll;
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ struct timeval copy_tv;
+ const struct timeval *tv = NULL;
+ uint16_t proto_type;
+ unsigned char buf[8192];
+ unsigned char control[32];
+ ssize_t bytes_read;
+ int fd;
+
+ fd = l_io_get_fd(io);
+ if (fd < 0)
+ return false;
+
+ memset(&sll, 0, sizeof(sll));
+
+ memset(&iov, 0, sizeof(iov));
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &sll;
+ msg.msg_namelen = sizeof(sll);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ bytes_read = recvmsg(fd, &msg, 0);
+ if (bytes_read < 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ return false;
+
+ return true;
+ }
+
+ if (sll.sll_hatype != ARPHRD_NETLINK)
+ return true;
+
+ proto_type = ntohs(sll.sll_protocol);
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_TIMESTAMP) {
+ memcpy(©_tv, CMSG_DATA(cmsg), sizeof(copy_tv));
+ tv = ©_tv;
+ }
+ }
+
+ switch (proto_type) {
+ case NETLINK_ROUTE:
+ nlmon_print_rtnl(nlmon, tv, iov.iov_base, bytes_read);
+ break;
+ case NETLINK_GENERIC:
+ nlmon_print_genl(nlmon, tv, iov.iov_base, bytes_read);
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * BPF filter to match skb->dev->type == 824 (ARPHRD_NETLINK) and
+ * either match skb->protocol == 0x0000 (NETLINK_ROUTE) or match
+ * skb->protocol == 0x0010 (NETLINK_GENERIC).
+ */
+static struct sock_filter mon_filter[] = {
+ { 0x28, 0, 0, 0xfffff01c }, /* ldh #hatype */
+ { 0x15, 0, 3, 0x00000338 }, /* jne #824, drop */
+ { 0x28, 0, 0, 0xfffff000 }, /* ldh #proto */
+ { 0x15, 2, 0, 0000000000 }, /* jeq #0x0000, pass */
+ { 0x15, 1, 0, 0x00000010 }, /* jeq #0x0010, pass */
+ { 0x06, 0, 0, 0000000000 }, /* drop: ret #0 */
+ { 0x06, 0, 0, 0xffffffff }, /* pass: ret #-1 */
+};
+
+static const struct sock_fprog mon_fprog = { .len = 7, .filter = mon_filter };
+
+static struct l_io *open_packet(const char *name)
+{
+ struct l_io *io;
+ struct sockaddr_ll sll;
+ struct packet_mreq mr;
+ struct ifreq ifr;
+ int fd, opt = 1;
+
+ fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0) {
+ perror("Failed to create packet socket");
+ return NULL;
+ }
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
+
+ if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
+ perror("Failed to get monitor index");
+ close(fd);
+ return NULL;
+ }
+
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_protocol = htons(ETH_P_ALL);
+ sll.sll_ifindex = ifr.ifr_ifindex;
+
+ if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
+ perror("Failed to bind packet socket");
+ close(fd);
+ return NULL;
+ }
+
+ memset(&mr, 0, sizeof(mr));
+ mr.mr_ifindex = ifr.ifr_ifindex;
+ mr.mr_type = PACKET_MR_ALLMULTI;
+
+ if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
+ &mr, sizeof(mr)) < 0) {
+ perror("Failed to enable all multicast");
+ close(fd);
+ return NULL;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
+ &mon_fprog, sizeof(mon_fprog)) < 0) {
+ perror("Failed to enable monitor filter");
+ close(fd);
+ return NULL;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable monitor timestamps");
+ close(fd);
+ return NULL;
+ }
+
+ io = l_io_new(fd);
+
+ l_io_set_close_on_destroy(io, true);
+
+ return io;
+}
+
struct iwmon_interface {
char *ifname;
bool exists;
struct l_netlink *rtnl;
struct l_netlink *genl;
+ struct l_io *io;
};
static struct iwmon_interface monitor_interface = { };
@@ -109,11 +261,23 @@ static void genl_parse(uint16_t type, const void *data, uint32_t len,
if (id == 0)
return;
- if (!strcmp(name, NL80211_GENL_NAME)) {
- nlmon = nlmon_open(ifname, id, writer_path, &config);
- if (!nlmon)
- l_main_quit();
- }
+ if (strcmp(name, NL80211_GENL_NAME))
+ return;
+
+ monitor_interface.io = open_packet(ifname);
+ if (!monitor_interface.io)
+ goto failed;
+
+ nlmon = nlmon_open(id, writer_path, &config);
+ if (!nlmon)
+ goto failed;
+
+ l_io_set_read_handler(monitor_interface.io, nlmon_receive, nlmon, NULL);
+
+ return;
+
+failed:
+ l_main_quit();
}
static void genl_notify(uint16_t type, const void *data,
@@ -790,6 +954,7 @@ int main(int argc, char *argv[])
exit_status = l_main_run_with_signal(signal_handler, NULL);
+ l_io_destroy(monitor_interface.io);
l_netlink_destroy(monitor_interface.rtnl);
l_netlink_destroy(monitor_interface.genl);
l_free(monitor_interface.ifname);
diff --git a/monitor/nlmon.c b/monitor/nlmon.c
index 72cd8ca7a05c..5e88cdf1355e 100644
--- a/monitor/nlmon.c
+++ b/monitor/nlmon.c
@@ -96,7 +96,6 @@ enum msg_type {
struct nlmon {
uint16_t id;
- struct l_io *io;
struct l_io *pae_io;
struct l_queue *req_list;
struct pcap *pcap;
@@ -8185,154 +8184,6 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
}
}
-static bool nlmon_receive(struct l_io *io, void *user_data)
-{
- struct nlmon *nlmon = user_data;
- struct msghdr msg;
- struct sockaddr_ll sll;
- struct iovec iov;
- struct cmsghdr *cmsg;
- struct timeval copy_tv;
- const struct timeval *tv = NULL;
- uint16_t proto_type;
- unsigned char buf[8192];
- unsigned char control[32];
- ssize_t bytes_read;
- int fd;
-
- fd = l_io_get_fd(io);
- if (fd < 0)
- return false;
-
- memset(&sll, 0, sizeof(sll));
-
- memset(&iov, 0, sizeof(iov));
- iov.iov_base = buf;
- iov.iov_len = sizeof(buf);
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_name = &sll;
- msg.msg_namelen = sizeof(sll);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- bytes_read = recvmsg(fd, &msg, 0);
- if (bytes_read < 0) {
- if (errno != EAGAIN && errno != EINTR)
- return false;
-
- return true;
- }
-
- if (sll.sll_hatype != ARPHRD_NETLINK)
- return true;
-
- proto_type = ntohs(sll.sll_protocol);
-
- for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
- cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_TIMESTAMP) {
- memcpy(©_tv, CMSG_DATA(cmsg), sizeof(copy_tv));
- tv = ©_tv;
- }
- }
-
- switch (proto_type) {
- case NETLINK_ROUTE:
- nlmon_print_rtnl(nlmon, tv, iov.iov_base, bytes_read);
- break;
- case NETLINK_GENERIC:
- nlmon_print_genl(nlmon, tv, iov.iov_base, bytes_read);
- break;
- }
-
- return true;
-}
-
-/*
- * BPF filter to match skb->dev->type == 824 (ARPHRD_NETLINK) and
- * either match skb->protocol == 0x0000 (NETLINK_ROUTE) or match
- * skb->protocol == 0x0010 (NETLINK_GENERIC).
- */
-static struct sock_filter mon_filter[] = {
- { 0x28, 0, 0, 0xfffff01c }, /* ldh #hatype */
- { 0x15, 0, 3, 0x00000338 }, /* jne #824, drop */
- { 0x28, 0, 0, 0xfffff000 }, /* ldh #proto */
- { 0x15, 2, 0, 0000000000 }, /* jeq #0x0000, pass */
- { 0x15, 1, 0, 0x00000010 }, /* jeq #0x0010, pass */
- { 0x06, 0, 0, 0000000000 }, /* drop: ret #0 */
- { 0x06, 0, 0, 0xffffffff }, /* pass: ret #-1 */
-};
-
-static const struct sock_fprog mon_fprog = { .len = 7, .filter = mon_filter };
-
-static struct l_io *open_packet(const char *name)
-{
- struct l_io *io;
- struct sockaddr_ll sll;
- struct packet_mreq mr;
- struct ifreq ifr;
- int fd, opt = 1;
-
- fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (fd < 0) {
- perror("Failed to create packet socket");
- return NULL;
- }
-
- strncpy(ifr.ifr_name, name, IFNAMSIZ - 1);
-
- if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
- perror("Failed to get monitor index");
- close(fd);
- return NULL;
- }
-
- memset(&sll, 0, sizeof(sll));
- sll.sll_family = AF_PACKET;
- sll.sll_protocol = htons(ETH_P_ALL);
- sll.sll_ifindex = ifr.ifr_ifindex;
-
- if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) {
- perror("Failed to bind packet socket");
- close(fd);
- return NULL;
- }
-
- memset(&mr, 0, sizeof(mr));
- mr.mr_ifindex = ifr.ifr_ifindex;
- mr.mr_type = PACKET_MR_ALLMULTI;
-
- if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
- &mr, sizeof(mr)) < 0) {
- perror("Failed to enable all multicast");
- close(fd);
- return NULL;
- }
-
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
- &mon_fprog, sizeof(mon_fprog)) < 0) {
- perror("Failed to enable monitor filter");
- close(fd);
- return NULL;
- }
-
- if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
- perror("Failed to enable monitor timestamps");
- close(fd);
- return NULL;
- }
-
- io = l_io_new(fd);
-
- l_io_set_close_on_destroy(io, true);
-
- return io;
-}
-
void nlmon_print_pae(struct nlmon *nlmon, const struct timeval *tv,
uint8_t type, int index,
const void *data, uint32_t size)
@@ -8460,28 +8311,21 @@ static struct l_io *open_pae(void)
return io;
}
-struct nlmon *nlmon_open(const char *ifname, uint16_t id, const char *pathname,
+struct nlmon *nlmon_open(uint16_t id, const char *pathname,
const struct nlmon_config *config)
{
struct nlmon *nlmon;
- struct l_io *io, *pae_io;
+ struct l_io *pae_io;
struct pcap *pcap;
- io = open_packet(ifname);
- if (!io)
- return NULL;
-
pae_io = open_pae();
- if (!pae_io) {
- l_io_destroy(io);
+ if (!pae_io)
return NULL;
- }
if (pathname) {
pcap = pcap_create(pathname);
if (!pcap) {
l_io_destroy(pae_io);
- l_io_destroy(io);
return NULL;
}
} else
@@ -8490,11 +8334,9 @@ struct nlmon *nlmon_open(const char *ifname, uint16_t id, const char *pathname,
nlmon = nlmon_create(id, config);
- nlmon->io = io;
nlmon->pae_io = pae_io;
nlmon->pcap = pcap;
- l_io_set_read_handler(nlmon->io, nlmon_receive, nlmon, NULL);
l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
wlan_iface_list = l_hashmap_new();
@@ -8507,7 +8349,6 @@ void nlmon_close(struct nlmon *nlmon)
if (!nlmon)
return;
- l_io_destroy(nlmon->io);
l_io_destroy(nlmon->pae_io);
l_queue_destroy(nlmon->req_list, nlmon_req_free);
diff --git a/monitor/nlmon.h b/monitor/nlmon.h
index 96958c25b2ba..bb1a7c58bd45 100644
--- a/monitor/nlmon.h
+++ b/monitor/nlmon.h
@@ -33,7 +33,7 @@ struct nlmon_config {
bool read_only;
};
-struct nlmon *nlmon_open(const char *ifname, uint16_t id, const char *pathname,
+struct nlmon *nlmon_open(uint16_t id, const char *pathname,
const struct nlmon_config *config);
void nlmon_close(struct nlmon *nlmon);
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/5] RFC: Initial iwtrace utility
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
` (2 preceding siblings ...)
2023-12-27 6:09 ` [PATCH 4/5] monitor: Move iwmon reading logic into main.c Denis Kenzior
@ 2023-12-27 6:09 ` Denis Kenzior
2024-01-02 13:30 ` James Prestwood
2024-01-02 17:01 ` [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
4 siblings, 1 reply; 7+ messages in thread
From: Denis Kenzior @ 2023-12-27 6:09 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
eBPF based tracing, similar to iwmon. This is meant as an alternative
to the nlmon based netlink tracing and can be used on kernels where
nlmon might not be available.
---
Makefile.am | 46 ++++++---
configure.ac | 11 +++
monitor/iwtrace.bpf.c | 194 +++++++++++++++++++++++++++++++++++++
monitor/iwtrace.c | 218 ++++++++++++++++++++++++++++++++++++++++++
monitor/iwtrace.h | 12 +++
5 files changed, 466 insertions(+), 15 deletions(-)
create mode 100644 monitor/iwtrace.bpf.c
create mode 100644 monitor/iwtrace.c
create mode 100644 monitor/iwtrace.h
diff --git a/Makefile.am b/Makefile.am
index 5ed6ab37164b..601db513d1e3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -327,23 +327,39 @@ endif
endif
if MONITOR
+MONITOR_SOURCES = linux/nl80211.h \
+ monitor/nlmon.h monitor/nlmon.c \
+ monitor/pcap.h monitor/pcap.c \
+ monitor/display.h monitor/display.c \
+ src/ie.h src/ie.c \
+ src/wscutil.h src/wscutil.c \
+ src/mpdu.h src/mpdu.c \
+ src/util.h src/util.c \
+ src/crypto.h src/crypto.c \
+ src/watchlist.h src/watchlist.c \
+ src/eapolutil.h src/eapolutil.c \
+ src/nl80211cmd.h src/nl80211cmd.c \
+ src/p2putil.c src/p2putil.h \
+ src/anqputil.h src/anqputil.c \
+ src/band.h src/band.c
bin_PROGRAMS += monitor/iwmon
-monitor_iwmon_SOURCES = monitor/main.c linux/nl80211.h \
- monitor/nlmon.h monitor/nlmon.c \
- monitor/pcap.h monitor/pcap.c \
- monitor/display.h monitor/display.c \
- src/ie.h src/ie.c \
- src/wscutil.h src/wscutil.c \
- src/mpdu.h src/mpdu.c \
- src/util.h src/util.c \
- src/crypto.h src/crypto.c \
- src/watchlist.h src/watchlist.c \
- src/eapolutil.h src/eapolutil.c \
- src/nl80211cmd.h src/nl80211cmd.c \
- src/p2putil.c src/p2putil.h \
- src/anqputil.h src/anqputil.c \
- src/band.h src/band.c
+if BPF
+bin_PROGRAMS += monitor/iwtrace
+
+monitor/iwtrace.bpf.o: monitor/iwtrace.bpf.c
+ clang -g -O2 -target bpf -c monitor/iwtrace.bpf.c -o monitor/iwtrace.tmp.bpf.o
+ bpftool gen object monitor/iwtrace.bpf.o monitor/iwtrace.tmp.bpf.o
+
+monitor/iwtrace.skel.h: monitor/iwtrace.bpf.o
+ bpftool gen skeleton monitor/iwtrace.bpf.o > monitor/iwtrace.skel.h
+
+monitor_iwtrace_SOURCES = monitor/iwtrace.c monitor/iwtrace.h \
+ monitor/iwtrace.skel.h $(MONITOR_SOURCES)
+monitor_iwtrace_LDADD = $(ell_ldadd) $(LIBBPF_LIBS)
+endif
+
+monitor_iwmon_SOURCES = monitor/main.c $(MONITOR_SOURCES)
monitor_iwmon_LDADD = $(ell_ldadd)
if MANUAL_PAGES
diff --git a/configure.ac b/configure.ac
index d84e035c2f5f..bb7bfedcf80f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -200,6 +200,17 @@ AC_ARG_ENABLE([monitor], AS_HELP_STRING([--disable-monitor],
[enable_monitor=${enableval}])
AM_CONDITIONAL(MONITOR, test "${enable_monitor}" != "no")
+AC_ARG_ENABLE([bpf], AS_HELP_STRING([--disable-bpf],
+ [don't enable bpf based tracing]),
+ [enable_bpf=${enableval}])
+if (test "${enable_bpf}" != "no"); then
+ PKG_CHECK_MODULES(LIBBPF, libbpf, dummy=yes,
+ AC_MSG_ERROR(libbpf is required))
+ AC_SUBST(LIBBPF_CFLAGS)
+ AC_SUBST(LIBBPF_LIBS)
+fi
+AM_CONDITIONAL(BPF, test "${enable_bpf}" != "no")
+
AC_ARG_ENABLE([dbus-policy], AS_HELP_STRING([--disable-dbus-policy],
[don't install D-Bus system policy files]),
[enable_dbus_policy=${enableval}])
diff --git a/monitor/iwtrace.bpf.c b/monitor/iwtrace.bpf.c
new file mode 100644
index 000000000000..251b54fc2a41
--- /dev/null
+++ b/monitor/iwtrace.bpf.c
@@ -0,0 +1,194 @@
+/*
+ * Embedded Linux library
+ * Copyright (C) 2023 Cruise, LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <linux/types.h>
+#include <linux/bpf.h>
+#include <linux/netlink.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include "iwtrace.h"
+
+char LICENSE[] SEC("license") = "GPL";
+
+struct sock;
+struct netlink_ext_ack;
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 256 * 1024);
+} rb SEC(".maps");
+
+struct sock {
+ uint16_t sk_protocol;
+};
+
+struct sk_buff {
+ unsigned int len, data_len;
+ unsigned char *data;
+ struct net_device *dev;
+ union {
+ struct sock *sk;
+ int ip_defrag_offset;
+ };
+};
+
+struct capture_256 {
+ struct metadata meta;
+ uint8_t packet[256 - sizeof(struct metadata)];
+} __attribute__ ((packed));
+
+struct capture_1k {
+ struct metadata meta;
+ uint8_t packet[1024 - sizeof(struct metadata)];
+} __attribute__ ((packed));
+
+struct capture_4k {
+ struct metadata meta;
+ uint8_t packet[4096 - sizeof(struct metadata)];
+} __attribute__ ((packed));
+
+struct capture_8k {
+ struct metadata meta;
+ uint8_t packet[8192 - sizeof(struct metadata)];
+} __attribute__ ((packed));
+
+struct capture_16k {
+ struct metadata meta;
+ uint8_t packet[16384];
+} __attribute__ ((packed));
+
+static void metadata_fill(struct metadata *meta, const struct sk_buff *skb)
+{
+ struct sock *sk = __builtin_preserve_access_index(skb->sk);
+
+ meta->timestamp = bpf_ktime_get_boot_ns();
+ meta->len = __builtin_preserve_access_index(skb->len);
+ meta->protocol = __builtin_preserve_access_index(sk->sk_protocol);
+}
+
+static int capture_common(const struct sk_buff *skb)
+{
+ uint16_t len = __builtin_preserve_access_index(skb->len);
+ const void *data = __builtin_preserve_access_index(skb->data);
+
+ /*
+ * bpf_ringbuf_reserve is currently limited to a known constant
+ * value, and cannot handle values that are not constant (even if
+ * bounded). bpf_ringbuf_output might be suitable, but no metadata
+ * could be prepended if that is used. Another alternative is to use
+ * a perf buffer, but it is per-CPU and might result in packets being
+ * processed out of order. We trick the validator by using several
+ * well known structure sizes (256/1k/4k/8k/16k) in order to save on
+ * memory space, but the resultant program is larger than it would be
+ * if dynamic sizing was supported.
+ */
+ if (len <= 256 - sizeof(struct metadata)) {
+ struct capture_256 *c256 = bpf_ringbuf_reserve(&rb,
+ sizeof(struct capture_256), 0);
+
+ if (!c256)
+ return 0;
+
+ metadata_fill(&c256->meta, skb);
+
+ if (bpf_probe_read_kernel(c256->packet, len, data) < 0)
+ bpf_ringbuf_discard(c256, BPF_RB_NO_WAKEUP);
+ else
+ bpf_ringbuf_submit(c256, 0);
+
+ return 0;
+ }
+
+ if (len <= sizeof(struct capture_1k) - sizeof(struct metadata)) {
+ struct capture_1k *c1k = bpf_ringbuf_reserve(&rb,
+ sizeof(struct capture_1k), 0);
+
+ if (!c1k)
+ return 0;
+
+ metadata_fill(&c1k->meta, skb);
+
+ if (bpf_probe_read_kernel(c1k->packet, len, data) < 0)
+ bpf_ringbuf_discard(c1k, BPF_RB_NO_WAKEUP);
+ else
+ bpf_ringbuf_submit(c1k, 0);
+
+ return 0;
+ }
+
+ if (len <= sizeof(struct capture_4k) - sizeof(struct metadata)) {
+ struct capture_4k *c4k = bpf_ringbuf_reserve(&rb,
+ sizeof(struct capture_4k), 0);
+
+ if (!c4k)
+ return 0;
+
+ metadata_fill(&c4k->meta, skb);
+
+ if (bpf_probe_read_kernel(c4k->packet, len, data) < 0)
+ bpf_ringbuf_discard(c4k, BPF_RB_NO_WAKEUP);
+ else
+ bpf_ringbuf_submit(c4k, 0);
+
+ return 0;
+ }
+
+ if (len <= sizeof(struct capture_8k) - sizeof(struct metadata)) {
+ struct capture_8k *c8k = bpf_ringbuf_reserve(&rb,
+ sizeof(struct capture_8k), 0);
+
+ if (!c8k)
+ return 0;
+
+ metadata_fill(&c8k->meta, skb);
+
+ if (bpf_probe_read_kernel(c8k->packet, len, data) < 0)
+ bpf_ringbuf_discard(c8k, BPF_RB_NO_WAKEUP);
+ else
+ bpf_ringbuf_submit(c8k, 0);
+
+ return 0;
+ }
+
+ /* 16384 is the largest packet size for genl currently */
+ if (len <= 16384) {
+ struct capture_16k *c16k = bpf_ringbuf_reserve(&rb,
+ sizeof(struct capture_16k), 0);
+
+ if (!c16k)
+ return 0;
+
+ metadata_fill(&c16k->meta, skb);
+
+ if (bpf_probe_read_kernel(c16k->packet, len, data) < 0)
+ bpf_ringbuf_discard(c16k, BPF_RB_NO_WAKEUP);
+ else
+ bpf_ringbuf_submit(c16k, 0);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+SEC("fentry/__netlink_sendskb")
+int BPF_PROG(trace___netlink_sendskb, struct sock *sk, struct sk_buff *skb)
+{
+ return capture_common(skb);
+}
+
+SEC("fentry/netlink_rcv_skb")
+int BPF_PROG(trace_netlink_rcv_skb, struct sk_buff *skb,
+ int (*cb)(struct sk_buff *,
+ struct nlmsghdr *,
+ struct netlink_ext_ack *))
+{
+ return capture_common(skb);
+}
diff --git a/monitor/iwtrace.c b/monitor/iwtrace.c
new file mode 100644
index 000000000000..ceff71753463
--- /dev/null
+++ b/monitor/iwtrace.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2023 Cruise, LLC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <argp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <inttypes.h>
+#include <arpa/inet.h>
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include <linux/rtnetlink.h>
+#include "iwtrace.h"
+#include "iwtrace.skel.h"
+#include "monitor/nlmon.h"
+#include "monitor/pcap.h"
+#include "monitor/display.h"
+
+#include <ell/ell.h>
+
+static struct iwtrace_bpf *skel;
+static struct ring_buffer *rb;
+static struct l_io *io;
+static struct l_genl *genl;
+static struct nlmon_config config;
+static const char *writer_path;
+static struct nlmon *nlmon;
+
+#ifndef ARPHRD_NETLINK
+#define ARPHRD_NETLINK 824
+#endif
+
+static int libbpf_print_fn(enum libbpf_print_level level,
+ const char *format, va_list args)
+{
+ return vfprintf(stderr, format, args);
+}
+
+static int handle_packet(void *ctx, void *data, size_t size)
+{
+ struct metadata *meta = data;
+ struct timeval tv;
+
+ data += sizeof(struct metadata);
+
+ tv.tv_sec = meta->timestamp / L_NSEC_PER_SEC;
+ tv.tv_usec = (meta->timestamp % L_NSEC_PER_SEC) / L_NSEC_PER_USEC;
+
+ switch (meta->protocol) {
+ case NETLINK_ROUTE:
+ nlmon_print_rtnl(nlmon, &tv, data, meta->len);
+ break;
+ case NETLINK_GENERIC:
+ nlmon_print_genl(nlmon, &tv, data, meta->len);
+ break;
+ }
+
+ return 0;
+}
+
+static bool ringbuf_receive(struct l_io *io, void *user_data)
+{
+ ring_buffer__poll(rb, 0);
+
+ return true;
+}
+
+static void nl80211_appeared(const struct l_genl_family_info *info,
+ void *user_data)
+{
+ int err;
+
+ err = iwtrace_bpf__attach(skel);
+ if (err) {
+ fprintf(stderr, "Unable to attach eBPF program\n");
+ goto failed;
+ }
+
+ rb = ring_buffer__new(bpf_map__fd(skel->maps.rb),
+ handle_packet, NULL, NULL);
+ if (!rb) {
+ fprintf(stderr, "Failed to create ringbuffer\n");
+ goto failed;
+ }
+
+ nlmon = nlmon_open(l_genl_family_info_get_id(info),
+ writer_path, &config);
+ if (!nlmon)
+ goto failed;
+
+ io = l_io_new(bpf_map__fd(skel->maps.rb));
+ l_io_set_close_on_destroy(io, false);
+ l_io_set_read_handler(io, ringbuf_receive, NULL, NULL);
+
+ return;
+failed:
+ l_main_quit();
+}
+
+static void signal_handler(uint32_t signo, void *user_data)
+{
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ l_main_quit();
+ break;
+ }
+}
+
+static void usage(void)
+{
+ printf("iwtrace - Wireless monitor using eBPF\n"
+ "Usage:\n");
+ printf("\tiwtrace [options]\n");
+ printf("Options:\n"
+ "\t-w, --write <file> Write netlink PCAP trace file\n"
+ "\t-n, --nortnl Don't show RTNL output\n"
+ "\t-y, --nowiphy Don't show 'New Wiphy' output\n"
+ "\t-s, --noscan Don't show scan result output\n"
+ "\t-e, --noies Don't show IEs except SSID\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "write", required_argument, NULL, 'w' },
+ { "nortnl", no_argument, NULL, 'n' },
+ { "nowiphy", no_argument, NULL, 'y' },
+ { "noscan", no_argument, NULL, 's' },
+ { "noies", no_argument, NULL, 'e' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+int main(int argc, char *argv[])
+{
+ int exit_status = EXIT_FAILURE;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "w:nvhyse",
+ main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'w':
+ writer_path = optarg;
+ break;
+ case 'n':
+ config.nortnl = true;
+ break;
+ case 'y':
+ config.nowiphy = true;
+ break;
+ case 's':
+ config.noscan = true;
+ break;
+ case 'e':
+ config.noies = true;
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind > 0) {
+ fprintf(stderr, "Invalid command line parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ libbpf_set_print(libbpf_print_fn);
+
+ skel = iwtrace_bpf__open_and_load();
+ if (!skel)
+ return EXIT_FAILURE;
+
+ fprintf(stdout, "Wireless monitor (eBPF) ver %s\n", VERSION);
+
+ if (!l_main_init())
+ goto init_failed;
+
+ genl = l_genl_new();
+ if (!genl) {
+ fprintf(stderr, "Failed to open generic netlink socket\n");
+ goto genl_failed;
+ }
+
+ l_genl_request_family(genl, "nl80211", nl80211_appeared, NULL, NULL);
+ exit_status = l_main_run_with_signal(signal_handler, NULL);
+
+ l_genl_unref(genl);
+ nlmon_close(nlmon);
+ l_io_destroy(io);
+ ring_buffer__free(rb);
+
+genl_failed:
+ l_main_exit();
+init_failed:
+ iwtrace_bpf__destroy(skel);
+
+ return exit_status;
+}
diff --git a/monitor/iwtrace.h b/monitor/iwtrace.h
new file mode 100644
index 000000000000..0df95f2ea989
--- /dev/null
+++ b/monitor/iwtrace.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2020 Facebook */
+#ifndef __BOOTSTRAP_H
+#define __BOOTSTRAP_H
+
+struct metadata {
+ uint64_t timestamp;
+ uint16_t len;
+ uint16_t protocol;
+} __attribute__ ((packed));
+
+#endif /* __BOOTSTRAP_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 5/5] RFC: Initial iwtrace utility
2023-12-27 6:09 ` [PATCH 5/5] RFC: Initial iwtrace utility Denis Kenzior
@ 2024-01-02 13:30 ` James Prestwood
0 siblings, 0 replies; 7+ messages in thread
From: James Prestwood @ 2024-01-02 13:30 UTC (permalink / raw)
To: Denis Kenzior, iwd
Hi Denis,
On 12/26/23 10:09 PM, Denis Kenzior wrote:
> eBPF based tracing, similar to iwmon. This is meant as an alternative
> to the nlmon based netlink tracing and can be used on kernels where
> nlmon might not be available.
All looks good to me. This seems like a nice utility if the support is
more widespread than nlmon.
> ---
> Makefile.am | 46 ++++++---
> configure.ac | 11 +++
> monitor/iwtrace.bpf.c | 194 +++++++++++++++++++++++++++++++++++++
> monitor/iwtrace.c | 218 ++++++++++++++++++++++++++++++++++++++++++
> monitor/iwtrace.h | 12 +++
> 5 files changed, 466 insertions(+), 15 deletions(-)
> create mode 100644 monitor/iwtrace.bpf.c
> create mode 100644 monitor/iwtrace.c
> create mode 100644 monitor/iwtrace.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 5ed6ab37164b..601db513d1e3 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -327,23 +327,39 @@ endif
> endif
>
> if MONITOR
> +MONITOR_SOURCES = linux/nl80211.h \
> + monitor/nlmon.h monitor/nlmon.c \
> + monitor/pcap.h monitor/pcap.c \
> + monitor/display.h monitor/display.c \
> + src/ie.h src/ie.c \
> + src/wscutil.h src/wscutil.c \
> + src/mpdu.h src/mpdu.c \
> + src/util.h src/util.c \
> + src/crypto.h src/crypto.c \
> + src/watchlist.h src/watchlist.c \
> + src/eapolutil.h src/eapolutil.c \
> + src/nl80211cmd.h src/nl80211cmd.c \
> + src/p2putil.c src/p2putil.h \
> + src/anqputil.h src/anqputil.c \
> + src/band.h src/band.c
> bin_PROGRAMS += monitor/iwmon
>
> -monitor_iwmon_SOURCES = monitor/main.c linux/nl80211.h \
> - monitor/nlmon.h monitor/nlmon.c \
> - monitor/pcap.h monitor/pcap.c \
> - monitor/display.h monitor/display.c \
> - src/ie.h src/ie.c \
> - src/wscutil.h src/wscutil.c \
> - src/mpdu.h src/mpdu.c \
> - src/util.h src/util.c \
> - src/crypto.h src/crypto.c \
> - src/watchlist.h src/watchlist.c \
> - src/eapolutil.h src/eapolutil.c \
> - src/nl80211cmd.h src/nl80211cmd.c \
> - src/p2putil.c src/p2putil.h \
> - src/anqputil.h src/anqputil.c \
> - src/band.h src/band.c
> +if BPF
> +bin_PROGRAMS += monitor/iwtrace
> +
> +monitor/iwtrace.bpf.o: monitor/iwtrace.bpf.c
> + clang -g -O2 -target bpf -c monitor/iwtrace.bpf.c -o monitor/iwtrace.tmp.bpf.o
> + bpftool gen object monitor/iwtrace.bpf.o monitor/iwtrace.tmp.bpf.o
> +
> +monitor/iwtrace.skel.h: monitor/iwtrace.bpf.o
> + bpftool gen skeleton monitor/iwtrace.bpf.o > monitor/iwtrace.skel.h
> +
> +monitor_iwtrace_SOURCES = monitor/iwtrace.c monitor/iwtrace.h \
> + monitor/iwtrace.skel.h $(MONITOR_SOURCES)
> +monitor_iwtrace_LDADD = $(ell_ldadd) $(LIBBPF_LIBS)
> +endif
> +
> +monitor_iwmon_SOURCES = monitor/main.c $(MONITOR_SOURCES)
> monitor_iwmon_LDADD = $(ell_ldadd)
>
> if MANUAL_PAGES
> diff --git a/configure.ac b/configure.ac
> index d84e035c2f5f..bb7bfedcf80f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -200,6 +200,17 @@ AC_ARG_ENABLE([monitor], AS_HELP_STRING([--disable-monitor],
> [enable_monitor=${enableval}])
> AM_CONDITIONAL(MONITOR, test "${enable_monitor}" != "no")
>
> +AC_ARG_ENABLE([bpf], AS_HELP_STRING([--disable-bpf],
> + [don't enable bpf based tracing]),
> + [enable_bpf=${enableval}])
> +if (test "${enable_bpf}" != "no"); then
> + PKG_CHECK_MODULES(LIBBPF, libbpf, dummy=yes,
> + AC_MSG_ERROR(libbpf is required))
> + AC_SUBST(LIBBPF_CFLAGS)
> + AC_SUBST(LIBBPF_LIBS)
> +fi
> +AM_CONDITIONAL(BPF, test "${enable_bpf}" != "no")
> +
> AC_ARG_ENABLE([dbus-policy], AS_HELP_STRING([--disable-dbus-policy],
> [don't install D-Bus system policy files]),
> [enable_dbus_policy=${enableval}])
> diff --git a/monitor/iwtrace.bpf.c b/monitor/iwtrace.bpf.c
> new file mode 100644
> index 000000000000..251b54fc2a41
> --- /dev/null
> +++ b/monitor/iwtrace.bpf.c
> @@ -0,0 +1,194 @@
> +/*
> + * Embedded Linux library
> + * Copyright (C) 2023 Cruise, LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-only
> + */
> +
> +#include <stdint.h>
> +#include <string.h>
> +#include <linux/types.h>
> +#include <linux/bpf.h>
> +#include <linux/netlink.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <bpf/bpf_core_read.h>
> +#include "iwtrace.h"
> +
> +char LICENSE[] SEC("license") = "GPL";
> +
> +struct sock;
> +struct netlink_ext_ack;
> +
> +struct {
> + __uint(type, BPF_MAP_TYPE_RINGBUF);
> + __uint(max_entries, 256 * 1024);
> +} rb SEC(".maps");
> +
> +struct sock {
> + uint16_t sk_protocol;
> +};
> +
> +struct sk_buff {
> + unsigned int len, data_len;
> + unsigned char *data;
> + struct net_device *dev;
> + union {
> + struct sock *sk;
> + int ip_defrag_offset;
> + };
> +};
> +
> +struct capture_256 {
> + struct metadata meta;
> + uint8_t packet[256 - sizeof(struct metadata)];
> +} __attribute__ ((packed));
> +
> +struct capture_1k {
> + struct metadata meta;
> + uint8_t packet[1024 - sizeof(struct metadata)];
> +} __attribute__ ((packed));
> +
> +struct capture_4k {
> + struct metadata meta;
> + uint8_t packet[4096 - sizeof(struct metadata)];
> +} __attribute__ ((packed));
> +
> +struct capture_8k {
> + struct metadata meta;
> + uint8_t packet[8192 - sizeof(struct metadata)];
> +} __attribute__ ((packed));
> +
> +struct capture_16k {
> + struct metadata meta;
> + uint8_t packet[16384];
> +} __attribute__ ((packed));
> +
> +static void metadata_fill(struct metadata *meta, const struct sk_buff *skb)
> +{
> + struct sock *sk = __builtin_preserve_access_index(skb->sk);
> +
> + meta->timestamp = bpf_ktime_get_boot_ns();
> + meta->len = __builtin_preserve_access_index(skb->len);
> + meta->protocol = __builtin_preserve_access_index(sk->sk_protocol);
> +}
> +
> +static int capture_common(const struct sk_buff *skb)
> +{
> + uint16_t len = __builtin_preserve_access_index(skb->len);
> + const void *data = __builtin_preserve_access_index(skb->data);
> +
> + /*
> + * bpf_ringbuf_reserve is currently limited to a known constant
> + * value, and cannot handle values that are not constant (even if
> + * bounded). bpf_ringbuf_output might be suitable, but no metadata
> + * could be prepended if that is used. Another alternative is to use
> + * a perf buffer, but it is per-CPU and might result in packets being
> + * processed out of order. We trick the validator by using several
> + * well known structure sizes (256/1k/4k/8k/16k) in order to save on
> + * memory space, but the resultant program is larger than it would be
> + * if dynamic sizing was supported.
> + */
> + if (len <= 256 - sizeof(struct metadata)) {
> + struct capture_256 *c256 = bpf_ringbuf_reserve(&rb,
> + sizeof(struct capture_256), 0);
> +
> + if (!c256)
> + return 0;
> +
> + metadata_fill(&c256->meta, skb);
> +
> + if (bpf_probe_read_kernel(c256->packet, len, data) < 0)
> + bpf_ringbuf_discard(c256, BPF_RB_NO_WAKEUP);
> + else
> + bpf_ringbuf_submit(c256, 0);
> +
> + return 0;
> + }
> +
> + if (len <= sizeof(struct capture_1k) - sizeof(struct metadata)) {
> + struct capture_1k *c1k = bpf_ringbuf_reserve(&rb,
> + sizeof(struct capture_1k), 0);
> +
> + if (!c1k)
> + return 0;
> +
> + metadata_fill(&c1k->meta, skb);
> +
> + if (bpf_probe_read_kernel(c1k->packet, len, data) < 0)
> + bpf_ringbuf_discard(c1k, BPF_RB_NO_WAKEUP);
> + else
> + bpf_ringbuf_submit(c1k, 0);
> +
> + return 0;
> + }
> +
> + if (len <= sizeof(struct capture_4k) - sizeof(struct metadata)) {
> + struct capture_4k *c4k = bpf_ringbuf_reserve(&rb,
> + sizeof(struct capture_4k), 0);
> +
> + if (!c4k)
> + return 0;
> +
> + metadata_fill(&c4k->meta, skb);
> +
> + if (bpf_probe_read_kernel(c4k->packet, len, data) < 0)
> + bpf_ringbuf_discard(c4k, BPF_RB_NO_WAKEUP);
> + else
> + bpf_ringbuf_submit(c4k, 0);
> +
> + return 0;
> + }
> +
> + if (len <= sizeof(struct capture_8k) - sizeof(struct metadata)) {
> + struct capture_8k *c8k = bpf_ringbuf_reserve(&rb,
> + sizeof(struct capture_8k), 0);
> +
> + if (!c8k)
> + return 0;
> +
> + metadata_fill(&c8k->meta, skb);
> +
> + if (bpf_probe_read_kernel(c8k->packet, len, data) < 0)
> + bpf_ringbuf_discard(c8k, BPF_RB_NO_WAKEUP);
> + else
> + bpf_ringbuf_submit(c8k, 0);
> +
> + return 0;
> + }
> +
> + /* 16384 is the largest packet size for genl currently */
> + if (len <= 16384) {
> + struct capture_16k *c16k = bpf_ringbuf_reserve(&rb,
> + sizeof(struct capture_16k), 0);
> +
> + if (!c16k)
> + return 0;
> +
> + metadata_fill(&c16k->meta, skb);
> +
> + if (bpf_probe_read_kernel(c16k->packet, len, data) < 0)
> + bpf_ringbuf_discard(c16k, BPF_RB_NO_WAKEUP);
> + else
> + bpf_ringbuf_submit(c16k, 0);
> +
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +SEC("fentry/__netlink_sendskb")
> +int BPF_PROG(trace___netlink_sendskb, struct sock *sk, struct sk_buff *skb)
> +{
> + return capture_common(skb);
> +}
> +
> +SEC("fentry/netlink_rcv_skb")
> +int BPF_PROG(trace_netlink_rcv_skb, struct sk_buff *skb,
> + int (*cb)(struct sk_buff *,
> + struct nlmsghdr *,
> + struct netlink_ext_ack *))
> +{
> + return capture_common(skb);
> +}
> diff --git a/monitor/iwtrace.c b/monitor/iwtrace.c
> new file mode 100644
> index 000000000000..ceff71753463
> --- /dev/null
> +++ b/monitor/iwtrace.c
> @@ -0,0 +1,218 @@
> +/*
> + * Copyright (C) 2023 Cruise, LLC
> + *
> + * SPDX-License-Identifier: LGPL-2.1-or-later
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#define _GNU_SOURCE
> +#include <argp.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <time.h>
> +#include <inttypes.h>
> +#include <arpa/inet.h>
> +#include <sys/resource.h>
> +#include <bpf/libbpf.h>
> +#include <linux/rtnetlink.h>
> +#include "iwtrace.h"
> +#include "iwtrace.skel.h"
> +#include "monitor/nlmon.h"
> +#include "monitor/pcap.h"
> +#include "monitor/display.h"
> +
> +#include <ell/ell.h>
> +
> +static struct iwtrace_bpf *skel;
> +static struct ring_buffer *rb;
> +static struct l_io *io;
> +static struct l_genl *genl;
> +static struct nlmon_config config;
> +static const char *writer_path;
> +static struct nlmon *nlmon;
> +
> +#ifndef ARPHRD_NETLINK
> +#define ARPHRD_NETLINK 824
> +#endif
> +
> +static int libbpf_print_fn(enum libbpf_print_level level,
> + const char *format, va_list args)
> +{
> + return vfprintf(stderr, format, args);
> +}
> +
> +static int handle_packet(void *ctx, void *data, size_t size)
> +{
> + struct metadata *meta = data;
> + struct timeval tv;
> +
> + data += sizeof(struct metadata);
> +
> + tv.tv_sec = meta->timestamp / L_NSEC_PER_SEC;
> + tv.tv_usec = (meta->timestamp % L_NSEC_PER_SEC) / L_NSEC_PER_USEC;
> +
> + switch (meta->protocol) {
> + case NETLINK_ROUTE:
> + nlmon_print_rtnl(nlmon, &tv, data, meta->len);
> + break;
> + case NETLINK_GENERIC:
> + nlmon_print_genl(nlmon, &tv, data, meta->len);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static bool ringbuf_receive(struct l_io *io, void *user_data)
> +{
> + ring_buffer__poll(rb, 0);
> +
> + return true;
> +}
> +
> +static void nl80211_appeared(const struct l_genl_family_info *info,
> + void *user_data)
> +{
> + int err;
> +
> + err = iwtrace_bpf__attach(skel);
> + if (err) {
> + fprintf(stderr, "Unable to attach eBPF program\n");
> + goto failed;
> + }
> +
> + rb = ring_buffer__new(bpf_map__fd(skel->maps.rb),
> + handle_packet, NULL, NULL);
> + if (!rb) {
> + fprintf(stderr, "Failed to create ringbuffer\n");
> + goto failed;
> + }
> +
> + nlmon = nlmon_open(l_genl_family_info_get_id(info),
> + writer_path, &config);
> + if (!nlmon)
> + goto failed;
> +
> + io = l_io_new(bpf_map__fd(skel->maps.rb));
> + l_io_set_close_on_destroy(io, false);
> + l_io_set_read_handler(io, ringbuf_receive, NULL, NULL);
> +
> + return;
> +failed:
> + l_main_quit();
> +}
> +
> +static void signal_handler(uint32_t signo, void *user_data)
> +{
> + switch (signo) {
> + case SIGINT:
> + case SIGTERM:
> + l_main_quit();
> + break;
> + }
> +}
> +
> +static void usage(void)
> +{
> + printf("iwtrace - Wireless monitor using eBPF\n"
> + "Usage:\n");
> + printf("\tiwtrace [options]\n");
> + printf("Options:\n"
> + "\t-w, --write <file> Write netlink PCAP trace file\n"
> + "\t-n, --nortnl Don't show RTNL output\n"
> + "\t-y, --nowiphy Don't show 'New Wiphy' output\n"
> + "\t-s, --noscan Don't show scan result output\n"
> + "\t-e, --noies Don't show IEs except SSID\n"
> + "\t-h, --help Show help options\n");
> +}
> +
> +static const struct option main_options[] = {
> + { "write", required_argument, NULL, 'w' },
> + { "nortnl", no_argument, NULL, 'n' },
> + { "nowiphy", no_argument, NULL, 'y' },
> + { "noscan", no_argument, NULL, 's' },
> + { "noies", no_argument, NULL, 'e' },
> + { "version", no_argument, NULL, 'v' },
> + { "help", no_argument, NULL, 'h' },
> + { }
> +};
> +
> +int main(int argc, char *argv[])
> +{
> + int exit_status = EXIT_FAILURE;
> +
> + for (;;) {
> + int opt;
> +
> + opt = getopt_long(argc, argv, "w:nvhyse",
> + main_options, NULL);
> + if (opt < 0)
> + break;
> +
> + switch (opt) {
> + case 'w':
> + writer_path = optarg;
> + break;
> + case 'n':
> + config.nortnl = true;
> + break;
> + case 'y':
> + config.nowiphy = true;
> + break;
> + case 's':
> + config.noscan = true;
> + break;
> + case 'e':
> + config.noies = true;
> + break;
> + case 'v':
> + printf("%s\n", VERSION);
> + return EXIT_SUCCESS;
> + case 'h':
> + usage();
> + return EXIT_SUCCESS;
> + default:
> + return EXIT_FAILURE;
> + }
> + }
> +
> + if (argc - optind > 0) {
> + fprintf(stderr, "Invalid command line parameters\n");
> + return EXIT_FAILURE;
> + }
> +
> + libbpf_set_print(libbpf_print_fn);
> +
> + skel = iwtrace_bpf__open_and_load();
> + if (!skel)
> + return EXIT_FAILURE;
> +
> + fprintf(stdout, "Wireless monitor (eBPF) ver %s\n", VERSION);
> +
> + if (!l_main_init())
> + goto init_failed;
> +
> + genl = l_genl_new();
> + if (!genl) {
> + fprintf(stderr, "Failed to open generic netlink socket\n");
> + goto genl_failed;
> + }
> +
> + l_genl_request_family(genl, "nl80211", nl80211_appeared, NULL, NULL);
> + exit_status = l_main_run_with_signal(signal_handler, NULL);
> +
> + l_genl_unref(genl);
> + nlmon_close(nlmon);
> + l_io_destroy(io);
> + ring_buffer__free(rb);
> +
> +genl_failed:
> + l_main_exit();
> +init_failed:
> + iwtrace_bpf__destroy(skel);
> +
> + return exit_status;
> +}
> diff --git a/monitor/iwtrace.h b/monitor/iwtrace.h
> new file mode 100644
> index 000000000000..0df95f2ea989
> --- /dev/null
> +++ b/monitor/iwtrace.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
> +/* Copyright (c) 2020 Facebook */
> +#ifndef __BOOTSTRAP_H
> +#define __BOOTSTRAP_H
> +
> +struct metadata {
> + uint64_t timestamp;
> + uint16_t len;
> + uint16_t protocol;
> +} __attribute__ ((packed));
> +
> +#endif /* __BOOTSTRAP_H */
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/5] monitor: Remove unused code path
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
` (3 preceding siblings ...)
2023-12-27 6:09 ` [PATCH 5/5] RFC: Initial iwtrace utility Denis Kenzior
@ 2024-01-02 17:01 ` Denis Kenzior
4 siblings, 0 replies; 7+ messages in thread
From: Denis Kenzior @ 2024-01-02 17:01 UTC (permalink / raw)
To: iwd
On 12/27/23 00:09, Denis Kenzior wrote:
> nlmon_print_genl invokes genl_ctrl when a generic netlink control
> message is encountered. genl_ctrl() tries to filter nl80211 family
> appearance messages and setup nlmon->id with the extracted family id.
> However, the id is already provided inside main.c by using nlmon_open,
> and no control messages are processed by nlmon in 'capture' mode (-r
> command line argument not passed) since all genl messages go through
> nlmon_message() path instead.
> ---
> monitor/nlmon.c | 36 +++---------------------------------
> 1 file changed, 3 insertions(+), 33 deletions(-)
>
I went ahead and applied patches 1-4. The RFC one still needs better support
from the build system, so it wasn't applied.
Regards,
-Denis
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-01-02 17:01 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-12-27 6:09 [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
2023-12-27 6:09 ` [PATCH 2/5] monitor: Remove unused PACKET_AUXDATA handling Denis Kenzior
2023-12-27 6:09 ` [PATCH 3/5] monitor: Use nlmon_print_* inside nlmon_receive Denis Kenzior
2023-12-27 6:09 ` [PATCH 4/5] monitor: Move iwmon reading logic into main.c Denis Kenzior
2023-12-27 6:09 ` [PATCH 5/5] RFC: Initial iwtrace utility Denis Kenzior
2024-01-02 13:30 ` James Prestwood
2024-01-02 17:01 ` [PATCH 1/5] monitor: Remove unused code path Denis Kenzior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox