]> git.meshlink.io Git - catta/blob - iface.c
* add support for both ipv4 AND ipv6 reverse lookups
[catta] / iface.c
1 #include <string.h>
2 #include <sys/socket.h>
3 #include <asm/types.h>
4 #include <linux/netlink.h>
5 #include <linux/rtnetlink.h>
6 #include <errno.h>
7
8 #include "iface.h"
9 #include "netlink.h"
10
11 typedef struct _interface_callback_info {
12     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata);
13     gpointer userdata;
14     struct _interface_callback_info *next;
15 } interface_callback_info;
16
17 typedef struct _address_callback_info {
18     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *i, gpointer userdata);
19     gpointer userdata;
20     struct _address_callback_info *next;
21 } address_callback_info;
22
23 struct _flxInterfaceMonitor {
24     flxNetlink *netlink;
25     GHashTable *hash_table;
26     interface_callback_info *interface_callbacks;
27     address_callback_info *address_callbacks;
28     flxInterface *interfaces;
29     guint query_addr_seq, query_link_seq;
30     enum { LIST_IFACE, LIST_ADDR, LIST_DONE } list;
31 };
32
33 static void run_interface_callbacks(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i) {
34     interface_callback_info *c;
35     g_assert(m);
36     g_assert(i);
37
38     for (c = m->interface_callbacks; c; c = c->next) {
39         g_assert(c->callback);
40         c->callback(m, change, i, c->userdata);
41     }
42 }
43
44 static void run_address_callbacks(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a) {
45     address_callback_info *c;
46     g_assert(m);
47     g_assert(a);
48
49     for (c = m->address_callbacks; c; c = c->next) {
50         g_assert(c->callback);
51         c->callback(m, change, a, c->userdata);
52     }
53 }
54
55 static void free_address(flxInterfaceMonitor *m, flxInterfaceAddress *a) {
56     g_assert(m);
57     g_assert(a);
58     g_assert(a->interface);
59
60     if (a->prev)
61         a->prev->next = a->next;
62     else
63         a->interface->addresses = a->next;
64
65     if (a->next)
66         a->next->prev = a->prev;
67
68     g_free(a);
69 }
70
71 static void free_interface(flxInterfaceMonitor *m, flxInterface *i) {
72     g_assert(m);
73     g_assert(i);
74
75     while (i->addresses)
76         free_address(m, i->addresses);
77
78     if (i->prev)
79         i->prev->next = i->next;
80     else
81         m->interfaces = i->next;
82
83     if (i->next)
84         i->next->prev = i->prev;
85
86     g_hash_table_remove(m->hash_table, &i->index);
87     
88     g_free(i->name);
89     g_free(i);
90 }
91
92 static flxInterfaceAddress* get_address(flxInterfaceMonitor *m, flxInterface *i, const flxAddress *raddr) {
93     flxInterfaceAddress *ia;
94     
95     g_assert(m);
96     g_assert(i);
97     g_assert(raddr);
98
99     for (ia = i->addresses; ia; ia = ia->next) {
100         if (flx_address_cmp(&ia->address, raddr) == 0)
101             return ia;
102     }
103
104     return NULL;
105 }
106
107 static int netlink_list_items(flxNetlink *nl, guint16 type, guint *ret_seq) {
108     struct nlmsghdr *n;
109     struct rtgenmsg *gen;
110     guint8 req[1024];
111     
112     memset(&req, 0, sizeof(req));
113     n = (struct nlmsghdr*) req;
114     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
115     n->nlmsg_type = type;
116     n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
117     n->nlmsg_pid = 0;
118
119     gen = NLMSG_DATA(n);
120     memset(gen, 0, sizeof(struct rtgenmsg));
121     gen->rtgen_family = AF_UNSPEC;
122
123     return flx_netlink_send(nl, n, ret_seq);
124 }
125
126 static void callback(flxNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
127     flxInterfaceMonitor *m = userdata;
128     
129     g_assert(m);
130     g_assert(n);
131     g_assert(m->netlink == nl);
132
133     if (n->nlmsg_type == RTM_NEWLINK) {
134         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
135         flxInterface *i;
136         struct rtattr *a = NULL;
137         size_t l;
138         int changed;
139
140         if (ifinfomsg->ifi_family != AF_UNSPEC)
141             return;
142
143         if ((i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
144             changed = 1;
145         else {
146             i = g_new0(flxInterface, 1);
147             i->index = ifinfomsg->ifi_index;
148             i->addresses = NULL;
149             if ((i->next = m->interfaces))
150                 i->next->prev = i;
151             m->interfaces = i;
152             i->prev = NULL;
153             g_hash_table_insert(m->hash_table, &i->index, i);
154             changed = 0;
155         }
156         
157         i->flags = ifinfomsg->ifi_flags;
158
159         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
160         a = IFLA_RTA(ifinfomsg);
161
162         while (RTA_OK(a, l)) {
163             switch(a->rta_type) {
164                 case IFLA_IFNAME:
165                     g_free(i->name);
166                     i->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
167                     break;
168                     
169                 default:
170                     ;
171             }
172
173             a = RTA_NEXT(a, l);
174         }
175
176         run_interface_callbacks(m, changed ? FLX_INTERFACE_CHANGE : FLX_INTERFACE_NEW, i);
177         
178     } else if (n->nlmsg_type == RTM_DELLINK) {
179         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
180         flxInterface *i;
181         flxInterfaceAddress *a;
182
183         if (ifinfomsg->ifi_family != AF_UNSPEC)
184             return;
185         
186         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
187             return;
188
189         for (a = i->addresses; a; a = a->next)
190             run_address_callbacks(m, FLX_INTERFACE_REMOVE, a);
191
192         run_interface_callbacks(m, FLX_INTERFACE_REMOVE, i);
193
194         free_interface(m, i);
195         
196     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
197
198         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
199         flxInterface *i;
200         struct rtattr *a = NULL;
201         size_t l;
202         int changed;
203         flxAddress raddr;
204         int raddr_valid = 0;
205
206         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
207             return;
208
209         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifaddrmsg->ifa_index)))
210             return;
211
212         raddr.family = ifaddrmsg->ifa_family;
213
214         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
215         a = IFA_RTA(ifaddrmsg);
216
217         while (RTA_OK(a, l)) {
218             switch(a->rta_type) {
219                 case IFA_ADDRESS:
220                     if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
221                         (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
222                         return;
223
224                     memcpy(raddr.data, RTA_DATA(a), RTA_PAYLOAD(a));
225                     raddr_valid = 1;
226                     break;
227                     
228                 default:
229                     ;
230             }
231
232             a = RTA_NEXT(a, l);
233         }
234
235         if (!raddr_valid)
236             return;
237
238         if (n->nlmsg_type == RTM_NEWADDR) {
239             flxInterfaceAddress *addr;
240             
241             if ((addr = get_address(m, i, &raddr)))
242                 changed = 1;
243             else {
244                 addr = g_new0(flxInterfaceAddress, 1);
245                 addr->address = raddr;
246                 addr->interface = i;
247                 if ((addr->next = i->addresses))
248                     addr->next->prev = addr;
249                 i->addresses = addr;
250                 addr->prev = NULL;
251                 
252                 changed = 0;
253             }
254             
255             addr->flags = ifaddrmsg->ifa_flags;
256             addr->scope = ifaddrmsg->ifa_scope;
257             
258             run_address_callbacks(m, changed ? FLX_INTERFACE_CHANGE : FLX_INTERFACE_NEW, addr);
259         } else {
260             flxInterfaceAddress *addr;
261             
262             if (!(addr = get_address(m, i, &raddr)))
263                 return;
264
265             run_address_callbacks(m, FLX_INTERFACE_REMOVE, addr);
266             free_address(m, addr);
267         }
268                 
269     } else if (n->nlmsg_type == NLMSG_DONE) {
270
271         if (m->list == LIST_IFACE) {
272             m->list = LIST_DONE;
273             
274             if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) {
275                 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
276             } else
277                 m->list = LIST_ADDR;
278         } else
279             m->list = LIST_DONE;
280         
281     } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
282         struct nlmsgerr *e = NLMSG_DATA (n);
283                     
284         if (e->error)
285             g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
286     }
287 }
288
289 flxInterfaceMonitor *flx_interface_monitor_new(GMainContext *c) {
290     flxInterfaceMonitor *m = NULL;
291
292     m = g_new0(flxInterfaceMonitor, 1);
293     if (!(m->netlink = flx_netlink_new(c, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
294         goto fail;
295
296     m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
297     m->interface_callbacks = NULL;
298     m->address_callbacks = NULL;
299     m->interfaces = NULL;
300
301     if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
302         goto fail;
303
304     m->list = LIST_IFACE;
305     
306     return m;
307
308 fail:
309     flx_interface_monitor_free(m);
310     return NULL;
311 }
312
313 void flx_interface_monitor_free(flxInterfaceMonitor *m) {
314     g_assert(m);
315
316     if (m->netlink)
317         flx_netlink_free(m->netlink);
318
319     if (m->hash_table)
320         g_hash_table_destroy(m->hash_table);
321
322     while (m->interface_callbacks) {
323         interface_callback_info *c = m->interface_callbacks;
324         m->interface_callbacks = c->next;
325         g_free(c);
326     }
327
328     while (m->address_callbacks) {
329         address_callback_info *c = m->address_callbacks;
330         m->address_callbacks = c->next;
331         g_free(c);
332     }
333     
334     g_free(m);
335 }
336
337
338 const flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index) {
339     g_assert(m);
340     g_assert(index > 0);
341
342     return g_hash_table_lookup(m->hash_table, &index);
343 }
344
345 void flx_interface_monitor_add_interface_callback(
346     flxInterfaceMonitor *m,
347     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
348     gpointer userdata) {
349     
350     interface_callback_info *info;
351     
352     g_assert(m);
353     g_assert(callback);
354
355     info = g_new(interface_callback_info, 1);
356     info->callback = callback;
357     info->userdata = userdata;
358     info->next = m->interface_callbacks;
359     m->interface_callbacks = info;
360 }
361
362 void flx_interface_monitor_remove_interface_callback(
363     flxInterfaceMonitor *m,
364     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterface *i, gpointer userdata),
365     gpointer userdata) {
366
367     interface_callback_info *info, *prev;
368
369     g_assert(m);
370     g_assert(callback);
371
372     info = m->interface_callbacks;
373     prev = NULL;
374     
375     while (info) {
376         if (info->callback == callback && info->userdata == userdata) {
377             interface_callback_info *c = info;
378             
379             if (prev)
380                 prev->next = c->next;
381             else
382                 m->interface_callbacks = c->next;
383             
384             info = c->next;
385             g_free(c);
386         } else {
387             prev = info;
388             info = info->next;
389         }
390     }
391 }
392
393 void flx_interface_monitor_add_address_callback(
394     flxInterfaceMonitor *m,
395     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
396     gpointer userdata) {
397
398     address_callback_info *info;
399     
400     g_assert(m);
401     g_assert(callback);
402
403     info = g_new(address_callback_info, 1);
404     info->callback = callback;
405     info->userdata = userdata;
406     info->next = m->address_callbacks;
407     m->address_callbacks = info;
408 }
409
410
411 void flx_interface_monitor_remove_address_callback(
412     flxInterfaceMonitor *m,
413     void (*callback)(flxInterfaceMonitor *m, flxInterfaceChange change, const flxInterfaceAddress *a, gpointer userdata),
414     gpointer userdata) {
415
416     address_callback_info *info, *prev;
417
418     g_assert(m);
419     g_assert(callback);
420
421     info = m->address_callbacks;
422     prev = NULL;
423     
424     while (info) {
425         if (info->callback == callback && info->userdata == userdata) {
426             address_callback_info *c = info;
427             
428             if (prev)
429                 prev->next = c->next;
430             else
431                 m->address_callbacks = c->next;
432             
433             info = c->next;
434             g_free(c);
435         } else {
436             prev = info;
437             info = info->next;
438         }
439     }
440
441 }
442
443 const flxInterface* flx_interface_monitor_get_first(flxInterfaceMonitor *m) {
444     g_assert(m);
445     return m->interfaces;
446 }