2 This file is part of catta.
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.
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.
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
21 #include "iface-windows.h"
23 #include <stdlib.h> // wcstombs
24 #include <catta/malloc.h>
25 #include <catta/log.h>
29 #include "util.h" // catta_format_mac_address
30 #include "fdutil.h" // catta_set_nonblock
34 INTERFACE_CHANGE_EVENT,
39 CATTA_LLIST_FIELDS(ChangeEvent, event);
41 MIB_NOTIFICATION_TYPE notification_type;
43 MIB_IPINTERFACE_ROW iface;
44 MIB_UNICASTIPADDRESS_ROW addr;
49 // integrate the information from an IP_ADAPTER_UNICAST_ADDRESS structure for
50 // given CattaHwInterface into the CattaInterfaceMonitor
51 static void ip_adapter_unicast_address(CattaInterfaceMonitor *m,
53 IP_ADAPTER_UNICAST_ADDRESS *a)
55 CattaInterface *iface;
57 CattaInterfaceAddress *ifaddr;
58 struct sockaddr *sa = a->Address.lpSockaddr;
60 // skip transient addresses; to quote MSDN: "The IP address is a cluster
61 // address and should not be used by most applications."
62 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
63 if(a->Flags & IP_ADAPTER_ADDRESS_TRANSIENT)
66 // fill addr struct for address lookup
67 switch(sa->sa_family) {
69 memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
72 memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
75 catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
78 addr.proto = catta_af_to_proto(sa->sa_family);
80 // get protocol-specific CattaInterface object
81 if(!(iface = catta_interface_monitor_get_interface(m, hw->index, addr.proto))) {
82 catta_log_error("CattaInterface (index %d, proto %d) not found", hw->index, addr.proto);
86 // find or allocate a CattaInterfaceAddress struct for this address
87 if(!(ifaddr = catta_interface_monitor_get_address(m, iface, &addr))) {
88 if(!(ifaddr = catta_interface_address_new(m, iface, &addr, a->OnLinkPrefixLength))) {
89 catta_log_error("out of memory in ip_adapter_unicast_address");
94 // set global scope flag
95 // XXX should we use the IP_ADAPTER_ADDRESS_DNS_ELIGIBLE flag for this?
96 // it looks like it gets set for IPv4 addresses that are not localhost
97 // and for IPv6 addresses with global scope. not sure about multicast
99 if(addr.proto == CATTA_PROTO_INET6)
100 ifaddr->global_scope = !(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr.data.data)
101 || IN6_IS_ADDR_MULTICAST((struct in6_addr *)addr.data.data));
103 ifaddr->global_scope = 1;
105 // XXX debugging, remove
107 char s[CATTA_ADDRESS_STR_MAX];
108 catta_log_debug(" address: %s\n"
109 " global_scope: %d\n"
111 catta_address_snprint(s, sizeof(s), &addr),
112 ifaddr->global_scope,
113 (unsigned int)a->Flags);
117 // integrate the information from an IP_ADAPTER_ADDRESSES structure
118 // as returned by GetAdaptersAddresses into the CattaInterfaceMonitor
119 static void ip_adapter(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
121 IP_ADAPTER_UNICAST_ADDRESS *a;
123 CattaHwInterface *hw;
126 // we want an index specific to the hardware interface, but Windows
127 // has one for IPv4 and one for IPv6. it seems like these are always the
128 // same unless one of the protocols is not available. let's have a bunch of
130 if(!p->IfIndex && !p->Ipv6IfIndex) {
131 return; // no usable protocols
132 } else if(!p->IfIndex) {
133 idx = p->Ipv6IfIndex; // IPv6 but no IPv4 (huh!)
134 } else if(!p->Ipv6IfIndex) {
135 idx = p->IfIndex; // IPv4 but no IPv6
136 } else if(p->IfIndex == p->Ipv6IfIndex) {
137 idx = p->IfIndex; // same index for both protocols
139 // both indexes valid but not equal
140 catta_log_error("unsupported interface: %ls (IfIndex and Ipv6IfIndex differ: %u/%u)",
141 p->FriendlyName, (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex);
145 // find the CattaHwInterface by index or allocate a new one
146 if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
147 if((hw = catta_hw_interface_new(m, idx)) == NULL) {
148 catta_log_error("catta_hw_interface_new failed in ip_adapter_address");
153 // fill the CattaHwInterface struct with data
155 (p->OperStatus == IfOperStatusUp) &&
156 !(p->IfType == IF_TYPE_SOFTWARE_LOOPBACK) &&
157 !(p->Flags & IP_ADAPTER_NO_MULTICAST) &&
158 (m->server->config.allow_point_to_point || !(p->IfType == IF_TYPE_PPP));
159 // XXX what about IF_TYPE_TUNNEL?
161 n = wcstombs(NULL, p->FriendlyName, 0) + 1;
162 catta_free(hw->name);
163 hw->name = catta_new(char, n);
164 wcstombs(hw->name, p->FriendlyName, n);
168 hw->mac_address_size = p->PhysicalAddressLength;
169 if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
170 hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
171 memcpy(hw->mac_address, p->PhysicalAddress, hw->mac_address_size);
173 // XXX debugging, remove
176 catta_log_debug(" name: %s\n"
188 (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex,
190 catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
192 (unsigned int)p->IfType,
193 (unsigned int)p->OperStatus,
194 !(p->Flags & IP_ADAPTER_NO_MULTICAST),
195 (unsigned int)p->Flags);
199 // XXX remove addresses that are no longer in the list
200 for(a=p->FirstUnicastAddress; a; a=a->Next)
201 ip_adapter_unicast_address(m, hw, a);
202 catta_log_debug("=====");
206 // place the event into the queue to be handled (by the main thread)
207 // and wake the event handler if necessary
208 static void queue_event(CattaInterfaceMonitor *m, ChangeEvent *ev)
215 if(!pthread_mutex_lock(&m->osdep.mutex)) {
217 // XXX event ordering!!
218 CATTA_LLIST_PREPEND(ChangeEvent, event, m->osdep.events, ev);
221 writepipe(m->osdep.pipefd[1], &c, sizeof(c));
223 pthread_mutex_unlock(&m->osdep.mutex);
225 catta_log_debug(__FILE__": queue_event: could not lock mutex");
230 // copy the given data row into an appropriate change event struct
231 static ChangeEvent *new_event(ChangeEventType type, MIB_NOTIFICATION_TYPE ntype, void *row, size_t n)
238 if(!(ev = catta_new(ChangeEvent, 1)))
242 ev->notification_type = ntype;
243 memcpy(&ev->data, row, n);
248 static void WINAPI icn_callback(void *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
250 queue_event(m, new_event(INTERFACE_CHANGE_EVENT, type, row, sizeof(*row)));
253 static void WINAPI acn_callback(void *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
255 queue_event(m, new_event(ADDRESS_CHANGE_EVENT, type, row, sizeof(*row)));
258 static void handle_iface_event(CattaInterfaceMonitor *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
260 catta_log_debug("interface change event on iface %u for address family %u",
261 (unsigned int)row->InterfaceIndex, (unsigned int)row->Family);
264 case MibParameterNotification:
265 catta_log_debug(" notification type: ParameterNotification");
268 catta_log_debug(" notification type: AddInstance");
270 case MibDeleteInstance:
271 catta_log_debug(" notification type: DeleteInstance");
274 catta_log_debug("unexpected type (%d) of interface change notification received", type);
278 static void handle_addr_event(CattaInterfaceMonitor *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
280 catta_log_debug("address change event on iface %u for address family %u",
281 (unsigned int)row->InterfaceIndex,
282 (unsigned int)row->Address.si_family);
285 case MibParameterNotification:
286 catta_log_debug(" notification type: ParameterNotification");
289 catta_log_debug(" notification type: AddInstance");
291 case MibDeleteInstance:
292 catta_log_debug(" notification type: DeleteInstance");
295 catta_log_debug("unexpected type (%d) of address change notification received", type);
299 static void handle_events(CattaInterfaceMonitor *m)
304 if(!pthread_mutex_lock(&m->osdep.mutex)) {
306 while(readpipe(m->osdep.pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {}
308 while((ev = m->osdep.events) != NULL) {
309 CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
311 // dispatch to the appropriate handler
313 case INTERFACE_CHANGE_EVENT:
314 handle_iface_event(m, &ev->data.iface, ev->notification_type);
316 case ADDRESS_CHANGE_EVENT:
317 handle_addr_event(m, &ev->data.addr, ev->notification_type);
320 catta_log_debug("unhandled change event type in handle_events");
326 pthread_mutex_unlock(&m->osdep.mutex);
330 static void pipe_callback(CattaWatch *w, int fd, CattaWatchEvent event, void *m)
332 // silence "unused parameter" warnings
341 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
345 pthread_mutex_init(&m->osdep.mutex, NULL);
347 CATTA_LLIST_HEAD_INIT(ChangeEvent, m->osdep.events);
349 if(pipe(m->osdep.pipefd) < 0) {
350 catta_log_error("pipe() in catta_interface_monitor_init_osdep failed");
353 catta_set_nonblock(m->osdep.pipefd[0]);
354 catta_set_nonblock(m->osdep.pipefd[1]);
356 m->osdep.icnhandle = NULL;
357 m->osdep.acnhandle = NULL;
359 // register handler for change events
360 m->osdep.watch = m->server->poll_api->watch_new(m->server->poll_api,
365 if(!m->osdep.watch) {
366 catta_log_error(__FILE__": Failed to create watch.");
370 // request async notification on interface changes
371 r = NotifyIpInterfaceChange(AF_UNSPEC,
372 // icn_callback needs to be WINAPI but
373 // MingW up to 3.1.0 erroneously defines
374 // PIPINTERFACE_CHANGE_CALLBACK without it
375 (PIPINTERFACE_CHANGE_CALLBACK)icn_callback,
376 m, FALSE, &m->osdep.icnhandle);
378 catta_log_error("NotifyIpInterfaceChange failed: %u", (unsigned int)r);
380 // request async notification on address changes
381 r = NotifyUnicastIpAddressChange(AF_UNSPEC, acn_callback, m, FALSE,
382 &m->osdep.acnhandle);
384 catta_log_error("NotifyUnicastIpAddressChange failed: %u", (unsigned int)r);
389 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
393 // unregister callbacks
394 if(m->osdep.icnhandle) CancelMibChangeNotify2(m->osdep.icnhandle);
395 if(m->osdep.acnhandle) CancelMibChangeNotify2(m->osdep.acnhandle);
397 // unregister event handler
398 m->server->poll_api->watch_free(m->osdep.watch);
401 closepipe(m->osdep.pipefd[0]);
402 closepipe(m->osdep.pipefd[1]);
404 // make sure no stray events can come in during destruction
405 pthread_mutex_lock(&m->osdep.mutex);
407 // free all events that are still in the queue
408 while((ev = m->osdep.events) != NULL) {
409 CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
413 pthread_mutex_unlock(&m->osdep.mutex);
414 pthread_mutex_destroy(&m->osdep.mutex);
417 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
419 IP_ADAPTER_ADDRESSES *buf = NULL;
420 IP_ADAPTER_ADDRESSES *p;
421 ULONG bufsize = 15000;
424 // allocate a buffer and call GetAdaptersAddresses
425 // retry with the correct size if the buffer was too small
427 catta_free(buf); // no-op on first iteration
428 if((buf = catta_malloc(bufsize)) == NULL) {
429 catta_log_error("malloc failed in catta_interface_monitor_sync");
433 r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
434 } while(r == ERROR_BUFFER_OVERFLOW);
437 catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
441 // XXX remove interfaces for adapters that are no longer in the list
443 // create 'CattaInterface's for every adapter
444 for(p=buf; p; p=p->Next)
449 m->list_complete = 1;
450 catta_interface_monitor_check_relevant(m);
451 catta_interface_monitor_update_rrs(m, 0);
452 catta_log_info("Network interface enumeration completed.");