]> git.meshlink.io Git - catta/blob - src/iface-windows.c
process change events in the order they are received
[catta] / src / iface-windows.c
1 /***
2   This file is part of catta.
3
4   catta is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   catta is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with catta; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #include "iface.h"
21 #include "iface-windows.h"
22
23 #include <stdlib.h> // wcstombs
24 #include <catta/malloc.h>
25 #include <catta/log.h>
26 #include <iphlpapi.h>
27 #include <assert.h>
28 #include "hashmap.h"
29 #include "util.h"   // catta_format_mac_address
30 #include "fdutil.h" // catta_set_nonblock
31
32
33 typedef enum {
34     INTERFACE_CHANGE_EVENT,
35     ADDRESS_CHANGE_EVENT
36 } ChangeEventType;
37
38 struct ChangeEvent {
39     CATTA_LLIST_FIELDS(ChangeEvent, event);
40     ChangeEventType type;
41     MIB_NOTIFICATION_TYPE notification_type;
42     union {
43         MIB_IPINTERFACE_ROW iface;
44         MIB_UNICASTIPADDRESS_ROW addr;
45     } data;
46 };
47
48
49 // helper: determine the global_scope flag for an address
50 static void set_global_scope_flag(CattaInterfaceAddress *ifaddr, const CattaAddress *addr)
51 {
52     if(addr->proto == CATTA_PROTO_INET6) {
53         const struct in6_addr *ia = (struct in6_addr *)addr->data.ipv6.address;
54         ifaddr->global_scope = !(IN6_IS_ADDR_LINKLOCAL(ia) || IN6_IS_ADDR_MULTICAST(ia));
55     } else {
56         ifaddr->global_scope = 1;
57     }
58 }
59
60 // integrate the information from an IP_ADAPTER_UNICAST_ADDRESS structure for
61 // given CattaHwInterface into the CattaInterfaceMonitor
62 static void ip_adapter_unicast_address(CattaInterfaceMonitor *m,
63                                        CattaHwInterface *hw,
64                                        IP_ADAPTER_UNICAST_ADDRESS *a)
65 {
66     CattaInterface *iface;
67     CattaAddress addr;
68     CattaInterfaceAddress *ifaddr;
69     struct sockaddr *sa = a->Address.lpSockaddr;
70
71     // skip transient addresses; to quote MSDN: "The IP address is a cluster
72     // address and should not be used by most applications."
73     // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
74     if(a->Flags & IP_ADAPTER_ADDRESS_TRANSIENT)
75         return;
76
77     // fill addr struct for address lookup
78     switch(sa->sa_family) {
79     case AF_INET:
80         memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
81         break;
82     case AF_INET6:
83         memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
84         break;
85     default:
86         catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
87         return;
88     }
89     addr.proto = catta_af_to_proto(sa->sa_family);
90
91     // get protocol-specific CattaInterface object
92     if(!(iface = catta_interface_monitor_get_interface(m, hw->index, addr.proto))) {
93         catta_log_error("CattaInterface (index %d, proto %d) not found", hw->index, addr.proto);
94         return;
95     }
96
97     // find or allocate a CattaInterfaceAddress struct for this address
98     if(!(ifaddr = catta_interface_monitor_get_address(m, iface, &addr))) {
99         if(!(ifaddr = catta_interface_address_new(m, iface, &addr, a->OnLinkPrefixLength))) {
100             catta_log_error("out of memory in ip_adapter_unicast_address");
101             return;
102         }
103     }
104
105     set_global_scope_flag(ifaddr, &addr);
106
107     // XXX debugging, remove
108     {
109         char s[CATTA_ADDRESS_STR_MAX];
110         catta_log_debug(" address: %s\n"
111                         "   global_scope: %d\n"
112                         "   flags: 0x%.4x",
113             catta_address_snprint(s, sizeof(s), &addr),
114             ifaddr->global_scope,
115             (unsigned int)a->Flags);
116     }
117 }
118
119 // integrate the information from an IP_ADAPTER_ADDRESSES structure
120 // as returned by GetAdaptersAddresses into the CattaInterfaceMonitor
121 static void ip_adapter(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
122 {
123     IP_ADAPTER_UNICAST_ADDRESS *a;
124     CattaIfIndex idx;
125     CattaHwInterface *hw;
126     size_t n;
127
128     // we want an index specific to the hardware interface, but Windows
129     // has one for IPv4 and one for IPv6. it seems like these are always the
130     // same unless one of the protocols is not available. let's have a bunch of
131     // checks...
132     if(!p->IfIndex && !p->Ipv6IfIndex) {
133         return; // no usable protocols
134     } else if(!p->IfIndex) {
135         idx = p->Ipv6IfIndex;   // IPv6 but no IPv4 (huh!)
136     } else if(!p->Ipv6IfIndex) {
137         idx = p->IfIndex;       // IPv4 but no IPv6
138     } else if(p->IfIndex == p->Ipv6IfIndex) {
139         idx = p->IfIndex;       // same index for both protocols
140     } else {
141         // both indexes valid but not equal
142         catta_log_error("unsupported interface: %ls (IfIndex and Ipv6IfIndex differ: %u/%u)",
143             p->FriendlyName, (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex);
144         return;
145     }
146
147     // find the CattaHwInterface by index or allocate a new one
148     if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
149         if((hw = catta_hw_interface_new(m, idx)) == NULL) {
150             catta_log_error("catta_hw_interface_new failed in ip_adapter_address");
151             return;
152         }
153     }
154
155     // fill the CattaHwInterface struct with data
156     // notice: this code is essentially duplicated in update_hw_interface()
157     hw->flags_ok =
158         (p->OperStatus == IfOperStatusUp) &&
159         !(p->IfType == IF_TYPE_SOFTWARE_LOOPBACK) &&
160         !(p->Flags & IP_ADAPTER_NO_MULTICAST) &&
161         (m->server->config.allow_point_to_point || !(p->IfType == IF_TYPE_PPP));
162             // XXX what about IF_TYPE_TUNNEL?
163
164     n = wcstombs(NULL, p->FriendlyName, 0) + 1;
165     catta_free(hw->name);
166     hw->name = catta_new(char, n);
167     wcstombs(hw->name, p->FriendlyName, n);
168
169     hw->mtu = p->Mtu;
170
171     hw->mac_address_size = p->PhysicalAddressLength;
172     if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
173         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
174     memcpy(hw->mac_address, p->PhysicalAddress, hw->mac_address_size);
175
176     // XXX debugging, remove
177     {
178         char mac[256];
179         catta_log_debug(" name: %s\n"
180                         " index: %d\n"
181                         "   IfIndex: %u\n"
182                         "   Ipv6IfIndex: %u\n"
183                         " mtu: %d\n"
184                         " mac: %s\n"
185                         " flags_ok: %d\n"
186                         "   type: %u\n"
187                         "   status: %u\n"
188                         "   multicast: %d\n"
189                         "   flags: 0x%.4x",
190             hw->name, hw->index,
191             (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex,
192             hw->mtu,
193             catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
194             hw->flags_ok,
195             (unsigned int)p->IfType,
196             (unsigned int)p->OperStatus,
197             !(p->Flags & IP_ADAPTER_NO_MULTICAST),
198             (unsigned int)p->Flags);
199     }
200
201     // process addresses
202     // XXX remove addresses that are no longer in the list
203     for(a=p->FirstUnicastAddress; a; a=a->Next)
204         ip_adapter_unicast_address(m, hw, a);
205     catta_log_debug("=====");
206 }
207
208
209 // place the event into the queue to be handled (by the main thread)
210 // and wake the event handler if necessary
211 static void queue_event(CattaInterfaceMonitor *m, ChangeEvent *ev)
212 {
213     char c = 'X';
214
215     if(!ev)
216         return;
217
218     if(!pthread_mutex_lock(&m->osdep.mutex)) {
219         // queue the event
220         CATTA_LLIST_APPEND(ChangeEvent, event, m->osdep.events, ev);
221
222         // wake the handler
223         writepipe(m->osdep.pipefd[1], &c, sizeof(c));
224
225         pthread_mutex_unlock(&m->osdep.mutex);
226     } else {
227         catta_log_debug(__FILE__": queue_event: could not lock mutex");
228         catta_free(ev);
229     }
230 }
231
232 // copy the given data row into an appropriate change event struct
233 static ChangeEvent *new_event(ChangeEventType type, MIB_NOTIFICATION_TYPE ntype, void *row, size_t n)
234 {
235     ChangeEvent *ev;
236
237     if(!row)
238         return NULL;
239
240     if(!(ev = catta_new(ChangeEvent, 1)))
241         return NULL;
242
243     ev->type = type;
244     ev->notification_type = ntype;
245     memcpy(&ev->data, row, n);
246
247     return ev;
248 }
249
250 static void WINAPI icn_callback(void *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
251 {
252     queue_event(m, new_event(INTERFACE_CHANGE_EVENT, type, row, sizeof(*row)));
253 }
254
255 static void WINAPI acn_callback(void *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
256 {
257     queue_event(m, new_event(ADDRESS_CHANGE_EVENT, type, row, sizeof(*row)));
258 }
259
260 static void update_hw_interface(CattaHwInterface *hw)
261 {
262     MIB_IF_ROW2 row;
263     DWORD r;
264     size_t n;
265     int multicast;  // synthetic flag
266
267     row.InterfaceLuid.Value = 0;
268     row.InterfaceIndex = hw->index;
269     if((r = GetIfEntry2(&row)) != NO_ERROR) {
270         catta_log_error("GetIfEntry2 failed for iface %d (error %u)", hw->index, (unsigned int)r);
271         return;
272     }
273
274     // fill the CattaHwInterface struct with data
275     // notice: this code is essentially duplicated from ip_adapter()
276     // notice: not sure where to find the IP_ADAPTER_NO_MULTICAST flag from an
277     //         MIB_IF_ROW2 struct, so try to deduce it otherwise
278     //         cf. http://msdn.microsoft.com/en-us/windows/desktop/ff568739(v=vs.100).aspx
279     multicast = row.AccessType == NET_IF_ACCESS_BROADCAST ||
280                 row.AccessType == NET_IF_ACCESS_POINT_TO_POINT;
281     hw->flags_ok =
282         (row.OperStatus == IfOperStatusUp) &&
283         !(row.Type == IF_TYPE_SOFTWARE_LOOPBACK) &&
284         multicast &&
285         (hw->monitor->server->config.allow_point_to_point || !(row.Type == IF_TYPE_PPP));
286             // XXX what about IF_TYPE_TUNNEL?
287
288     n = wcstombs(NULL, row.Alias, 0) + 1;
289     catta_free(hw->name);
290     hw->name = catta_new(char, n);
291     wcstombs(hw->name, row.Alias, n);
292
293     hw->mtu = row.Mtu;
294
295     hw->mac_address_size = row.PhysicalAddressLength;
296     if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
297         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
298     memcpy(hw->mac_address, row.PhysicalAddress, hw->mac_address_size);
299
300     // XXX debugging, remove
301     {
302         char mac[256];
303         catta_log_debug(" name: %s\n"
304                         " mtu: %d\n"
305                         " mac: %s\n"
306                         " flags_ok: %d\n"
307                         "   type: %u\n"
308                         "   status: %u\n"
309                         "   multicast: %d\n"
310                         "   access type: %d",
311             hw->name,
312             hw->mtu,
313             catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
314             hw->flags_ok,
315             (unsigned int)row.Type,
316             (unsigned int)row.OperStatus,
317             multicast,
318             (int)row.AccessType);
319     }
320
321     catta_hw_interface_check_relevant(hw);
322     catta_hw_interface_update_rrs(hw, 0);
323 }
324
325 static void handle_iface_event(CattaInterfaceMonitor *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
326 {
327     CattaIfIndex idx = row->InterfaceIndex;
328     CattaProtocol proto = catta_af_to_proto(row->Family);
329     const char *protostr = catta_proto_to_string(proto);
330     CattaInterface *iface;
331     CattaHwInterface *hw;
332
333     // XXX debug, remove
334     {
335         const char *typestr = NULL;
336
337         switch(type) {
338             case MibParameterNotification:  typestr = "ParameterNotification"; break;
339             case MibAddInstance:            typestr = "AddInstance"; break;
340             case MibDeleteInstance:         typestr = "DeleteInstance"; break;
341             default:                        typestr = "Unknown";
342         }
343
344         catta_log_debug("interface %s on iface %d for %s", typestr, idx, protostr);
345     }
346
347     // see if we know this interface
348     iface = catta_interface_monitor_get_interface(m, idx, proto);
349     hw = iface ? iface->hardware : catta_interface_monitor_get_hw_interface(m, idx);
350
351     // print debug messages for some unexpected cases
352     if(type==MibParameterNotification && !iface)
353         catta_log_debug("ParameterNotification received for unknown interface %d (%s)", idx, protostr);
354     if(type==MibDeleteInstance && !iface)
355         catta_log_debug("DeleteInstance received for unknown interface %d (%s)", idx, protostr);
356     if(type==MibAddInstance && iface)
357         catta_log_debug("AddInstance received for existing interface %d (%s)", idx, protostr);
358     if(iface && !hw)
359         catta_log_debug("missing CattaHwInterface for interface %d (%s)", idx, protostr);
360
361     switch(type) {
362     case MibParameterNotification:
363     case MibAddInstance:
364         // create the physical interface if it is missing
365         if(!hw) {
366             if((hw = catta_hw_interface_new(m, idx)) == NULL) {
367                 catta_log_error("catta_hw_interface_new failed in handle_iface_event");
368                 return;
369             }
370         }
371
372         // create the protocol-specific interface if it is missing
373         if(!iface) {
374             if((iface = catta_interface_new(m, hw, proto)) == NULL) {
375                 catta_log_error("catta_interface_new failed in handle_iface_event");
376                 return;
377             }
378         }
379
380         assert(iface != NULL);
381         assert(hw != NULL);
382         assert(iface->hardware == hw);
383
384         update_hw_interface(hw);
385         break;
386     case MibDeleteInstance:
387         if(iface)
388             catta_interface_free(iface, 0);
389
390         // free the hardware interface when there are no more protocol-specific interfaces
391         if(hw && !hw->interfaces)
392             catta_hw_interface_free(hw, 0);
393         break;
394     default:
395         catta_log_debug("unexpected type (%d) of interface change notification received", type);
396     }
397 }
398
399 static void handle_addr_event(CattaInterfaceMonitor *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
400 {
401     CattaIfIndex idx = row->InterfaceIndex;
402     CattaInterfaceAddress *ifaddr;
403     CattaInterface *iface;
404     CattaAddress addr;
405     const char *protostr;
406
407     // fill addr struct for address lookup
408     switch(row->Address.si_family) {
409     case AF_INET:
410         memcpy(addr.data.data, &row->Address.Ipv4.sin_addr, sizeof(struct in_addr));
411         break;
412     case AF_INET6:
413         memcpy(addr.data.data, &row->Address.Ipv6.sin6_addr, sizeof(struct in6_addr));
414         break;
415     default:
416         catta_log_debug("unexpected address family on interface %d: %u", idx, row->Address.si_family);
417         return;
418     }
419     addr.proto = catta_af_to_proto(row->Address.si_family);
420     protostr = catta_proto_to_string(addr.proto);
421
422     // XXX debug, remove
423     {
424         const char *typestr = NULL;
425         char buf[CATTA_ADDRESS_STR_MAX];
426
427         switch(type) {
428             case MibParameterNotification:  typestr = "ParameterNotification"; break;
429             case MibAddInstance:            typestr = "AddInstance"; break;
430             case MibDeleteInstance:         typestr = "DeleteInstance"; break;
431             default:                        typestr = "Unknown";
432         }
433
434         catta_log_debug("%s for %s address %s on iface %d",
435                         typestr, protostr,
436                         catta_address_snprint(buf, sizeof(buf), &addr),
437                         idx);
438     }
439
440     // see if we know this address/interface
441     iface = catta_interface_monitor_get_interface(m, idx, addr.proto);
442     ifaddr = iface ? catta_interface_monitor_get_address(m, iface, &addr) : NULL;
443
444     // print debug messages for some unexpected cases
445     if(type==MibParameterNotification && !ifaddr)
446         catta_log_debug("ParameterNotification received for unknown address on interface %d (%s)", idx, protostr);
447     if(type==MibDeleteInstance && !ifaddr)
448         catta_log_debug("DeleteInstance received for unknown address on interface %d (%s)", idx, protostr);
449     if(type==MibAddInstance && ifaddr)
450         catta_log_debug("AddInstance received for existing address on interface %d (%s)", idx, protostr);
451     if(ifaddr && !iface)
452         catta_log_debug("missing CattaInterface for address on interface %d (%s)", idx, protostr);
453
454     switch(type) {
455     case MibParameterNotification:
456     case MibAddInstance:
457         // fetch the full event data
458         if(GetUnicastIpAddressEntry(row) != NO_ERROR) {
459             catta_log_error("GetUnicastIpAddressEntry failed in handle_addr_event");
460             return;
461         }
462
463         // skip addresses that are not suitable as source addresses
464         if(row->SkipAsSource)
465             return;
466
467         // create the interface if it is missing
468         if(!iface) {
469             CattaHwInterface *hw;
470
471             if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
472                 catta_log_error("interface %d not found in handle_addr_event", idx);
473                 return;
474             }
475
476             if((iface = catta_interface_new(m, hw, addr.proto)) == NULL) {
477                 catta_log_error("catta_interface_new failed in handle_addr_event");
478                 return;
479             }
480         }
481         assert(iface != NULL);
482
483         // create the interface-associated address if it is missing
484         if(!ifaddr) {
485             unsigned prefixlen = row->OnLinkPrefixLength;
486
487             if((ifaddr = catta_interface_address_new(m, iface, &addr, prefixlen)) == NULL) {
488                 catta_log_error("catta_interface_address_new failed in handle_addr_event");
489                 return;
490             }
491         }
492         assert(ifaddr != NULL);
493
494         set_global_scope_flag(ifaddr, &addr);
495         catta_log_debug("   global_scope: %d", ifaddr->global_scope); // XXX debugging, remove
496         break;
497     case MibDeleteInstance:
498         if(ifaddr)
499             catta_interface_address_free(ifaddr);
500         break;
501     default:
502         catta_log_debug("unexpected type (%d) of address change notification received", type);
503     }
504
505     if(iface) {
506         catta_interface_check_relevant(iface);
507         catta_interface_update_rrs(iface, 0);
508     }
509 }
510
511 static void handle_events(CattaInterfaceMonitor *m)
512 {
513     char buf[16];
514     ChangeEvent *ev;
515
516     if(!pthread_mutex_lock(&m->osdep.mutex)) {
517         // clear the pipe
518         while(readpipe(m->osdep.pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {}
519
520         while((ev = m->osdep.events) != NULL) {
521             CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
522
523             // dispatch to the appropriate handler
524             switch(ev->type) {
525             case INTERFACE_CHANGE_EVENT:
526                 handle_iface_event(m, &ev->data.iface, ev->notification_type);
527                 break;
528             case ADDRESS_CHANGE_EVENT:
529                 handle_addr_event(m, &ev->data.addr, ev->notification_type);
530                 break;
531             default:
532                 catta_log_debug("unhandled change event type in handle_events");
533             }
534
535             catta_free(ev);
536         }
537
538         pthread_mutex_unlock(&m->osdep.mutex);
539     }
540 }
541
542 static void pipe_callback(CattaWatch *w, int fd, CattaWatchEvent event, void *m)
543 {
544     // silence "unused parameter" warnings
545     (void)w;
546     (void)fd;
547     (void)event;
548
549     handle_events(m);
550 }
551
552
553 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
554 {
555     DWORD r;
556
557     pthread_mutex_init(&m->osdep.mutex, NULL);
558
559     CATTA_LLIST_HEAD_INIT(ChangeEvent, m->osdep.events);
560
561     if(pipe(m->osdep.pipefd) < 0) {
562         catta_log_error("pipe() in catta_interface_monitor_init_osdep failed");
563         return -1;
564     }
565     catta_set_nonblock(m->osdep.pipefd[0]);
566     catta_set_nonblock(m->osdep.pipefd[1]);
567
568     m->osdep.icnhandle = NULL;
569     m->osdep.acnhandle = NULL;
570
571     // register handler for change events
572     m->osdep.watch = m->server->poll_api->watch_new(m->server->poll_api,
573                                                     m->osdep.pipefd[0],
574                                                     CATTA_WATCH_IN,
575                                                     pipe_callback,
576                                                     m);
577     if(!m->osdep.watch) {
578         catta_log_error(__FILE__": Failed to create watch.");
579         return -1;
580     }
581
582     // request async notification on interface changes
583     r = NotifyIpInterfaceChange(AF_UNSPEC,
584                                 // icn_callback needs to be WINAPI but
585                                 // MingW up to 3.1.0 erroneously defines
586                                 // PIPINTERFACE_CHANGE_CALLBACK without it
587                                 (PIPINTERFACE_CHANGE_CALLBACK)icn_callback,
588                                 m, FALSE, &m->osdep.icnhandle);
589     if(r != NO_ERROR)
590         catta_log_error("NotifyIpInterfaceChange failed: %u", (unsigned int)r);
591
592     // request async notification on address changes
593     r = NotifyUnicastIpAddressChange(AF_UNSPEC, acn_callback, m, FALSE,
594                                      &m->osdep.acnhandle);
595     if(r != NO_ERROR)
596         catta_log_error("NotifyUnicastIpAddressChange failed: %u", (unsigned int)r);
597
598     return 0;
599 }
600
601 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
602 {
603     ChangeEvent *ev;
604
605     // unregister callbacks
606     if(m->osdep.icnhandle) CancelMibChangeNotify2(m->osdep.icnhandle);
607     if(m->osdep.acnhandle) CancelMibChangeNotify2(m->osdep.acnhandle);
608
609     // unregister event handler
610     m->server->poll_api->watch_free(m->osdep.watch);
611
612     // close pipe
613     closepipe(m->osdep.pipefd[0]);
614     closepipe(m->osdep.pipefd[1]);
615
616     // make sure no stray events can come in during destruction
617     pthread_mutex_lock(&m->osdep.mutex);
618
619     // free all events that are still in the queue
620     while((ev = m->osdep.events) != NULL) {
621         CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
622         catta_free(ev);
623     }
624
625     pthread_mutex_unlock(&m->osdep.mutex);
626     pthread_mutex_destroy(&m->osdep.mutex);
627 }
628
629 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
630 {
631     IP_ADAPTER_ADDRESSES *buf = NULL;
632     IP_ADAPTER_ADDRESSES *p;
633     ULONG bufsize = 15000;
634     ULONG r;
635
636     // allocate a buffer and call GetAdaptersAddresses
637     // retry with the correct size if the buffer was too small
638     do {
639         catta_free(buf);    // no-op on first iteration
640         if((buf = catta_malloc(bufsize)) == NULL) {
641             catta_log_error("malloc failed in catta_interface_monitor_sync");
642             return;
643         }
644
645         r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
646     } while(r == ERROR_BUFFER_OVERFLOW);
647
648     if(r != NO_ERROR) {
649         catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
650         return;
651     }
652
653     // XXX remove interfaces for adapters that are no longer in the list
654
655     // create 'CattaInterface's for every adapter
656     for(p=buf; p; p=p->Next)
657         ip_adapter(m, p);
658
659     catta_free(buf);
660
661     m->list_complete = 1;
662     catta_interface_monitor_check_relevant(m);
663     catta_interface_monitor_update_rrs(m, 0);
664     catta_log_info("Network interface enumeration completed.");
665 }