netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Duncan Roe <duncan_roe@optusnet.com.au>
To: pablo@netfilter.org
Cc: netfilter-devel@vger.kernel.org
Subject: [PATCH libnetfilter_queue 19/32] src: Convert all nlif_* functions to use libmnl
Date: Fri, 15 Mar 2024 18:33:34 +1100	[thread overview]
Message-ID: <20240315073347.22628-20-duncan_roe@optusnet.com.au> (raw)
In-Reply-To: <ZcyaQvJ1SvnYgakf@calendula>

In iftable.c, replace calls to functions in rtnetlink.c with inline code
(converted to use libmnl instead of libnfnetlink).

Signed-off-by: Duncan Roe <duncan_roe@optusnet.com.au>
---
 src/Makefile.am |   1 +
 src/iftable.c   | 304 ++++++++++++++++++++++--------------------------
 2 files changed, 138 insertions(+), 167 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index 079853e..a6813e8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ libnetfilter_queue_la_LDFLAGS = -Wc,-nostartfiles \
 				-version-info $(LIBVERSION)
 libnetfilter_queue_la_SOURCES = libnetfilter_queue.c	\
 				nlmsg.c			\
+				iftable.c		\
 				extra/checksum.c	\
 				extra/icmp.c		\
 				extra/ipv6.c		\
diff --git a/src/iftable.c b/src/iftable.c
index 307acc1..76a6cad 100644
--- a/src/iftable.c
+++ b/src/iftable.c
@@ -11,19 +11,27 @@
 
 #include <unistd.h>
 #include <stdlib.h>
+#include <time.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
-#include <assert.h>
 
 #include <linux/netdevice.h>
 
-#include <libnfnetlink/libnfnetlink.h>
-#include "rtnl.h"
-#include "linux_list.h"
+#include <linux/rtnetlink.h>
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+#include "internal.h"
+
+#define NUM_NLIF_BITS 4
+#define NUM_NLIF_ENTRIES (1 << NUM_NLIF_BITS)
+#define NLIF_ENTRY_MASK (NUM_NLIF_ENTRIES -1)
+
+static int data_cb(const struct nlmsghdr *nlh, void *data);
 
 /**
  * \defgroup iftable Functions to manage a table of network interfaces
@@ -42,116 +50,16 @@ struct ifindex_node {
 
 	uint32_t	index;
 	uint32_t	type;
-	uint32_t	alen;
 	uint32_t	flags;
-	char		addr[8];
-	char		name[16];
+	char		name[IFNAMSIZ];
 };
 
 struct nlif_handle {
-	struct list_head ifindex_hash[16];
-	struct rtnl_handle *rtnl_handle;
-	struct rtnl_handler ifadd_handler;
-	struct rtnl_handler ifdel_handler;
+	struct list_head ifindex_hash[NUM_NLIF_ENTRIES];
+	struct mnl_socket *nl;
+	unsigned int portid;
 };
 
-/* iftable_add - Add/Update an entry to/in the interface table
- * \param n:	netlink message header of a RTM_NEWLINK message
- * \param arg:	not used
- *
- * This function adds/updates an entry in the intrface table.
- * Returns -1 on error, 1 on success.
- */
-static int iftable_add(struct nlmsghdr *n, void *arg)
-{
-	unsigned int hash, found = 0;
-	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
-	struct ifindex_node *this;
-	struct rtattr *cb[IFLA_MAX+1];
-	struct nlif_handle *h = arg;
-
-	if (n->nlmsg_type != RTM_NEWLINK)
-		return -1;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
-		return -1;
-
-	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
-
-	if (!cb[IFLA_IFNAME])
-		return -1;
-
-	hash = ifi_msg->ifi_index & 0xF;
-	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
-		if (this->index == ifi_msg->ifi_index) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (!found) {
-		this = malloc(sizeof(*this));
-		if (!this)
-			return -1;
-
-		this->index = ifi_msg->ifi_index;
-	}
-
-	this->type = ifi_msg->ifi_type;
-	this->flags = ifi_msg->ifi_flags;
-	if (cb[IFLA_ADDRESS]) {
-		unsigned int alen;
-		this->alen = alen = RTA_PAYLOAD(cb[IFLA_ADDRESS]);
-		if (alen > sizeof(this->addr))
-			alen = sizeof(this->addr);
-		memcpy(this->addr, RTA_DATA(cb[IFLA_ADDRESS]), alen);
-	} else {
-		this->alen = 0;
-		memset(this->addr, 0, sizeof(this->addr));
-	}
-	strcpy(this->name, RTA_DATA(cb[IFLA_IFNAME]));
-
-	if (!found)
-		list_add(&this->head, &h->ifindex_hash[hash]);
-
-	return 1;
-}
-
-/* iftable_del - Delete an entry from the interface table
- * \param n:	netlink message header of a RTM_DELLINK nlmsg
- * \param arg:	not used
- *
- * Delete an entry from the interface table.
- * Returns -1 on error, 0 if no matching entry was found or 1 on success.
- */
-static int iftable_del(struct nlmsghdr *n, void *arg)
-{
-	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
-	struct rtattr *cb[IFLA_MAX+1];
-	struct nlif_handle *h = arg;
-	struct ifindex_node *this, *tmp;
-	unsigned int hash;
-
-	if (n->nlmsg_type != RTM_DELLINK)
-		return -1;
-
-	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
-		return -1;
-
-	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
-
-	hash = ifi_msg->ifi_index & 0xF;
-	list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash], head) {
-		if (this->index == ifi_msg->ifi_index) {
-			list_del(&this->head);
-			free(this);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
 /**
  * nlif_index2name - get the name for an ifindex
  *
@@ -160,6 +68,7 @@ static int iftable_del(struct nlmsghdr *n, void *arg)
  * \param name interface name, pass a buffer of IFNAMSIZ size
  * \return -1 on error, 1 on success
  */
+EXPORT_SYMBOL
 int nlif_index2name(struct nlif_handle *h,
 		    unsigned int index,
 		    char *name)
@@ -167,9 +76,6 @@ int nlif_index2name(struct nlif_handle *h,
 	unsigned int hash;
 	struct ifindex_node *this;
 
-	assert(h != NULL);
-	assert(name != NULL);
-
 	if (index == 0) {
 		strcpy(name, "*");
 		return 1;
@@ -195,6 +101,7 @@ int nlif_index2name(struct nlif_handle *h,
  * \param flags pointer to variable used to store the interface flags
  * \return -1 on error, 1 on success
  */
+EXPORT_SYMBOL
 int nlif_get_ifflags(const struct nlif_handle *h,
 		     unsigned int index,
 		     unsigned int *flags)
@@ -202,9 +109,6 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 	unsigned int hash;
 	struct ifindex_node *this;
 
-	assert(h != NULL);
-	assert(flags != NULL);
-
 	if (index == 0) {
 		errno = ENOENT;
 		return -1;
@@ -224,11 +128,12 @@ int nlif_get_ifflags(const struct nlif_handle *h,
 /**
  * nlif_open - initialize interface table
  *
- * Initialize rtnl interface and interface table
+ * Open a netlink socket and initialize interface table
  * Call this before any nlif_* function
  *
- * \return file descriptor to netlink socket
+ * \return NULL on error, else valid pointer to an nlif_handle structure
  */
+EXPORT_SYMBOL
 struct nlif_handle *nlif_open(void)
 {
 	int i;
@@ -238,32 +143,22 @@ struct nlif_handle *nlif_open(void)
 	if (h == NULL)
 		goto err;
 
-	for (i=0; i<16; i++)
+	for (i=0; i < NUM_NLIF_ENTRIES; i++)
 		INIT_LIST_HEAD(&h->ifindex_hash[i]);
 
-	h->ifadd_handler.nlmsg_type = RTM_NEWLINK;
-	h->ifadd_handler.handlefn = iftable_add;
-	h->ifadd_handler.arg = h;
-	h->ifdel_handler.nlmsg_type = RTM_DELLINK;
-	h->ifdel_handler.handlefn = iftable_del;
-	h->ifdel_handler.arg = h;
+	h->nl = mnl_socket_open(NETLINK_ROUTE);
+	if (!h->nl)
+		goto err_free;
 
-	h->rtnl_handle = rtnl_open();
-	if (h->rtnl_handle == NULL)
-		goto err;
-
-	if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0)
+	if (mnl_socket_bind(h->nl, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0)
 		goto err_close;
-
-	if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0)
-		goto err_unregister;
+	h->portid = mnl_socket_get_portid(h->nl);
 
 	return h;
 
-err_unregister:
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
 err_close:
-	rtnl_close(h->rtnl_handle);
+	mnl_socket_close(h->nl);
+err_free:
 	free(h);
 err:
 	return NULL;
@@ -274,18 +169,15 @@ err:
  *
  * \param h pointer to nlif_handle created by nlif_open()
  */
+EXPORT_SYMBOL
 void nlif_close(struct nlif_handle *h)
 {
 	int i;
 	struct ifindex_node *this, *tmp;
 
-	assert(h != NULL);
+	mnl_socket_close(h->nl);
 
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
-	rtnl_handler_unregister(h->rtnl_handle, &h->ifdel_handler);
-	rtnl_close(h->rtnl_handle);
-
-	for (i=0; i<16; i++) {
+	for (i=0; i < NUM_NLIF_ENTRIES; i++) {
 		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
 			list_del(&this->head);
 			free(this);
@@ -304,56 +196,134 @@ void nlif_close(struct nlif_handle *h)
  * \param h pointer to nlif_handle created by nlif_open()
  * \return 0 if OK
  */
+EXPORT_SYMBOL
 int nlif_catch(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return rtnl_receive(h->rtnl_handle);
-
-	return -1;
-}
-
-static int nlif_catch_multi(struct nlif_handle *h)
-{
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return rtnl_receive_multi(h->rtnl_handle);
+	/*
+	 * Use MNL_SOCKET_BUFFER_SIZE instead of MNL_SOCKET_DUMP_SIZE
+	 * to keep memory footprint same as it was.
+	 */
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	int ret;
+
+	if (!h->nl)                /* The old library had this test */
+		return -1;
 
-	return -1;
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof buf);
+	if (ret == -1)
+		return -1;
+	return mnl_cb_run(buf, ret, 0, h->portid, data_cb, h) == -1 ? -1 : 0;
 }
 
 /**
  * nlif_query - request a dump of interfaces available in the system
  * \param h: pointer to a valid nlif_handler
+ * \return -1 on err with errno set, else >=0
  */
+EXPORT_SYMBOL
 int nlif_query(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0)
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	uint32_t seq;
+	int ret;
+	struct rtgenmsg *rt;
+
+	nlh = mnl_nlmsg_put_header(buf);
+	nlh->nlmsg_type = RTM_GETLINK;
+	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	nlh->nlmsg_seq = seq = time(NULL);
+	rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
+	rt->rtgen_family = AF_PACKET;
+	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
 		return -1;
-
-	return nlif_catch_multi(h);
+	ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	while (ret > 0) {
+		ret = mnl_cb_run(buf, ret, seq, h->portid, data_cb, h);
+		if (ret <= MNL_CB_STOP)
+			break;
+		ret = mnl_socket_recvfrom(h->nl, buf, sizeof(buf));
+	}
+	return ret;
 }
 
 /**
  * nlif_fd - get file descriptor for the netlink socket
  *
  * \param h pointer to nlif_handle created by nlif_open()
- * \return The fd or -1 if there's an error
+ * \return socket fd or -1 on error
  */
+EXPORT_SYMBOL
 int nlif_fd(struct nlif_handle *h)
 {
-	assert(h != NULL);
-
-	if (h->rtnl_handle)
-		return h->rtnl_handle->rtnl_fd;
-
-	return -1;
+	return h->nl? mnl_socket_get_fd(h->nl) : -1;
 }
 
 /**
  * @}
  */
+
+/*
+ * data_cb - callback for rtnetlink messages
+ *           caller will put nlif_handle in data
+ */
+
+static int data_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct ifinfomsg *ifi_msg = mnl_nlmsg_get_payload(nlh);
+	struct nlif_handle *h = data;
+	struct nlattr *attr;
+	uint32_t hash;
+	struct ifindex_node *this, *tmp;
+
+	if (nlh->nlmsg_type != RTM_NEWLINK && nlh->nlmsg_type != RTM_DELLINK) {
+		errno = EPROTO;
+		return MNL_CB_ERROR;
+	}
+	hash = ifi_msg->ifi_index & NLIF_ENTRY_MASK;
+
+	/* RTM_DELLINK is simple, do it first for less indenting */
+	if (nlh->nlmsg_type == RTM_DELLINK) {
+		/*
+		 * The original code used list_for_each_entry_safe when deleting
+		 * and list_for_each_entry when adding.
+		 * The code is only ever going to delete one entry
+		 * so what does the safe variant achieve?
+		 * In a multi-threaded app,
+		 * I'd suggest a pthread rwlock on all nlif accesses.
+		 */
+		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash],
+					 head) {
+			if (this->index == ifi_msg->ifi_index) {
+				list_del(&this->head);
+				free(this);
+			}
+		}
+	return MNL_CB_OK;
+	}
+
+	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+		if (this->index == ifi_msg->ifi_index)
+			goto found;
+	}
+	this = calloc(1, sizeof(*this));
+	if (!this)
+		return MNL_CB_ERROR;
+	this->index = ifi_msg->ifi_index;
+	this->type = ifi_msg->ifi_type;
+	this->flags = ifi_msg->ifi_flags;
+	list_add(&this->head, &h->ifindex_hash[hash]);
+found:
+	mnl_attr_for_each(attr, nlh, sizeof(*ifi_msg)) {
+		/* All we want is the interface name */
+		if (mnl_attr_get_type(attr) == IFLA_IFNAME) {
+			if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
+				perror("mnl_attr_validate");
+				return MNL_CB_ERROR;
+			}
+			strcpy(this->name, mnl_attr_get_str(attr));
+			break;
+		}
+	}
+	return MNL_CB_OK;
+}
-- 
2.35.8


  parent reply	other threads:[~2024-03-15  7:34 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-13 21:07 [PATCH libnetfilter_queue 0/1] Convert libnetfilter_queue to use entirely libmnl functions Duncan Roe
2024-02-13 21:07 ` [PATCH libnetfilter_queue 1/1] " Duncan Roe
2024-02-14 10:47   ` Pablo Neira Ayuso
2024-02-16  1:41     ` Duncan Roe
2024-02-19  2:27       ` Duncan Roe
2024-03-19 23:58       ` [PATCH libnetfilter_queue 00/32] Convert libnetfilter_queue to not need libnfnetlink Duncan Roe
2024-03-15  7:33     ` Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 01/32] src: Convert nfq_open() to use libmnl Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 02/32] src: Convert nfq_open_nfnl() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 03/32] src: Convert nfq_close() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 04/32] src: Convert nfq_create_queue(), nfq_bind_pf() & nfq_unbind_pf() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 05/32] src: Convert nfq_set_queue_flags() & nfq_set_queue_maxlen() " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 06/32] src: Convert nfq_handle_packet(), nfq_get_secctx(), nfq_get_payload() and all the nfq_get_ functions " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 07/32] src: Convert nfq_set_verdict() and nfq_set_verdict2() to use libmnl if there is no data Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 08/32] src: Incorporate nfnl_rcvbufsiz() in libnetfilter_queue Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 09/32] src: Convert nfq_fd() to use libmnl Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 10/32] src: Convert remaining nfq_* functions " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 11/32] src: Fix checkpatch whitespace and block comment warnings Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 12/32] src: Copy nlif-related code from libnfnetlink Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 13/32] include: Cherry-pick macros and functions that nlif will need Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 14/32] doc: Add linux_list.h to the doxygen system Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 15/32] doc: Eliminate doxygen warnings from linux_list.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 16/32] doc: Eliminate doxygen warnings from iftable.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 17/32] whitespace: remove trailing spaces " Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 18/32] include: Use libmnl.h instead of libnfnetlink.h Duncan Roe
2024-03-15  7:33     ` Duncan Roe [this message]
2024-03-15  7:33     ` [PATCH libnetfilter_queue 20/32] src: Delete rtnl.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 21/32] build: Remove libnfnetlink from the build Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 22/32] include: Remove the last remaining use of a libnfnetlink header Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 23/32] doc: Get doxygen to document useful static inline functions Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 24/32] doc: SYNOPSIS of linux_list.h nominates libnetfilter_queue/libnetfilter_queue.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 25/32] doc: Move nlif usage description from libnetfilter_queue.c to iftable.c Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 26/32] build: Shave some time off build Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 27/32] doc: Resolve most issues with man page generated from linux_list.h Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 28/32] build: Get real & user times back to what they were Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 29/32] doc: Cater for doxygen variants w.r.t. #define stmts Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 30/32] doc: Fix list_empty() doxygen comments Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 31/32] src: Use a cast in place of convoluted construct Duncan Roe
2024-03-15  7:33     ` [PATCH libnetfilter_queue 32/32] whitespace: Fix more checkpatch errors & warnings Duncan Roe

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240315073347.22628-20-duncan_roe@optusnet.com.au \
    --to=duncan_roe@optusnet.com.au \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pablo@netfilter.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).