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