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