]> git.meshlink.io Git - catta/blobdiff - src/iface-windows.c
remove debug output on initial interface enumeration
[catta] / src / iface-windows.c
index 9fe4ffeb66f1d66f01f48081eee1ba7765536daf..37dc702559673ba4695a397caf6ce5719a772dce 100644 (file)
   USA.
 ***/
 
-#include "iface-windows.h"
 #include "iface.h"
+#include "iface-windows.h"
 
 #include <stdlib.h> // wcstombs
 #include <catta/malloc.h>
 #include <catta/log.h>
 #include <iphlpapi.h>
+#include <assert.h>
 #include "hashmap.h"
 #include "util.h"   // catta_format_mac_address
+#include "fdutil.h" // catta_set_nonblock
 
 
-// for the luid-to-idx hashmap
-static unsigned luid_hash(const void *data)
-{
-    return ((NET_LUID *)data)->Info.NetLuidIndex;
-}
-static int luid_equal(const void *a, const void *b)
+typedef enum {
+    INTERFACE_CHANGE_EVENT,
+    ADDRESS_CHANGE_EVENT
+} ChangeEventType;
+
+struct ChangeEvent {
+    CATTA_LLIST_FIELDS(ChangeEvent, event);
+    ChangeEventType type;
+    MIB_NOTIFICATION_TYPE notification_type;
+    union {
+        MIB_IPINTERFACE_ROW iface;
+        MIB_UNICASTIPADDRESS_ROW addr;
+    } data;
+};
+
+
+// helper: determine the global_scope flag for an address
+static void set_global_scope_flag(CattaInterfaceAddress *ifaddr, const CattaAddress *addr)
 {
-    return (((NET_LUID *)a)->Value == ((NET_LUID *)b)->Value);
+    if(addr->proto == CATTA_PROTO_INET6) {
+        const struct in6_addr *ia = (struct in6_addr *)addr->data.ipv6.address;
+        ifaddr->global_scope = !(IN6_IS_ADDR_LINKLOCAL(ia) || IN6_IS_ADDR_MULTICAST(ia));
+    } else {
+        ifaddr->global_scope = 1;
+    }
 }
 
-static CattaIfIndex find_ifindex(CattaInterfaceMonitor *m, NET_LUID luid)
+// integrate the information from an IP_ADAPTER_UNICAST_ADDRESS structure for
+// given CattaHwInterface into the CattaInterfaceMonitor
+static void ip_adapter_unicast_address(CattaInterfaceMonitor *m,
+                                       CattaHwInterface *hw,
+                                       IP_ADAPTER_UNICAST_ADDRESS *a)
 {
-    CattaIfIndex *pi = NULL;
-    NET_LUID *key = NULL;
-
-    if((pi = catta_hashmap_lookup(m->osdep.idxmap, &luid)) == NULL) {
-        // allocate memory for the hashmap key and value
-        key = catta_malloc(sizeof(luid));
-        pi = catta_malloc(sizeof(CattaIfIndex));
-        if(!key || !pi)
-            goto fail;
-
-        *key = luid;
-            
-        // find an index for this luid
-        *pi = m->osdep.nidx;
-        if(*pi < 0)  // overflow
-            goto fail;
-
-        // register the index
-        if(catta_hashmap_replace(m->osdep.idxmap, key, pi) < 0)
-            goto fail;
-        m->osdep.nidx++;
-    }
-
-    return *pi;
-
-fail:
-    catta_free(key);
-    catta_free(pi);
-    return -1;
+    CattaInterface *iface;
+    CattaAddress addr;
+    CattaInterfaceAddress *ifaddr;
+    struct sockaddr *sa = a->Address.lpSockaddr;
+
+    // skip transient addresses; to quote MSDN: "The IP address is a cluster
+    // address and should not be used by most applications."
+    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
+    if(a->Flags & IP_ADAPTER_ADDRESS_TRANSIENT)
+        return;
+
+    // fill addr struct for address lookup
+    switch(sa->sa_family) {
+    case AF_INET:
+        memcpy(addr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
+        break;
+    case AF_INET6:
+        memcpy(addr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
+        break;
+    default:
+        catta_log_debug("unexpected address family on interface %d: %u", hw->index, sa->sa_family);
+        return;
+    }
+    addr.proto = catta_af_to_proto(sa->sa_family);
+
+    // get protocol-specific CattaInterface object
+    if(!(iface = catta_interface_monitor_get_interface(m, hw->index, addr.proto))) {
+        catta_log_error("CattaInterface (index %d, proto %d) not found", hw->index, addr.proto);
+        return;
+    }
+
+    // find or allocate a CattaInterfaceAddress struct for this address
+    if(!(ifaddr = catta_interface_monitor_get_address(m, iface, &addr))) {
+        if(!(ifaddr = catta_interface_address_new(m, iface, &addr, a->OnLinkPrefixLength))) {
+            catta_log_error("out of memory in ip_adapter_unicast_address");
+            return;
+        }
+    }
+
+    set_global_scope_flag(ifaddr, &addr);
 }
 
 // integrate the information from an IP_ADAPTER_ADDRESSES structure
 // as returned by GetAdaptersAddresses into the CattaInterfaceMonitor
-static void ip_adapter_address(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
+static void ip_adapter(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p)
 {
+    IP_ADAPTER_UNICAST_ADDRESS *a;
     CattaIfIndex idx;
     CattaHwInterface *hw;
     size_t n;
 
-    // look up the interface index by LUID
-    if((idx = find_ifindex(m, p->Luid)) < 0) {
-        catta_log_error("could not allocate index ip_adapter_address");
+    // we want an index specific to the hardware interface, but Windows
+    // has one for IPv4 and one for IPv6. it seems like these are always the
+    // same unless one of the protocols is not available. let's have a bunch of
+    // checks...
+    if(!p->IfIndex && !p->Ipv6IfIndex) {
+        return; // no usable protocols
+    } else if(!p->IfIndex) {
+        idx = p->Ipv6IfIndex;   // IPv6 but no IPv4 (huh!)
+    } else if(!p->Ipv6IfIndex) {
+        idx = p->IfIndex;       // IPv4 but no IPv6
+    } else if(p->IfIndex == p->Ipv6IfIndex) {
+        idx = p->IfIndex;       // same index for both protocols
+    } else {
+        // both indexes valid but not equal
+        catta_log_error("unsupported interface: %ls (IfIndex and Ipv6IfIndex differ: %u/%u)",
+            p->FriendlyName, (unsigned int)p->IfIndex, (unsigned int)p->Ipv6IfIndex);
         return;
     }
 
@@ -94,6 +142,7 @@ static void ip_adapter_address(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p
     }
 
     // fill the CattaHwInterface struct with data
+    // notice: this code is essentially duplicated in update_hw_interface()
     hw->flags_ok =
         (p->OperStatus == IfOperStatusUp) &&
         !(p->IfType == IF_TYPE_SOFTWARE_LOOPBACK) &&
@@ -113,41 +162,431 @@ static void ip_adapter_address(CattaInterfaceMonitor *m, IP_ADAPTER_ADDRESSES *p
         hw->mac_address_size = CATTA_MAC_ADDRESS_MAX;
     memcpy(hw->mac_address, p->PhysicalAddress, hw->mac_address_size);
 
-    // XXX process addresses
+    // process addresses
+    // XXX remove addresses that are no longer in the list
+    for(a=p->FirstUnicastAddress; a; a=a->Next)
+        ip_adapter_unicast_address(m, hw, a);
+}
+
+
+// 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);
 
     // XXX debugging, remove
-   { 
-     char mac[256]; 
-     catta_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n type:%u\n status:%u\n multicast:%d\n flags:0x%.4x\n flags_ok:%d\n======",  
-                   hw->name, hw->index,  
-                   hw->mtu,  
-                   catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size), 
-                   (unsigned int)p->IfType,
-                   (unsigned int)p->OperStatus,
-                   !(p->Flags & IP_ADAPTER_NO_MULTICAST),
-                   (unsigned int)p->Flags,
-            hw->flags_ok); 
-   } 
+    {
+        char mac[256];
+        catta_log_debug(" name: %s\n"
+                        " mtu: %d\n"
+                        " mac: %s\n"
+                        " flags_ok: %d\n"
+                        "   type: %u\n"
+                        "   status: %u\n"
+                        "   multicast: %d\n"
+                        "   access type: %d",
+            hw->name,
+            hw->mtu,
+            catta_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size),
+            hw->flags_ok,
+            (unsigned int)row.Type,
+            (unsigned int)row.OperStatus,
+            multicast,
+            (int)row.AccessType);
+    }
+
+    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);
 }
 
 
 int catta_interface_monitor_init_osdep(CattaInterfaceMonitor *m)
 {
-    m->osdep.nidx = 0;
-    m->osdep.idxmap = catta_hashmap_new(luid_hash, luid_equal, catta_free, catta_free);
-    if(m->osdep.idxmap == NULL) {
-        catta_log_error("out of memory in catta_interface_monitor_init_osdep");
+    DWORD r;
+
+    pthread_mutex_init(&m->osdep.mutex, NULL);
+
+    CATTA_LLIST_HEAD_INIT(ChangeEvent, m->osdep.events);
+
+    if(pipe(m->osdep.pipefd) < 0) {
+        catta_log_error("pipe() in catta_interface_monitor_init_osdep failed");
+        return -1;
+    }
+    catta_set_nonblock(m->osdep.pipefd[0]);
+    catta_set_nonblock(m->osdep.pipefd[1]);
+
+    m->osdep.icnhandle = NULL;
+    m->osdep.acnhandle = NULL;
+
+    // register handler for change events
+    m->osdep.watch = m->server->poll_api->watch_new(m->server->poll_api,
+                                                    m->osdep.pipefd[0],
+                                                    CATTA_WATCH_IN,
+                                                    pipe_callback,
+                                                    m);
+    if(!m->osdep.watch) {
+        catta_log_error(__FILE__": Failed to create watch.");
         return -1;
     }
 
-    // XXX register callbacks to get notified of interface/address changes
+    // request async notification on interface changes
+    r = NotifyIpInterfaceChange(AF_UNSPEC,
+                                // icn_callback needs to be WINAPI but
+                                // MingW up to 3.1.0 erroneously defines
+                                // PIPINTERFACE_CHANGE_CALLBACK without it
+                                (PIPINTERFACE_CHANGE_CALLBACK)icn_callback,
+                                m, FALSE, &m->osdep.icnhandle);
+    if(r != NO_ERROR)
+        catta_log_error("NotifyIpInterfaceChange failed: %u", (unsigned int)r);
+
+    // request async notification on address changes
+    r = NotifyUnicastIpAddressChange(AF_UNSPEC, acn_callback, m, FALSE,
+                                     &m->osdep.acnhandle);
+    if(r != NO_ERROR)
+        catta_log_error("NotifyUnicastIpAddressChange failed: %u", (unsigned int)r);
 
     return 0;
 }
 
 void catta_interface_monitor_free_osdep(CattaInterfaceMonitor *m)
 {
-    catta_hashmap_free(m->osdep.idxmap);
+    ChangeEvent *ev;
+
+    // unregister callbacks
+    if(m->osdep.icnhandle) CancelMibChangeNotify2(m->osdep.icnhandle);
+    if(m->osdep.acnhandle) CancelMibChangeNotify2(m->osdep.acnhandle);
+
+    // unregister event handler
+    m->server->poll_api->watch_free(m->osdep.watch);
+
+    // close pipe
+    closepipe(m->osdep.pipefd[0]);
+    closepipe(m->osdep.pipefd[1]);
+
+    // make sure no stray events can come in during destruction
+    pthread_mutex_lock(&m->osdep.mutex);
+
+    // free all events that are still in the queue
+    while((ev = m->osdep.events) != NULL) {
+        CATTA_LLIST_REMOVE(ChangeEvent, event, m->osdep.events, ev);
+        catta_free(ev);
+    }
+
+    pthread_mutex_unlock(&m->osdep.mutex);
+    pthread_mutex_destroy(&m->osdep.mutex);
 }
 
 void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
@@ -178,7 +617,7 @@ void catta_interface_monitor_sync(CattaInterfaceMonitor *m)
 
     // create 'CattaInterface's for every adapter
     for(p=buf; p; p=p->Next)
-        ip_adapter_address(m, p);
+        ip_adapter(m, p);
 
     catta_free(buf);