]> git.meshlink.io Git - catta/blob - src/iface-windows.c
handle address change events
[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         // XXX event ordering!!
221         CATTA_LLIST_PREPEND(ChangeEvent, event, m->osdep.events, ev);
222
223         // wake the handler
224         writepipe(m->osdep.pipefd[1], &c, sizeof(c));
225
226         pthread_mutex_unlock(&m->osdep.mutex);
227     } else {
228         catta_log_debug(__FILE__": queue_event: could not lock mutex");
229         catta_free(ev);
230     }
231 }
232
233 // copy the given data row into an appropriate change event struct
234 static ChangeEvent *new_event(ChangeEventType type, MIB_NOTIFICATION_TYPE ntype, void *row, size_t n)
235 {
236     ChangeEvent *ev;
237
238     if(!row)
239         return NULL;
240
241     if(!(ev = catta_new(ChangeEvent, 1)))
242         return NULL;
243
244     ev->type = type;
245     ev->notification_type = ntype;
246     memcpy(&ev->data, row, n);
247
248     return ev;
249 }
250
251 static void WINAPI icn_callback(void *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
252 {
253     queue_event(m, new_event(INTERFACE_CHANGE_EVENT, type, row, sizeof(*row)));
254 }
255
256 static void WINAPI acn_callback(void *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
257 {
258     queue_event(m, new_event(ADDRESS_CHANGE_EVENT, type, row, sizeof(*row)));
259 }
260
261 static void update_hw_interface(CattaHwInterface *hw)
262 {
263     MIB_IF_ROW2 row;
264     DWORD r;
265     size_t n;
266     int multicast;  // synthetic flag
267
268     row.InterfaceLuid.Value = 0;
269     row.InterfaceIndex = hw->index;
270     if((r = GetIfEntry2(&row)) != NO_ERROR) {
271         catta_log_error("GetIfEntry2 failed for iface %d (error %u)", hw->index, (unsigned int)r);
272         return;
273     }
274
275     // fill the CattaHwInterface struct with data
276     // notice: this code is essentially duplicated from ip_adapter()
277     // notice: not sure where to find the IP_ADAPTER_NO_MULTICAST flag from an
278     //         MIB_IF_ROW2 struct, so try to deduce it otherwise
279     //         cf. http://msdn.microsoft.com/en-us/windows/desktop/ff568739(v=vs.100).aspx
280     multicast = row.AccessType == NET_IF_ACCESS_BROADCAST ||
281                 row.AccessType == NET_IF_ACCESS_POINT_TO_POINT;
282     hw->flags_ok =
283         (row.OperStatus == IfOperStatusUp) &&
284         !(row.Type == IF_TYPE_SOFTWARE_LOOPBACK) &&
285         multicast &&
286         (hw->monitor->server->config.allow_point_to_point || !(row.Type == IF_TYPE_PPP));
287             // XXX what about IF_TYPE_TUNNEL?
288
289     n = wcstombs(NULL, row.Alias, 0) + 1;
290     catta_free(hw->name);
291     hw->name = catta_new(char, n);
292     wcstombs(hw->name, row.Alias, n);
293
294     hw->mtu = row.Mtu;
295
296     hw->mac_address_size = row.PhysicalAddressLength;
297     if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
298         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
299     memcpy(hw->mac_address, row.PhysicalAddress, hw->mac_address_size);
300
301     // XXX debugging, remove
302     {
303         char mac[256];
304         catta_log_debug(" name: %s\n"
305                         " mtu: %d\n"
306                         " mac: %s\n"
307                         " flags_ok: %d\n"
308                         "   type: %u\n"
309                         "   status: %u\n"
310                         "   multicast: %d\n"
311                         "   access type: %d",
312             hw->name,
313             hw->mtu,
314             catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
315             hw->flags_ok,
316             (unsigned int)row.Type,
317             (unsigned int)row.OperStatus,
318             multicast,
319             (int)row.AccessType);
320     }
321
322     catta_hw_interface_check_relevant(hw);
323     catta_hw_interface_update_rrs(hw, 0);
324 }
325
326 static void handle_iface_event(CattaInterfaceMonitor *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
327 {
328     CattaIfIndex idx = row->InterfaceIndex;
329     CattaProtocol proto = catta_af_to_proto(row->Family);
330     const char *protostr = catta_proto_to_string(proto);
331     CattaInterface *iface;
332     CattaHwInterface *hw;
333
334     // XXX debug, remove
335     {
336         const char *typestr = NULL;
337
338         switch(type) {
339             case MibParameterNotification:  typestr = "ParameterNotification"; break;
340             case MibAddInstance:            typestr = "AddInstance"; break;
341             case MibDeleteInstance:         typestr = "DeleteInstance"; break;
342             default:                        typestr = "Unknown";
343         }
344
345         catta_log_debug("interface %s on iface %d for %s", typestr, idx, protostr);
346     }
347
348     // see if we know this interface
349     iface = catta_interface_monitor_get_interface(m, idx, proto);
350     hw = iface ? iface->hardware : catta_interface_monitor_get_hw_interface(m, idx);
351
352     // print debug messages for some unexpected cases
353     if(type==MibParameterNotification && !iface)
354         catta_log_debug("ParameterNotification received for unknown interface %d (%s)", idx, protostr);
355     if(type==MibDeleteInstance && !iface)
356         catta_log_debug("DeleteInstance received for unknown interface %d (%s)", idx, protostr);
357     if(type==MibAddInstance && iface)
358         catta_log_debug("AddInstance received for existing interface %d (%s)", idx, protostr);
359     if(iface && !hw)
360         catta_log_debug("missing CattaHwInterface for interface %d (%s)", idx, protostr);
361
362     switch(type) {
363     case MibParameterNotification:
364     case MibAddInstance:
365         // create the physical interface if it is missing
366         if(!hw) {
367             if((hw = catta_hw_interface_new(m, idx)) == NULL) {
368                 catta_log_error("catta_hw_interface_new failed in handle_iface_event");
369                 return;
370             }
371         }
372
373         // create the protocol-specific interface if it is missing
374         if(!iface) {
375             if((iface = catta_interface_new(m, hw, proto)) == NULL) {
376                 catta_log_error("catta_interface_new failed in handle_iface_event");
377                 return;
378             }
379         }
380
381         assert(iface != NULL);
382         assert(hw != NULL);
383         assert(iface->hardware == hw);
384
385         update_hw_interface(hw);
386         break;
387     case MibDeleteInstance:
388         if(iface)
389             catta_interface_free(iface, 0);
390
391         // free the hardware interface when there are no more protocol-specific interfaces
392         if(hw && !hw->interfaces)
393             catta_hw_interface_free(hw, 0);
394         break;
395     default:
396         catta_log_debug("unexpected type (%d) of interface change notification received", type);
397     }
398 }
399
400 static void handle_addr_event(CattaInterfaceMonitor *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
401 {
402     CattaIfIndex idx = row->InterfaceIndex;
403     CattaInterfaceAddress *ifaddr;
404     CattaInterface *iface;
405     CattaAddress addr;
406     const char *protostr;
407
408     // fill addr struct for address lookup
409     switch(row->Address.si_family) {
410     case AF_INET:
411         memcpy(addr.data.data, &row->Address.Ipv4.sin_addr, sizeof(struct in_addr));
412         break;
413     case AF_INET6:
414         memcpy(addr.data.data, &row->Address.Ipv6.sin6_addr, sizeof(struct in6_addr));
415         break;
416     default:
417         catta_log_debug("unexpected address family on interface %d: %u", idx, row->Address.si_family);
418         return;
419     }
420     addr.proto = catta_af_to_proto(row->Address.si_family);
421     protostr = catta_proto_to_string(addr.proto);
422
423     // XXX debug, remove
424     {
425         const char *typestr = NULL;
426         char buf[CATTA_ADDRESS_STR_MAX];
427
428         switch(type) {
429             case MibParameterNotification:  typestr = "ParameterNotification"; break;
430             case MibAddInstance:            typestr = "AddInstance"; break;
431             case MibDeleteInstance:         typestr = "DeleteInstance"; break;
432             default:                        typestr = "Unknown";
433         }
434
435         catta_log_debug("%s for %s address %s on iface %d",
436                         typestr, protostr,
437                         catta_address_snprint(buf, sizeof(buf), &addr),
438                         idx);
439     }
440
441     // see if we know this address/interface
442     iface = catta_interface_monitor_get_interface(m, idx, addr.proto);
443     ifaddr = iface ? catta_interface_monitor_get_address(m, iface, &addr) : NULL;
444
445     // print debug messages for some unexpected cases
446     if(type==MibParameterNotification && !ifaddr)
447         catta_log_debug("ParameterNotification received for unknown address on interface %d (%s)", idx, protostr);
448     if(type==MibDeleteInstance && !ifaddr)
449         catta_log_debug("DeleteInstance received for unknown address on interface %d (%s)", idx, protostr);
450     if(type==MibAddInstance && ifaddr)
451         catta_log_debug("AddInstance received for existing address on interface %d (%s)", idx, protostr);
452     if(ifaddr && !iface)
453         catta_log_debug("missing CattaInterface for address on interface %d (%s)", idx, protostr);
454
455     switch(type) {
456     case MibParameterNotification:
457     case MibAddInstance:
458         // fetch the full event data
459         if(GetUnicastIpAddressEntry(row) != NO_ERROR) {
460             catta_log_error("GetUnicastIpAddressEntry failed in handle_addr_event");
461             return;
462         }
463
464         // skip addresses that are not suitable as source addresses
465         if(row->SkipAsSource)
466             return;
467
468         // create the interface if it is missing
469         if(!iface) {
470             CattaHwInterface *hw;
471
472             if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
473                 catta_log_error("interface %d not found in handle_addr_event", idx);
474                 return;
475             }
476
477             if((iface = catta_interface_new(m, hw, addr.proto)) == NULL) {
478                 catta_log_error("catta_interface_new failed in handle_addr_event");
479                 return;
480             }
481         }
482         assert(iface != NULL);
483
484         // create the interface-associated address if it is missing
485         if(!ifaddr) {
486             unsigned prefixlen = row->OnLinkPrefixLength;
487
488             if((ifaddr = catta_interface_address_new(m, iface, &addr, prefixlen)) == NULL) {
489                 catta_log_error("catta_interface_address_new failed in handle_addr_event");
490                 return;
491             }
492         }
493         assert(ifaddr != NULL);
494
495         set_global_scope_flag(ifaddr, &addr);
496         catta_log_debug("   global_scope: %d", ifaddr->global_scope); // XXX debugging, remove
497         break;
498     case MibDeleteInstance:
499         if(ifaddr)
500             catta_interface_address_free(ifaddr);
501         break;
502     default:
503         catta_log_debug("unexpected type (%d) of address change notification received", type);
504     }
505
506     if(iface) {
507         catta_interface_check_relevant(iface);
508         catta_interface_update_rrs(iface, 0);
509     }
510 }
511
512 static void handle_events(CattaInterfaceMonitor *m)
513 {
514     char buf[16];
515     ChangeEvent *ev;
516
517     if(!pthread_mutex_lock(&m->osdep.mutex)) {
518         // clear the pipe
519         while(readpipe(m->osdep.pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {}
520
521         while((ev = m->osdep.events) != NULL) {
522             CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
523
524             // dispatch to the appropriate handler
525             switch(ev->type) {
526             case INTERFACE_CHANGE_EVENT:
527                 handle_iface_event(m, &ev->data.iface, ev->notification_type);
528                 break;
529             case ADDRESS_CHANGE_EVENT:
530                 handle_addr_event(m, &ev->data.addr, ev->notification_type);
531                 break;
532             default:
533                 catta_log_debug("unhandled change event type in handle_events");
534             }
535
536             catta_free(ev);
537         }
538
539         pthread_mutex_unlock(&m->osdep.mutex);
540     }
541 }
542
543 static void pipe_callback(CattaWatch *w, int fd, CattaWatchEvent event, void *m)
544 {
545     // silence "unused parameter" warnings
546     (void)w;
547     (void)fd;
548     (void)event;
549
550     handle_events(m);
551 }
552
553
554 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
555 {
556     DWORD r;
557
558     pthread_mutex_init(&m->osdep.mutex, NULL);
559
560     CATTA_LLIST_HEAD_INIT(ChangeEvent, m->osdep.events);
561
562     if(pipe(m->osdep.pipefd) < 0) {
563         catta_log_error("pipe() in catta_interface_monitor_init_osdep failed");
564         return -1;
565     }
566     catta_set_nonblock(m->osdep.pipefd[0]);
567     catta_set_nonblock(m->osdep.pipefd[1]);
568
569     m->osdep.icnhandle = NULL;
570     m->osdep.acnhandle = NULL;
571
572     // register handler for change events
573     m->osdep.watch = m->server->poll_api->watch_new(m->server->poll_api,
574                                                     m->osdep.pipefd[0],
575                                                     CATTA_WATCH_IN,
576                                                     pipe_callback,
577                                                     m);
578     if(!m->osdep.watch) {
579         catta_log_error(__FILE__": Failed to create watch.");
580         return -1;
581     }
582
583     // request async notification on interface changes
584     r = NotifyIpInterfaceChange(AF_UNSPEC,
585                                 // icn_callback needs to be WINAPI but
586                                 // MingW up to 3.1.0 erroneously defines
587                                 // PIPINTERFACE_CHANGE_CALLBACK without it
588                                 (PIPINTERFACE_CHANGE_CALLBACK)icn_callback,
589                                 m, FALSE, &m->osdep.icnhandle);
590     if(r != NO_ERROR)
591         catta_log_error("NotifyIpInterfaceChange failed: %u", (unsigned int)r);
592
593     // request async notification on address changes
594     r = NotifyUnicastIpAddressChange(AF_UNSPEC, acn_callback, m, FALSE,
595                                      &m->osdep.acnhandle);
596     if(r != NO_ERROR)
597         catta_log_error("NotifyUnicastIpAddressChange failed: %u", (unsigned int)r);
598
599     return 0;
600 }
601
602 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
603 {
604     ChangeEvent *ev;
605
606     // unregister callbacks
607     if(m->osdep.icnhandle) CancelMibChangeNotify2(m->osdep.icnhandle);
608     if(m->osdep.acnhandle) CancelMibChangeNotify2(m->osdep.acnhandle);
609
610     // unregister event handler
611     m->server->poll_api->watch_free(m->osdep.watch);
612
613     // close pipe
614     closepipe(m->osdep.pipefd[0]);
615     closepipe(m->osdep.pipefd[1]);
616
617     // make sure no stray events can come in during destruction
618     pthread_mutex_lock(&m->osdep.mutex);
619
620     // free all events that are still in the queue
621     while((ev = m->osdep.events) != NULL) {
622         CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
623         catta_free(ev);
624     }
625
626     pthread_mutex_unlock(&m->osdep.mutex);
627     pthread_mutex_destroy(&m->osdep.mutex);
628 }
629
630 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
631 {
632     IP_ADAPTER_ADDRESSES *buf = NULL;
633     IP_ADAPTER_ADDRESSES *p;
634     ULONG bufsize = 15000;
635     ULONG r;
636
637     // allocate a buffer and call GetAdaptersAddresses
638     // retry with the correct size if the buffer was too small
639     do {
640         catta_free(buf);    // no-op on first iteration
641         if((buf = catta_malloc(bufsize)) == NULL) {
642             catta_log_error("malloc failed in catta_interface_monitor_sync");
643             return;
644         }
645
646         r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
647     } while(r == ERROR_BUFFER_OVERFLOW);
648
649     if(r != NO_ERROR) {
650         catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
651         return;
652     }
653
654     // XXX remove interfaces for adapters that are no longer in the list
655
656     // create 'CattaInterface's for every adapter
657     for(p=buf; p; p=p->Next)
658         ip_adapter(m, p);
659
660     catta_free(buf);
661
662     m->list_complete = 1;
663     catta_interface_monitor_check_relevant(m);
664     catta_interface_monitor_update_rrs(m, 0);
665     catta_log_info("Network interface enumeration completed.");
666 }