1/* iftable - table of network interfaces
2 *
3 * (C) 2004 by Astaro AG, written by Harald Welte <hwelte@astaro.com>
4 * (C) 2008 by Pablo Neira Ayuso <pablo@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
27struct 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
38struct 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 */
52static 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 */
114static 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 */
149int 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 */
183int 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 */
216struct 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
247err_unregister:
248	rtnl_handler_unregister(h->rtnl_handle, &h->ifadd_handler);
249err_close:
250	rtnl_close(h->rtnl_handle);
251	free(h);
252err:
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 */
261void 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 */
288int 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
298static 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 */
312int 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 */
327int 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