Home | History | Annotate | Download | only in src
      1 /* iftable - table of network interfaces
      2  *
      3  * (C) 2004 by Astaro AG, written by Harald Welte <hwelte (at) astaro.com>
      4  * (C) 2008 by Pablo Neira Ayuso <pablo (at) netfilter.org>
      5  *
      6  * This software is Free Software and licensed under GNU GPLv2.
      7  */
      8 
      9 /* IFINDEX handling */
     10 
     11 #include <unistd.h>
     12 #include <stdlib.h>
     13 #include <stdio.h>
     14 #include <string.h>
     15 #include <sys/types.h>
     16 #include <netinet/in.h>
     17 #include <arpa/inet.h>
     18 #include <errno.h>
     19 #include <assert.h>
     20 
     21 #include <linux/netdevice.h>
     22 
     23 #include <libnfnetlink/libnfnetlink.h>
     24 #include "rtnl.h"
     25 #include "linux_list.h"
     26 
     27 struct ifindex_node {
     28 	struct list_head head;
     29 
     30 	u_int32_t	index;
     31 	u_int32_t	type;
     32 	u_int32_t	alen;
     33 	u_int32_t	flags;
     34 	char		addr[8];
     35 	char		name[16];
     36 };
     37 
     38 struct nlif_handle {
     39 	struct list_head ifindex_hash[16];
     40 	struct rtnl_handle *rtnl_handle;
     41 	struct rtnl_handler ifadd_handler;
     42 	struct rtnl_handler ifdel_handler;
     43 };
     44 
     45 /* iftable_add - Add/Update an entry to/in the interface table
     46  * @n:		netlink message header of a RTM_NEWLINK message
     47  * @arg:	not used
     48  *
     49  * This function adds/updates an entry in the intrface table.
     50  * Returns -1 on error, 1 on success.
     51  */
     52 static int iftable_add(struct nlmsghdr *n, void *arg)
     53 {
     54 	unsigned int hash, found = 0;
     55 	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
     56 	struct ifindex_node *this;
     57 	struct rtattr *cb[IFLA_MAX+1];
     58 	struct nlif_handle *h = arg;
     59 
     60 	if (n->nlmsg_type != RTM_NEWLINK)
     61 		return -1;
     62 
     63 	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
     64 		return -1;
     65 
     66 	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
     67 
     68 	if (!cb[IFLA_IFNAME])
     69 		return -1;
     70 
     71 	hash = ifi_msg->ifi_index & 0xF;
     72 	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
     73 		if (this->index == ifi_msg->ifi_index) {
     74 			found = 1;
     75 			break;
     76 		}
     77 	}
     78 
     79 	if (!found) {
     80 		this = malloc(sizeof(*this));
     81 		if (!this)
     82 			return -1;
     83 
     84 		this->index = ifi_msg->ifi_index;
     85 	}
     86 
     87 	this->type = ifi_msg->ifi_type;
     88 	this->flags = ifi_msg->ifi_flags;
     89 	if (cb[IFLA_ADDRESS]) {
     90 		unsigned int alen;
     91 		this->alen = alen = RTA_PAYLOAD(cb[IFLA_ADDRESS]);
     92 		if (alen > sizeof(this->addr))
     93 			alen = sizeof(this->addr);
     94 		memcpy(this->addr, RTA_DATA(cb[IFLA_ADDRESS]), alen);
     95 	} else {
     96 		this->alen = 0;
     97 		memset(this->addr, 0, sizeof(this->addr));
     98 	}
     99 	strcpy(this->name, RTA_DATA(cb[IFLA_IFNAME]));
    100 
    101 	if (!found)
    102 		list_add(&this->head, &h->ifindex_hash[hash]);
    103 
    104 	return 1;
    105 }
    106 
    107 /* iftable_del - Delete an entry from the interface table
    108  * @n:		netlink message header of a RTM_DELLINK nlmsg
    109  * @arg:	not used
    110  *
    111  * Delete an entry from the interface table.
    112  * Returns -1 on error, 0 if no matching entry was found or 1 on success.
    113  */
    114 static int iftable_del(struct nlmsghdr *n, void *arg)
    115 {
    116 	struct ifinfomsg *ifi_msg = NLMSG_DATA(n);
    117 	struct rtattr *cb[IFLA_MAX+1];
    118 	struct nlif_handle *h = arg;
    119 	struct ifindex_node *this, *tmp;
    120 	unsigned int hash;
    121 
    122 	if (n->nlmsg_type != RTM_DELLINK)
    123 		return -1;
    124 
    125 	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi_msg)))
    126 		return -1;
    127 
    128 	rtnl_parse_rtattr(cb, IFLA_MAX, IFLA_RTA(ifi_msg), IFLA_PAYLOAD(n));
    129 
    130 	hash = ifi_msg->ifi_index & 0xF;
    131 	list_for_each_entry_safe(this, tmp, &h->ifindex_hash[hash], head) {
    132 		if (this->index == ifi_msg->ifi_index) {
    133 			list_del(&this->head);
    134 			free(this);
    135 			return 1;
    136 		}
    137 	}
    138 
    139 	return 0;
    140 }
    141 
    142 /** Get the name for an ifindex
    143  *
    144  * \param nlif_handle A pointer to a ::nlif_handle created
    145  * \param index ifindex to be resolved
    146  * \param name interface name, pass a buffer of IFNAMSIZ size
    147  * \return -1 on error, 1 on success
    148  */
    149 int nlif_index2name(struct nlif_handle *h,
    150 		    unsigned int index,
    151 		    char *name)
    152 {
    153 	unsigned int hash;
    154 	struct ifindex_node *this;
    155 
    156 	assert(h != NULL);
    157 	assert(name != NULL);
    158 
    159 	if (index == 0) {
    160 		strcpy(name, "*");
    161 		return 1;
    162 	}
    163 
    164 	hash = index & 0xF;
    165 	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
    166 		if (this->index == index) {
    167 			strcpy(name, this->name);
    168 			return 1;
    169 		}
    170 	}
    171 
    172 	errno = ENOENT;
    173 	return -1;
    174 }
    175 
    176 /** Get the flags for an ifindex
    177  *
    178  * \param nlif_handle A pointer to a ::nlif_handle created
    179  * \param index ifindex to be resolved
    180  * \param flags pointer to variable used to store the interface flags
    181  * \return -1 on error, 1 on success
    182  */
    183 int nlif_get_ifflags(const struct nlif_handle *h,
    184 		     unsigned int index,
    185 		     unsigned int *flags)
    186 {
    187 	unsigned int hash;
    188 	struct ifindex_node *this;
    189 
    190 	assert(h != NULL);
    191 	assert(flags != NULL);
    192 
    193 	if (index == 0) {
    194 		errno = ENOENT;
    195 		return -1;
    196 	}
    197 
    198 	hash = index & 0xF;
    199 	list_for_each_entry(this, &h->ifindex_hash[hash], head) {
    200 		if (this->index == index) {
    201 			*flags = this->flags;
    202 			return 1;
    203 		}
    204 	}
    205 	errno = ENOENT;
    206 	return -1;
    207 }
    208 
    209 /** Initialize interface table
    210  *
    211  * Initialize rtnl interface and interface table
    212  * Call this before any nlif_* function
    213  *
    214  * \return file descriptor to netlink socket
    215  */
    216 struct nlif_handle *nlif_open(void)
    217 {
    218 	int i;
    219 	struct nlif_handle *h;
    220 
    221 	h = calloc(1,  sizeof(struct nlif_handle));
    222 	if (h == NULL)
    223 		goto err;
    224 
    225 	for (i=0; i<16; i++)
    226 		INIT_LIST_HEAD(&h->ifindex_hash[i]);
    227 
    228 	h->ifadd_handler.nlmsg_type = RTM_NEWLINK;
    229 	h->ifadd_handler.handlefn = iftable_add;
    230 	h->ifadd_handler.arg = h;
    231 	h->ifdel_handler.nlmsg_type = RTM_DELLINK;
    232 	h->ifdel_handler.handlefn = iftable_del;
    233 	h->ifdel_handler.arg = h;
    234 
    235 	h->rtnl_handle = rtnl_open();
    236 	if (h->rtnl_handle == NULL)
    237 		goto err;
    238 
    239 	if (rtnl_handler_register(h->rtnl_handle, &h->ifadd_handler) < 0)
    240 		goto err_close;
    241 
    242 	if (rtnl_handler_register(h->rtnl_handle, &h->ifdel_handler) < 0)
    243 		goto err_unregister;
    244 
    245 	return h;
    246 
    247 err_unregister:
    248 	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
    249 err_close:
    250 	rtnl_close(h->rtnl_handle);
    251 	free(h);
    252 err:
    253 	return NULL;
    254 }
    255 
    256 /** Destructor of interface table
    257  *
    258  * \param nlif_handle A pointer to a ::nlif_handle created
    259  * via nlif_open()
    260  */
    261 void nlif_close(struct nlif_handle *h)
    262 {
    263 	int i;
    264 	struct ifindex_node *this, *tmp;
    265 
    266 	assert(h != NULL);
    267 
    268 	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
    269 	rtnl_handler_unregister(h->rtnl_handle, &h->ifdel_handler);
    270 	rtnl_close(h->rtnl_handle);
    271 
    272 	for (i=0; i<16; i++) {
    273 		list_for_each_entry_safe(this, tmp, &h->ifindex_hash[i], head) {
    274 			list_del(&this->head);
    275 			free(this);
    276 		}
    277 	}
    278 
    279 	free(h);
    280 	h = NULL; /* bugtrap */
    281 }
    282 
    283 /** Receive message from netlink and update interface table
    284  *
    285  * \param nlif_handle A pointer to a ::nlif_handle created
    286  * \return 0 if OK
    287  */
    288 int nlif_catch(struct nlif_handle *h)
    289 {
    290 	assert(h != NULL);
    291 
    292 	if (h->rtnl_handle)
    293 		return rtnl_receive(h->rtnl_handle);
    294 
    295 	return -1;
    296 }
    297 
    298 static int nlif_catch_multi(struct nlif_handle *h)
    299 {
    300 	assert(h != NULL);
    301 
    302 	if (h->rtnl_handle)
    303 		return rtnl_receive_multi(h->rtnl_handle);
    304 
    305 	return -1;
    306 }
    307 
    308 /**
    309  * nlif_query - request a dump of interfaces available in the system
    310  * @h: pointer to a valid nlif_handler
    311  */
    312 int nlif_query(struct nlif_handle *h)
    313 {
    314 	assert(h != NULL);
    315 
    316 	if (rtnl_dump_type(h->rtnl_handle, RTM_GETLINK) < 0)
    317 		return -1;
    318 
    319 	return nlif_catch_multi(h);
    320 }
    321 
    322 /** Returns socket descriptor for the netlink socket
    323  *
    324  * \param nlif_handle A pointer to a ::nlif_handle created
    325  * \return The fd or -1 if there's an error
    326  */
    327 int nlif_fd(struct nlif_handle *h)
    328 {
    329 	assert(h != NULL);
    330 
    331 	if (h->rtnl_handle)
    332 		return h->rtnl_handle->rtnl_fd;
    333 
    334 	return -1;
    335 }
    336