+}
+
+
+// place the event into the queue to be handled (by the main thread)
+// and wake the event handler if necessary
+static void queue_event(CattaInterfaceMonitor *m, ChangeEvent *ev)
+{
+ char c = 'X';
+
+ if(!ev)
+ return;
+
+ if(!pthread_mutex_lock(&m->osdep.mutex)) {
+ // queue the event
+ CATTA_LLIST_APPEND(ChangeEvent, event, m->osdep.events, ev);
+
+ // wake the handler
+ writepipe(m->osdep.pipefd[1], &c, sizeof(c));
+
+ pthread_mutex_unlock(&m->osdep.mutex);
+ } else {
+ catta_log_debug(__FILE__": queue_event: could not lock mutex");
+ catta_free(ev);
+ }
+}
+
+// copy the given data row into an appropriate change event struct
+static ChangeEvent *new_event(ChangeEventType type, MIB_NOTIFICATION_TYPE ntype, void *row, size_t n)
+{
+ ChangeEvent *ev;
+
+ if(!row)
+ return NULL;
+
+ if(!(ev = catta_new(ChangeEvent, 1)))
+ return NULL;
+
+ ev->type = type;
+ ev->notification_type = ntype;
+ memcpy(&ev->data, row, n);
+
+ return ev;
+}
+
+static void WINAPI icn_callback(void *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
+{
+ queue_event(m, new_event(INTERFACE_CHANGE_EVENT, type, row, sizeof(*row)));
+}
+
+static void WINAPI acn_callback(void *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
+{
+ queue_event(m, new_event(ADDRESS_CHANGE_EVENT, type, row, sizeof(*row)));
+}
+
+static void update_hw_interface(CattaHwInterface *hw)
+{
+ MIB_IF_ROW2 row;
+ DWORD r;
+ size_t n;
+ int multicast; // synthetic flag
+
+ row.InterfaceLuid.Value = 0;
+ row.InterfaceIndex = hw->index;
+ if((r = GetIfEntry2(&row)) != NO_ERROR) {
+ catta_log_error("GetIfEntry2 failed for iface %d (error %u)", hw->index, (unsigned int)r);
+ return;
+ }
+
+ // fill the CattaHwInterface struct with data
+ // notice: this code is essentially duplicated from ip_adapter()
+ // notice: not sure where to find the IP_ADAPTER_NO_MULTICAST flag from an
+ // MIB_IF_ROW2 struct, so try to deduce it otherwise
+ // cf. http://msdn.microsoft.com/en-us/windows/desktop/ff568739(v=vs.100).aspx
+ multicast = row.AccessType == NET_IF_ACCESS_BROADCAST ||
+ row.AccessType == NET_IF_ACCESS_POINT_TO_POINT;
+ hw->flags_ok =
+ (row.OperStatus == IfOperStatusUp) &&
+ !(row.Type == IF_TYPE_SOFTWARE_LOOPBACK) &&
+ multicast &&
+ (hw->monitor->server->config.allow_point_to_point || !(row.Type == IF_TYPE_PPP));
+ // XXX what about IF_TYPE_TUNNEL?
+
+ n = wcstombs(NULL, row.Alias, 0) + 1;
+ catta_free(hw->name);
+ hw->name = catta_new(char, n);
+ wcstombs(hw->name, row.Alias, n);
+
+ hw->mtu = row.Mtu;
+
+ hw->mac_address_size = row.PhysicalAddressLength;
+ if(hw->mac_address_size > CATTA_MAC_ADDRESS_MAX)
+ hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
+ memcpy(hw->mac_address, row.PhysicalAddress, hw->mac_address_size);
+
+ catta_hw_interface_check_relevant(hw);
+ catta_hw_interface_update_rrs(hw, 0);
+}
+
+static void handle_iface_event(CattaInterfaceMonitor *m, MIB_IPINTERFACE_ROW *row, MIB_NOTIFICATION_TYPE type)
+{
+ CattaIfIndex idx = row->InterfaceIndex;
+ CattaProtocol proto = catta_af_to_proto(row->Family);
+ const char *protostr = catta_proto_to_string(proto);
+ CattaInterface *iface;
+ CattaHwInterface *hw;
+
+ // see if we know this interface
+ iface = catta_interface_monitor_get_interface(m, idx, proto);
+ hw = iface ? iface->hardware : catta_interface_monitor_get_hw_interface(m, idx);
+
+ // print debug messages for some unexpected cases
+ if(type==MibParameterNotification && !iface)
+ catta_log_debug("ParameterNotification received for unknown interface %d (%s)", idx, protostr);
+ if(type==MibDeleteInstance && !iface)
+ catta_log_debug("DeleteInstance received for unknown interface %d (%s)", idx, protostr);
+ if(type==MibAddInstance && iface)
+ catta_log_debug("AddInstance received for existing interface %d (%s)", idx, protostr);
+ if(iface && !hw)
+ catta_log_debug("missing CattaHwInterface for interface %d (%s)", idx, protostr);
+
+ switch(type) {
+ case MibParameterNotification:
+ case MibAddInstance:
+ // create the physical interface if it is missing
+ if(!hw) {
+ if((hw = catta_hw_interface_new(m, idx)) == NULL) {
+ catta_log_error("catta_hw_interface_new failed in handle_iface_event");
+ return;
+ }
+ }
+
+ // create the protocol-specific interface if it is missing
+ if(!iface) {
+ if((iface = catta_interface_new(m, hw, proto)) == NULL) {
+ catta_log_error("catta_interface_new failed in handle_iface_event");
+ return;
+ }
+ }
+
+ assert(iface != NULL);
+ assert(hw != NULL);
+ assert(iface->hardware == hw);
+
+ update_hw_interface(hw);
+ break;
+ case MibDeleteInstance:
+ if(iface)
+ catta_interface_free(iface, 0);
+
+ // free the hardware interface when there are no more protocol-specific interfaces
+ if(hw && !hw->interfaces)
+ catta_hw_interface_free(hw, 0);
+ break;
+ default:
+ catta_log_debug("unexpected type (%d) of interface change notification received", type);
+ }
+}
+
+static void handle_addr_event(CattaInterfaceMonitor *m, MIB_UNICASTIPADDRESS_ROW *row, MIB_NOTIFICATION_TYPE type)
+{
+ CattaIfIndex idx = row->InterfaceIndex;
+ CattaInterfaceAddress *ifaddr;
+ CattaInterface *iface;
+ CattaAddress addr;
+ const char *protostr;
+
+ // fill addr struct for address lookup
+ switch(row->Address.si_family) {
+ case AF_INET:
+ memcpy(addr.data.data, &row->Address.Ipv4.sin_addr, sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ memcpy(addr.data.data, &row->Address.Ipv6.sin6_addr, sizeof(struct in6_addr));
+ break;
+ default:
+ catta_log_debug("unexpected address family on interface %d: %u", idx, row->Address.si_family);
+ return;
+ }
+ addr.proto = catta_af_to_proto(row->Address.si_family);
+ protostr = catta_proto_to_string(addr.proto);
+
+ // see if we know this address/interface
+ iface = catta_interface_monitor_get_interface(m, idx, addr.proto);
+ ifaddr = iface ? catta_interface_monitor_get_address(m, iface, &addr) : NULL;
+
+ // print debug messages for some unexpected cases
+ if(type==MibParameterNotification && !ifaddr)
+ catta_log_debug("ParameterNotification received for unknown address on interface %d (%s)", idx, protostr);
+ if(type==MibDeleteInstance && !ifaddr)
+ catta_log_debug("DeleteInstance received for unknown address on interface %d (%s)", idx, protostr);
+ if(type==MibAddInstance && ifaddr)
+ catta_log_debug("AddInstance received for existing address on interface %d (%s)", idx, protostr);
+ if(ifaddr && !iface)
+ catta_log_debug("missing CattaInterface for address on interface %d (%s)", idx, protostr);
+
+ switch(type) {
+ case MibParameterNotification:
+ case MibAddInstance:
+ // fetch the full event data
+ if(GetUnicastIpAddressEntry(row) != NO_ERROR) {
+ catta_log_error("GetUnicastIpAddressEntry failed in handle_addr_event");
+ return;
+ }
+
+ // skip addresses that are not suitable as source addresses
+ if(row->SkipAsSource)
+ return;
+
+ // create the interface if it is missing
+ if(!iface) {
+ CattaHwInterface *hw;
+
+ if((hw = catta_interface_monitor_get_hw_interface(m, idx)) == NULL) {
+ catta_log_error("interface %d not found in handle_addr_event", idx);
+ return;
+ }
+
+ if((iface = catta_interface_new(m, hw, addr.proto)) == NULL) {
+ catta_log_error("catta_interface_new failed in handle_addr_event");
+ return;
+ }
+ }
+ assert(iface != NULL);
+
+ // create the interface-associated address if it is missing
+ if(!ifaddr) {
+ unsigned prefixlen = row->OnLinkPrefixLength;
+
+ if((ifaddr = catta_interface_address_new(m, iface, &addr, prefixlen)) == NULL) {
+ catta_log_error("catta_interface_address_new failed in handle_addr_event");
+ return;
+ }
+ }
+ assert(ifaddr != NULL);
+
+ set_global_scope_flag(ifaddr, &addr);
+ break;
+ case MibDeleteInstance:
+ if(ifaddr)
+ catta_interface_address_free(ifaddr);
+ break;
+ default:
+ catta_log_debug("unexpected type (%d) of address change notification received", type);
+ }
+
+ if(iface) {
+ catta_interface_check_relevant(iface);
+ catta_interface_update_rrs(iface, 0);
+ }
+}
+
+static void handle_events(CattaInterfaceMonitor *m)
+{
+ char buf[16];
+ ChangeEvent *ev;
+
+ if(!pthread_mutex_lock(&m->osdep.mutex)) {
+ // clear the pipe
+ while(readpipe(m->osdep.pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {}
+
+ while((ev = m->osdep.events) != NULL) {
+ CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
+
+ // dispatch to the appropriate handler
+ switch(ev->type) {
+ case INTERFACE_CHANGE_EVENT:
+ handle_iface_event(m, &ev->data.iface, ev->notification_type);
+ break;
+ case ADDRESS_CHANGE_EVENT:
+ handle_addr_event(m, &ev->data.addr, ev->notification_type);
+ break;
+ default:
+ catta_log_debug("unhandled change event type in handle_events");
+ }
+
+ catta_free(ev);
+ }
+
+ pthread_mutex_unlock(&m->osdep.mutex);
+ }
+}
+
+static void pipe_callback(CattaWatch *w, int fd, CattaWatchEvent event, void *m)
+{
+ // silence "unused parameter" warnings
+ (void)w;
+ (void)fd;
+ (void)event;
+
+ handle_events(m);