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 "compat/windows/wincompat.h"
31 #include "util.h" // catta_format_mac_address
32 #include "fdutil.h" // catta_set_nonblock
36 INTERFACE_CHANGE_EVENT,
41 CATTA_LLIST_FIELDS(ChangeEvent, event);
43 MIB_NOTIFICATION_TYPE notification_type;
45 MIB_IPINTERFACE_ROW iface;
46 MIB_UNICASTIPADDRESS_ROW addr;
51 // helper: determine the global_scope flag for an address
52 static void set_global_scope_flag(CattaInterfaceAddress *ifaddr, const CattaAddress *addr)
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));
58 ifaddr->global_scope = 1;
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,
66 IP_ADAPTER_UNICAST_ADDRESS *a)
68 CattaInterface *iface;
70 CattaInterfaceAddress *ifaddr;
71 struct sockaddr *sa = a->Address.lpSockaddr;
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)
79 // fill addr struct for address lookup
80 switch(sa->sa_family) {
82 memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
85 memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
88 catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
91 addr.proto = catta_af_to_proto(sa->sa_family);
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);
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");
107 set_global_scope_flag(ifaddr, &addr);
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)
114 IP_ADAPTER_UNICAST_ADDRESS *a;
116 CattaHwInterface *hw;
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
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
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);
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");
146 // fill the CattaHwInterface struct with data
147 // notice: this code is essentially duplicated in update_hw_interface()
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?
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);
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);
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);
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)
183 if(!pthread_mutex_lock(&m->osdep.mutex)) {
185 CATTA_LLIST_APPEND(ChangeEvent, event, m->osdep.events, ev);
188 writepipe(m->osdep.pipefd[1], &c, sizeof(c));
190 pthread_mutex_unlock(&m->osdep.mutex);
192 catta_log_debug(__FILE__": queue_event: could not lock mutex");
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)
205 if(!(ev = catta_new(ChangeEvent, 1)))
209 ev->notification_type = ntype;
210 memcpy(&ev->data, row, n);
215 static void WINAPI icn_callback(void *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
217 queue_event(m, new_event(INTERFACE_CHANGE_EVENT, type, row, sizeof(*row)));
220 static void WINAPI acn_callback(void *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
222 queue_event(m, new_event(ADDRESS_CHANGE_EVENT, type, row, sizeof(*row)));
225 static void update_hw_interface(CattaHwInterface *hw)
230 int multicast; // synthetic flag
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);
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;
247 (row.OperStatus == IfOperStatusUp) &&
248 !(row.Type == IF_TYPE_SOFTWARE_LOOPBACK) &&
250 (hw->monitor->server->config.allow_point_to_point || !(row.Type == IF_TYPE_PPP));
251 // XXX what about IF_TYPE_TUNNEL?
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);
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);
265 catta_hw_interface_check_relevant(hw);
266 catta_hw_interface_update_rrs(hw, 0);
269 static void handle_iface_event(CattaInterfaceMonitor *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
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;
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);
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);
289 catta_log_debug("missing CattaHwInterface for interface %d (%s)", idx, protostr);
292 case MibParameterNotification:
294 // create the physical interface if it is missing
296 if((hw = catta_hw_interface_new(m, idx)) == NULL) {
297 catta_log_error("catta_hw_interface_new failed in handle_iface_event");
302 // create the protocol-specific interface if it is missing
304 if((iface = catta_interface_new(m, hw, proto)) == NULL) {
305 catta_log_error("catta_interface_new failed in handle_iface_event");
310 assert(iface != NULL);
312 assert(iface->hardware == hw);
314 update_hw_interface(hw);
316 case MibDeleteInstance:
318 catta_interface_free(iface, 0);
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);
325 catta_log_debug("unexpected type (%d) of interface change notification received", type);
329 static void handle_addr_event(CattaInterfaceMonitor *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
331 CattaIfIndex idx = row->InterfaceIndex;
332 CattaInterfaceAddress *ifaddr;
333 CattaInterface *iface;
335 const char *protostr;
337 // fill addr struct for address lookup
338 switch(row->Address.si_family) {
340 memcpy(addr.data.data, &row->Address.Ipv4.sin_addr, sizeof(struct in_addr));
343 memcpy(addr.data.data, &row->Address.Ipv6.sin6_addr, sizeof(struct in6_addr));
346 catta_log_debug("unexpected address family on interface %d: %u", idx, row->Address.si_family);
349 addr.proto = catta_af_to_proto(row->Address.si_family);
350 protostr = catta_proto_to_string(addr.proto);
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;
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);
364 catta_log_debug("missing CattaInterface for address on interface %d (%s)", idx, protostr);
367 case MibParameterNotification:
369 // fetch the full event data
370 if(GetUnicastIpAddressEntry(row) != NO_ERROR) {
371 catta_log_error("GetUnicastIpAddressEntry failed in handle_addr_event");
375 // skip addresses that are not suitable as source addresses
376 if(row->SkipAsSource)
379 // create the interface if it is missing
381 CattaHwInterface *hw;
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);
388 if((iface = catta_interface_new(m, hw, addr.proto)) == NULL) {
389 catta_log_error("catta_interface_new failed in handle_addr_event");
393 assert(iface != NULL);
395 // create the interface-associated address if it is missing
397 unsigned prefixlen = row->OnLinkPrefixLength;
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");
404 assert(ifaddr != NULL);
406 set_global_scope_flag(ifaddr, &addr);
408 case MibDeleteInstance:
410 catta_interface_address_free(ifaddr);
413 catta_log_debug("unexpected type (%d) of address change notification received", type);
417 catta_interface_check_relevant(iface);
418 catta_interface_update_rrs(iface, 0);
422 static void handle_events(CattaInterfaceMonitor *m)
427 if(!pthread_mutex_lock(&m->osdep.mutex)) {
429 while(readpipe(m->osdep.pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {}
431 while((ev = m->osdep.events) != NULL) {
432 CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
434 // dispatch to the appropriate handler
436 case INTERFACE_CHANGE_EVENT:
437 handle_iface_event(m, &ev->data.iface, ev->notification_type);
439 case ADDRESS_CHANGE_EVENT:
440 handle_addr_event(m, &ev->data.addr, ev->notification_type);
443 catta_log_debug("unhandled change event type in handle_events");
449 pthread_mutex_unlock(&m->osdep.mutex);
453 static void pipe_callback(CattaWatch *w, int fd, CattaWatchEvent event, void *m)
455 // silence "unused parameter" warnings
464 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
468 pthread_mutex_init(&m->osdep.mutex, NULL);
470 CATTA_LLIST_HEAD_INIT(ChangeEvent, m->osdep.events);
472 if(pipe(m->osdep.pipefd) < 0) {
473 catta_log_error("pipe() in catta_interface_monitor_init_osdep failed");
476 if(catta_set_nonblock(m->osdep.pipefd[0]) < 0 ||
477 catta_set_nonblock(m->osdep.pipefd[1]) < 0)
479 catta_log_error(__FILE__": catta_set_nonblock failed: %s", errnostrsocket());
483 m->osdep.icnhandle = NULL;
484 m->osdep.acnhandle = NULL;
486 // register handler for change events
487 m->osdep.watch = m->server->poll_api->watch_new(m->server->poll_api,
492 if(!m->osdep.watch) {
493 catta_log_error(__FILE__": Failed to create watch.");
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);
505 catta_log_error("NotifyIpInterfaceChange failed: %u", (unsigned int)r);
507 // request async notification on address changes
508 r = NotifyUnicastIpAddressChange(AF_UNSPEC, acn_callback, m, FALSE,
509 &m->osdep.acnhandle);
511 catta_log_error("NotifyUnicastIpAddressChange failed: %u", (unsigned int)r);
516 closesocket(m->osdep.pipefd[0]);
517 closesocket(m->osdep.pipefd[1]);
521 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
525 // unregister callbacks
526 if(m->osdep.icnhandle) CancelMibChangeNotify2(m->osdep.icnhandle);
527 if(m->osdep.acnhandle) CancelMibChangeNotify2(m->osdep.acnhandle);
529 // unregister event handler
530 m->server->poll_api->watch_free(m->osdep.watch);
533 closepipe(m->osdep.pipefd[0]);
534 closepipe(m->osdep.pipefd[1]);
536 // make sure no stray events can come in during destruction
537 pthread_mutex_lock(&m->osdep.mutex);
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);
545 pthread_mutex_unlock(&m->osdep.mutex);
546 pthread_mutex_destroy(&m->osdep.mutex);
549 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
551 IP_ADAPTER_ADDRESSES *buf = NULL;
552 IP_ADAPTER_ADDRESSES *p;
553 ULONG bufsize = 15000;
556 // allocate a buffer and call GetAdaptersAddresses
557 // retry with the correct size if the buffer was too small
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");
565 r = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buf, &bufsize);
566 } while(r == ERROR_BUFFER_OVERFLOW);
569 catta_log_error("GetAdaptersAddresses failed: %u", (unsigned int)r);
573 // XXX remove interfaces for adapters that are no longer in the list
575 // create 'CattaInterface's for every adapter
576 for(p=buf; p; p=p->Next)
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.");