• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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  */
iftable_add(struct nlmsghdr * n,void * arg)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  */
iftable_del(struct nlmsghdr * n,void * arg)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  */
nlif_index2name(struct nlif_handle * h,unsigned int index,char * name)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  */
nlif_get_ifflags(const struct nlif_handle * h,unsigned int index,unsigned int * flags)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  */
nlif_open(void)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  */
nlif_close(struct nlif_handle * h)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  */
nlif_catch(struct nlif_handle * h)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 
nlif_catch_multi(struct nlif_handle * h)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  */
nlif_query(struct nlif_handle * h)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  */
nlif_fd(struct nlif_handle * h)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