+ 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;
+
+ // XXX debug, remove
+ {
+ const char *typestr = NULL;
+
+ switch(type) {
+ case MibParameterNotification: typestr = "ParameterNotification"; break;
+ case MibAddInstance: typestr = "AddInstance"; break;
+ case MibDeleteInstance: typestr = "DeleteInstance"; break;
+ default: typestr = "Unknown";
+ }
+
+ catta_log_debug("interface %s on iface %d for %s", typestr, idx, protostr);
+ }
+
+ // 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);
+
+ // XXX debug, remove
+ {
+ const char *typestr = NULL;
+ char buf[CATTA_ADDRESS_STR_MAX];
+
+ switch(type) {
+ case MibParameterNotification: typestr = "ParameterNotification"; break;
+ case MibAddInstance: typestr = "AddInstance"; break;
+ case MibDeleteInstance: typestr = "DeleteInstance"; break;
+ default: typestr = "Unknown";
+ }
+
+ catta_log_debug("%s for %s address %s on iface %d",
+ typestr, protostr,
+ catta_address_snprint(buf, sizeof(buf), &addr),
+ idx);
+ }
+
+ // 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);
+ catta_log_debug(" global_scope: %d", ifaddr->global_scope); // XXX debugging, remove
+ 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);