aboutsummaryrefslogtreecommitdiff
path: root/src/iftable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iftable.c')
-rw-r--r--src/iftable.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/iftable.c b/src/iftable.c
new file mode 100644
index 0000000..0325335
--- /dev/null
+++ b/src/iftable.c
@@ -0,0 +1,335 @@
+/* iftable - table of network interfaces
+ *
+ * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
+ * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software is Free Software and licensed under GNU GPLv2.
+ */
+
+/* IFINDEX handling */
+
+#include <unistd.h>
+#include <stdlib.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"
+
+struct ifindex_node {
+ struct list_head head;
+
+ u_int32_t index;
+ u_int32_t type;
+ u_int32_t alen;
+ u_int32_t flags;
+ char addr[8];
+ char name[16];
+};
+
+struct nlif_handle {
+ struct list_head ifindex_hash[16];
+ struct rtnl_handle *rtnl_handle;
+ struct rtnl_handler ifadd_handler;
+ struct rtnl_handler ifdel_handler;
+};
+
+/* iftable_add - Add/Update an entry to/in the interface table
+ * @n: netlink message header of a RTM_NEWLINK message
+ * @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
+ * @n: netlink message header of a RTM_DELLINK nlmsg
+ * @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;
+}
+
+/** Get the name for an ifindex
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param index ifindex to be resolved
+ * \param name interface name, pass a buffer of IFNAMSIZ size
+ * \return -1 on error, 1 on success
+ */
+int nlif_index2name(struct nlif_handle *h,
+ unsigned int index,
+ char *name)
+{
+ unsigned int hash;
+ struct ifindex_node *this;
+
+ assert(h != NULL);
+ assert(name != NULL);
+
+ if (index == 0) {
+ strcpy(name, "*");
+ return 1;
+ }
+
+ hash = index & 0xF;
+ list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+ if (this->index == index) {
+ strcpy(name, this->name);
+ return 1;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/** Get the flags for an ifindex
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \param index ifindex to be resolved
+ * \param flags pointer to variable used to store the interface flags
+ * \return -1 on error, 1 on success
+ */
+int nlif_get_ifflags(const struct nlif_handle *h,
+ unsigned int index,
+ unsigned int *flags)
+{
+ unsigned int hash;
+ struct ifindex_node *this;
+
+ assert(h != NULL);
+ assert(flags != NULL);
+
+ if (index == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ hash = index & 0xF;
+ list_for_each_entry(this, &h->ifindex_hash[hash], head) {
+ if (this->index == index) {
+ *flags = this->flags;
+ return 1;
+ }
+ }
+ errno = ENOENT;
+ return -1;
+}
+
+/** Initialize interface table
+ *
+ * Initialize rtnl interface and interface table
+ * Call this before any nlif_* function
+ *
+ * \return file descriptor to netlink socket
+ */
+struct nlif_handle *nlif_open(void)
+{
+ int i;
+ struct nlif_handle *h;
+
+ h = calloc(1, sizeof(struct nlif_handle));
+ if (h == NULL)
+ goto err;
+
+ for (i=0; i<16; 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->rtnl_handle = rtnl_open();
+ if (h->rtnl_handle == NULL)
+ goto err;
+
+ if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0)
+ goto err_close;
+
+ if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0)
+ goto err_unregister;
+
+ return h;
+
+err_unregister:
+ rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
+err_close:
+ rtnl_close(h->rtnl_handle);
+ free(h);
+err:
+ return NULL;
+}
+
+/** Destructor of interface table
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * via nlif_open()
+ */
+void nlif_close(struct nlif_handle *h)
+{
+ int i;
+ struct ifindex_node *this, *tmp;
+
+ assert(h != NULL);
+
+ 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++) {
+ list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
+ list_del(&this->head);
+ free(this);
+ }
+ }
+
+ free(h);
+ h = NULL; /* bugtrap */
+}
+
+/** Receive message from netlink and update interface table
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \return 0 if OK
+ */
+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);
+
+ return -1;
+}
+
+/**
+ * nlif_query - request a dump of interfaces available in the system
+ * @h: pointer to a valid nlif_handler
+ */
+int nlif_query(struct nlif_handle *h)
+{
+ assert(h != NULL);
+
+ if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0)
+ return -1;
+
+ return nlif_catch_multi(h);
+}
+
+/** Returns socket descriptor for the netlink socket
+ *
+ * \param nlif_handle A pointer to a ::nlif_handle created
+ * \return The fd or -1 if there's an error
+ */
+int nlif_fd(struct nlif_handle *h)
+{
+ assert(h != NULL);
+
+ if (h->rtnl_handle)
+ return h->rtnl_handle->rtnl_fd;
+
+ return -1;
+}