| /* 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; |
| } |