]> git.meshlink.io Git - catta/blob - iface.c
4bcd2d9de3fb228138116d36c16cd3114ea855d1
[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 #include <net/if.h>
8
9 #include "iface.h"
10 #include "netlink.h"
11
12 static void update_address_rr(flxInterfaceMonitor *m, flxInterfaceAddress *a, int remove) {
13     g_assert(m);
14     g_assert(a);
15
16     if (!flx_address_is_relevant(a) || remove) {
17         if (a->rr_id >= 0) {
18             flx_server_remove(m->server, a->rr_id);
19             a->rr_id = -1;
20         }
21     } else {
22         if (a->rr_id < 0) {
23             a->rr_id = flx_server_get_next_id(m->server);
24             flx_server_add_address(m->server, a->rr_id, a->interface->index, AF_UNSPEC, m->server->hostname, &a->address);
25         }
26     }
27 }
28
29 static void update_interface_rr(flxInterfaceMonitor *m, flxInterface *i, int remove) {
30     flxInterfaceAddress *a;
31     g_assert(m);
32     g_assert(i);
33
34     for (a = i->addresses; a; a = a->next)
35         update_address_rr(m, a, remove);
36 }
37
38 static void free_address(flxInterfaceMonitor *m, flxInterfaceAddress *a) {
39     g_assert(m);
40     g_assert(a);
41     g_assert(a->interface);
42
43     if (a->address.family == AF_INET)
44         a->interface->n_ipv4_addrs --;
45     else if (a->address.family == AF_INET6)
46         a->interface->n_ipv6_addrs --;
47  
48     if (a->prev)
49         a->prev->next = a->next;
50     else
51         a->interface->addresses = a->next;
52
53     if (a->next)
54         a->next->prev = a->prev;
55
56     g_free(a);
57 }
58
59 static void free_interface(flxInterfaceMonitor *m, flxInterface *i) {
60     g_assert(m);
61     g_assert(i);
62
63     while (i->addresses)
64         free_address(m, i->addresses);
65
66     g_assert(i->n_ipv6_addrs == 0);
67     g_assert(i->n_ipv4_addrs == 0);
68
69     if (i->prev)
70         i->prev->next = i->next;
71     else
72         m->interfaces = i->next;
73
74     if (i->next)
75         i->next->prev = i->prev;
76
77     g_hash_table_remove(m->hash_table, &i->index);
78     
79     g_free(i->name);
80     g_free(i);
81 }
82
83 static flxInterfaceAddress* get_address(flxInterfaceMonitor *m, flxInterface *i, const flxAddress *raddr) {
84     flxInterfaceAddress *ia;
85     
86     g_assert(m);
87     g_assert(i);
88     g_assert(raddr);
89
90     for (ia = i->addresses; ia; ia = ia->next)
91         if (flx_address_cmp(&ia->address, raddr) == 0)
92             return ia;
93
94     return NULL;
95 }
96
97 static int netlink_list_items(flxNetlink *nl, guint16 type, guint *ret_seq) {
98     struct nlmsghdr *n;
99     struct rtgenmsg *gen;
100     guint8 req[1024];
101     
102     memset(&req, 0, sizeof(req));
103     n = (struct nlmsghdr*) req;
104     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
105     n->nlmsg_type = type;
106     n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
107     n->nlmsg_pid = 0;
108
109     gen = NLMSG_DATA(n);
110     memset(gen, 0, sizeof(struct rtgenmsg));
111     gen->rtgen_family = AF_UNSPEC;
112
113     return flx_netlink_send(nl, n, ret_seq);
114 }
115
116 static void callback(flxNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
117     flxInterfaceMonitor *m = userdata;
118     
119     g_assert(m);
120     g_assert(n);
121     g_assert(m->netlink == nl);
122
123     if (n->nlmsg_type == RTM_NEWLINK) {
124         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
125         flxInterface *i;
126         struct rtattr *a = NULL;
127         size_t l;
128         int changed;
129
130         if (ifinfomsg->ifi_family != AF_UNSPEC)
131             return;
132
133         if ((i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
134             changed = 1;
135         else {
136             i = g_new(flxInterface, 1);
137             i->name = NULL;
138             i->index = ifinfomsg->ifi_index;
139             i->addresses = NULL;
140             i->n_ipv4_addrs = i->n_ipv6_addrs = 0;
141             if ((i->next = m->interfaces))
142                 i->next->prev = i;
143             m->interfaces = i;
144             i->prev = NULL;
145             g_hash_table_insert(m->hash_table, &i->index, i);
146             changed = 0;
147         }
148         
149         i->flags = ifinfomsg->ifi_flags;
150
151         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
152         a = IFLA_RTA(ifinfomsg);
153
154         while (RTA_OK(a, l)) {
155             switch(a->rta_type) {
156                 case IFLA_IFNAME:
157                     g_free(i->name);
158                     i->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
159                     break;
160                     
161                 default:
162                     ;
163             }
164
165             a = RTA_NEXT(a, l);
166         }
167
168         update_interface_rr(m, i, 0);
169     } else if (n->nlmsg_type == RTM_DELLINK) {
170         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
171         flxInterface *i;
172
173         if (ifinfomsg->ifi_family != AF_UNSPEC)
174             return;
175         
176         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
177             return;
178
179         update_interface_rr(m, i, 1);
180         free_interface(m, i);
181         
182     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
183
184         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
185         flxInterface *i;
186         struct rtattr *a = NULL;
187         size_t l;
188         int changed;
189         flxAddress raddr;
190         int raddr_valid = 0;
191
192         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
193             return;
194
195         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifaddrmsg->ifa_index)))
196             return;
197
198         raddr.family = ifaddrmsg->ifa_family;
199
200         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
201         a = IFA_RTA(ifaddrmsg);
202
203         while (RTA_OK(a, l)) {
204             switch(a->rta_type) {
205                 case IFA_ADDRESS:
206                     if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
207                         (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
208                         return;
209
210                     memcpy(raddr.data, RTA_DATA(a), RTA_PAYLOAD(a));
211                     raddr_valid = 1;
212
213                     break;
214                     
215                 default:
216                     ;
217             }
218
219             a = RTA_NEXT(a, l);
220         }
221
222
223         if (!raddr_valid)
224             return;
225
226         if (n->nlmsg_type == RTM_NEWADDR) {
227             flxInterfaceAddress *addr;
228             
229             if ((addr = get_address(m, i, &raddr)))
230                 changed = 1;
231             else {
232                 addr = g_new(flxInterfaceAddress, 1);
233                 addr->address = raddr;
234
235                 if (raddr.family == AF_INET)
236                     i->n_ipv4_addrs++;
237                 else if (raddr.family == AF_INET6)
238                     i->n_ipv6_addrs++;
239                 
240                 addr->interface = i;
241                 if ((addr->next = i->addresses))
242                     addr->next->prev = addr;
243                 i->addresses = addr;
244                 addr->prev = NULL;
245                 addr->rr_id = -1;
246                 
247                 changed = 0;
248             }
249             
250             addr->flags = ifaddrmsg->ifa_flags;
251             addr->scope = ifaddrmsg->ifa_scope;
252
253             update_address_rr(m, addr, 0);
254         } else {
255             flxInterfaceAddress *addr;
256             
257             if (!(addr = get_address(m, i, &raddr)))
258                 return;
259
260             update_address_rr(m, addr, 1);
261             free_address(m, addr);
262         }
263                 
264     } else if (n->nlmsg_type == NLMSG_DONE) {
265
266         if (m->list == LIST_IFACE) {
267             m->list = LIST_DONE;
268             
269             if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) {
270                 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
271             } else
272                 m->list = LIST_ADDR;
273         } else
274             m->list = LIST_DONE;
275         
276     } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
277         struct nlmsgerr *e = NLMSG_DATA (n);
278                     
279         if (e->error)
280             g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
281     }
282 }
283
284 flxInterfaceMonitor *flx_interface_monitor_new(flxServer *s) {
285     flxInterfaceMonitor *m = NULL;
286
287     m = g_new0(flxInterfaceMonitor, 1);
288     m->server = s;
289     if (!(m->netlink = flx_netlink_new(s->context, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
290         goto fail;
291
292     m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
293     m->interfaces = NULL;
294
295     if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
296         goto fail;
297
298     m->list = LIST_IFACE;
299     
300     return m;
301
302 fail:
303     flx_interface_monitor_free(m);
304     return NULL;
305 }
306
307 void flx_interface_monitor_free(flxInterfaceMonitor *m) {
308     g_assert(m);
309
310     if (m->netlink)
311         flx_netlink_free(m->netlink);
312
313     if (m->hash_table)
314         g_hash_table_destroy(m->hash_table);
315
316     g_free(m);
317 }
318
319
320 const flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index) {
321     g_assert(m);
322     g_assert(index > 0);
323
324     return g_hash_table_lookup(m->hash_table, &index);
325 }
326
327 const flxInterface* flx_interface_monitor_get_first(flxInterfaceMonitor *m) {
328     g_assert(m);
329     return m->interfaces;
330 }
331
332 int flx_interface_is_relevant(flxInterface *i) {
333     g_assert(i);
334
335     return
336         (i->flags & IFF_UP) &&
337         (i->flags & IFF_RUNNING) &&
338         !(i->flags & IFF_LOOPBACK);
339 }
340
341 int flx_address_is_relevant(flxInterfaceAddress *a) {
342     g_assert(a);
343
344     return
345         a->scope == RT_SCOPE_UNIVERSE &&
346         flx_interface_is_relevant(a->interface);
347 }