Răsfoiți Sursa

ncd: NCDInterfaceMonitor: implement observing IP addresses of interfaces, implement
reporting initial state

ambrop7 14 ani în urmă
părinte
comite
9707c898a3
4 a modificat fișierele cu 395 adăugiri și 77 ștergeri
  1. 6 2
      ncd/CMakeLists.txt
  2. 254 48
      ncd/NCDInterfaceMonitor.c
  3. 102 6
      ncd/NCDInterfaceMonitor.h
  4. 33 21
      ncd/modules/net_backend_waitlink.c

+ 6 - 2
ncd/CMakeLists.txt

@@ -40,12 +40,16 @@ add_library(ncdrequest
 )
 target_link_libraries(ncdrequest base system ncdvalue)
 
+add_library(ncdinterfacemonitor
+    NCDInterfaceMonitor.c
+)
+target_link_libraries(ncdinterfacemonitor base system)
+
 add_executable(badvpn-ncd
     ncd.c
     NCDModule.c
     NCDModuleIndex.c
     NCDIfConfig.c
-    NCDInterfaceMonitor.c
     NCDObject.c
     BEventLock.c
     modules/command_template.c
@@ -99,7 +103,7 @@ add_executable(badvpn-ncd
     modules/sys_request_server.c
     ${NCD_ADDITIONAL_SOURCES}
 )
-target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdconfig ncdvalue udevmonitor)
+target_link_libraries(badvpn-ncd system flow flowextra dhcpclient arpprobe ncdconfig ncdvalue udevmonitor ncdinterfacemonitor)
 
 if (BADVPN_USE_LINUX_INPUT)
     string(REPLACE " " ";" FLAGS_LIST "${CMAKE_C_FLAGS}")

+ 254 - 48
ncd/NCDInterfaceMonitor.c

@@ -28,7 +28,6 @@
  */
 
 #include <string.h>
-#include <stdio.h>
 #include <unistd.h>
 #include <stdint.h>
 
@@ -47,91 +46,245 @@
 
 #include <generated/blog_channel_NCDInterfaceMonitor.h>
 
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+
+static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type);
+static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len);
+static void report_error (NCDInterfaceMonitor *o);
+static int send_next_dump_request (NCDInterfaceMonitor *o);
 static void netlink_fd_handler (NCDInterfaceMonitor *o, int events);
 static void process_buffer (NCDInterfaceMonitor *o);
 static void more_job_handler (NCDInterfaceMonitor *o);
 
+static int send_wilddump_request (NCDInterfaceMonitor *o, int fd, uint32_t seq, int family, int type)
+{
+    struct {
+        struct nlmsghdr nlh;
+        struct rtgenmsg g;
+    } req;
+    
+    memset(&req, 0, sizeof(req));
+    req.nlh.nlmsg_len = sizeof(req);
+    req.nlh.nlmsg_type = type;
+    req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+    req.nlh.nlmsg_pid = 0;
+    req.nlh.nlmsg_seq = seq;
+    req.g.rtgen_family = family;
+    
+    int res = write(fd, &req, sizeof(req));
+    if (res < 0) {
+        BLog(BLOG_ERROR, "write failed");
+        return 0;
+    }
+    if (res != sizeof(req)) {
+        BLog(BLOG_ERROR, "write short");
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int get_attr (int type, struct rtattr *rta, int rta_len, void **out_attr, int *out_attr_len)
+{
+    for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
+        uint8_t *attr = RTA_DATA(rta);
+        int attr_len = RTA_PAYLOAD(rta);
+        
+        if (rta->rta_type == type) {
+            *out_attr = attr;
+            *out_attr_len = attr_len;
+            return 1;
+        }
+    }
+    
+    return 0;
+}
+
+static void report_error (NCDInterfaceMonitor *o)
+{
+    DEBUGERROR(&o->d_err, o->handler_error(o->user))
+}
+
+static int send_next_dump_request (NCDInterfaceMonitor *o)
+{
+    ASSERT(o->dump_queue)
+    
+    if (o->dump_queue & NCDIFMONITOR_WATCH_LINK) {
+        o->dump_queue &= ~NCDIFMONITOR_WATCH_LINK;
+        return send_wilddump_request(o, o->netlink_fd, o->dump_seq, 0, RTM_GETLINK);
+    }
+    else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV4_ADDR) {
+        o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV4_ADDR;
+        return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET, RTM_GETADDR);
+    }
+    else if (o->dump_queue & NCDIFMONITOR_WATCH_IPV6_ADDR) {
+        o->dump_queue &= ~NCDIFMONITOR_WATCH_IPV6_ADDR;
+        return send_wilddump_request(o, o->netlink_fd, o->dump_seq, AF_INET6, RTM_GETADDR);
+    }
+    
+    ASSERT(0)
+    return 0;
+}
+
 void netlink_fd_handler (NCDInterfaceMonitor *o, int events)
 {
     DebugObject_Access(&o->d_obj);
     ASSERT(o->buf_left == -1)
+    ASSERT(o->have_bfd)
     
     // read from netlink fd
-    int len = read(o->netlink_fd, o->buf, sizeof(o->buf));
+    int len = read(o->netlink_fd, o->buf.buf, sizeof(o->buf));
     if (len < 0) {
         BLog(BLOG_ERROR, "read failed");
-        return;
+        goto fail;
     }
     
     // stop receiving fd events
     BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, 0);
     
     // set buffer
-    o->buf_nh = (struct nlmsghdr *)o->buf;
+    o->buf_nh = &o->buf.nlh;
     o->buf_left = len;
     
     // process buffer
     process_buffer(o);
     return;
+    
+fail:
+    report_error(o);
 }
 
 void process_buffer (NCDInterfaceMonitor *o)
 {
     ASSERT(o->buf_left >= 0)
+    ASSERT(o->have_bfd)
+    
+    int done = 0;
     
     for (; NLMSG_OK(o->buf_nh, o->buf_left); o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left)) {
         if (o->buf_nh->nlmsg_type == NLMSG_DONE) {
+            done = 1;
             break;
         }
         
-        if (o->buf_nh->nlmsg_type != RTM_NEWLINK && o->buf_nh->nlmsg_type != RTM_DELLINK) {
-            continue;
-        }
-        
-        void *pl = NLMSG_DATA(o->buf_nh);
-        int pl_len = NLMSG_PAYLOAD(o->buf_nh, 0);
-        
-        if (pl_len < sizeof(struct ifinfomsg)) {
-            BLog(BLOG_ERROR, "missing infomsg");
-            continue;
-        }
-        struct ifinfomsg *im = (void *)pl;
-        
-        // parse attributes to get interface name
-        
-        char *ifname = NULL;
+        struct nlmsghdr *buf = o->buf_nh;
+        void *pl = NLMSG_DATA(buf);
+        int pl_len = NLMSG_PAYLOAD(buf, 0);
         
-        int rta_len = pl_len - sizeof(struct ifinfomsg);
+        struct NCDInterfaceMonitor_event ev;
         
-        for (struct rtattr *rta = (void *)(im + 1); RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
-            uint8_t *attr = RTA_DATA(rta);
-            int attr_len = RTA_PAYLOAD(rta);
+        switch (buf->nlmsg_type) {
+            case RTM_NEWLINK: { // not RTM_DELLINK! who knows what these mean...
+                if (pl_len < sizeof(struct ifinfomsg)) {
+                    BLog(BLOG_ERROR, "ifinfomsg too short");
+                    goto fail;
+                }
+                struct ifinfomsg *msg = pl;
+                
+                if (msg->ifi_index == o->ifindex && (o->watch_events & NCDIFMONITOR_WATCH_LINK)) {
+                    ev.event = (buf->nlmsg_type == RTM_NEWLINK && (msg->ifi_flags & IFF_RUNNING)) ? NCDIFMONITOR_EVENT_LINK_UP : NCDIFMONITOR_EVENT_LINK_DOWN;
+                    goto dispatch;
+                }
+            } break;
             
-            if (rta->rta_type == IFLA_IFNAME && attr_len > 0 && attr[attr_len - 1] == '\0') {
-                ifname = (char *)attr;
-            }
+            case RTM_NEWADDR:
+            case RTM_DELADDR: {
+                if (pl_len < sizeof(struct ifaddrmsg)) {
+                    BLog(BLOG_ERROR, "ifaddrmsg too short");
+                    goto fail;
+                }
+                struct ifaddrmsg *msg = pl;
+                
+                void *addr;
+                int addr_len;
+                if (!get_attr(IFA_ADDRESS, IFA_RTA(msg), buf->nlmsg_len - NLMSG_LENGTH(sizeof(*msg)), &addr, &addr_len)) {
+                    break;
+                }
+                
+                if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET && (o->watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR)) {
+                    if (addr_len != 4 || msg->ifa_prefixlen > 32) {
+                        BLog(BLOG_ERROR, "bad ipv4 ifaddrmsg");
+                        goto fail;
+                    }
+                    
+                    ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED;
+                    ev.u.ipv4_addr.addr.addr = ((struct in_addr *)addr)->s_addr;
+                    ev.u.ipv4_addr.addr.prefix = msg->ifa_prefixlen;
+                    goto dispatch;
+                }
+                
+                if (msg->ifa_index == o->ifindex && msg->ifa_family == AF_INET6 && (o->watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR)) {
+                    if (addr_len != 16 || msg->ifa_prefixlen > 128) {
+                        BLog(BLOG_ERROR, "bad ipv6 ifaddrmsg");
+                        goto fail;
+                    }
+                    
+                    ev.event = (buf->nlmsg_type == RTM_NEWADDR) ? NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED : NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED;
+                    memcpy(ev.u.ipv6_addr.addr.addr, ((struct in6_addr *)addr)->s6_addr, 16);
+                    ev.u.ipv6_addr.addr.prefix = msg->ifa_prefixlen;
+                    ev.u.ipv6_addr.addr.scope = msg->ifa_scope;
+                    ev.u.ipv6_addr.addr_flags = 0;
+                    if (!(msg->ifa_flags & IFA_F_PERMANENT)) {
+                        ev.u.ipv6_addr.addr_flags |= NCDIFMONITOR_ADDR_FLAG_DYNAMIC;
+                    }
+                    goto dispatch;
+                }
+            } break;
         }
         
-        if (!ifname) {
-            continue;
-        }
+        continue;
         
-        // finish this message
+    dispatch:
+        // move to next message
         o->buf_nh = NLMSG_NEXT(o->buf_nh, o->buf_left);
         
         // schedule more job
         BPending_Set(&o->more_job);
         
         // dispatch event
-        o->handler(o->user, ifname, NCDIfConfig_query(ifname));
+        o->handler(o->user, ev);
         return;
     }
     
+    if (done) {
+        if (o->dump_queue) {
+            // increment dump request sequence number
+            o->dump_seq++;
+            
+            // send next dump request
+            if (!send_next_dump_request(o)) {
+                goto fail;
+            }
+        }
+        else if (o->event_netlink_fd >= 0) {
+            // stop watching dump fd
+            BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+            o->have_bfd = 0;
+            
+            // close dump fd, make event fd current
+            close(o->netlink_fd);
+            o->netlink_fd = o->event_netlink_fd;
+            o->event_netlink_fd = -1;
+            
+            // start watching event fd
+            BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
+            if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+                BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
+                goto fail;
+            }
+            o->have_bfd = 1;
+        }
+    }
+    
     // set no buffer
     o->buf_left = -1;
     
     // continue receiving fd events
     BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
+    return;
+    
+fail:
+    report_error(o);
 }
 
 void more_job_handler (NCDInterfaceMonitor *o)
@@ -144,53 +297,94 @@ void more_job_handler (NCDInterfaceMonitor *o)
     return;
 }
 
-int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, BReactor *reactor, NCDInterfaceMonitor_handler handler, void *user)
+int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user,
+                              NCDInterfaceMonitor_handler handler,
+                              NCDInterfaceMonitor_handler_error handler_error)
 {
+    ASSERT(watch_events)
+    ASSERT((watch_events&~(NCDIFMONITOR_WATCH_LINK|NCDIFMONITOR_WATCH_IPV4_ADDR|NCDIFMONITOR_WATCH_IPV6_ADDR)) == 0)
+    ASSERT(handler)
+    ASSERT(handler_error)
+    BNetwork_Assert();
+    
     // init arguments
+    o->ifindex = ifindex;
+    o->watch_events = watch_events;
     o->reactor = reactor;
-    o->handler = handler;
     o->user = user;
+    o->handler = handler;
+    o->handler_error = handler_error;
     
-    // init netlink fd
+    // init dump netlink fd
     if ((o->netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
         BLog(BLOG_ERROR, "socket failed");
         goto fail0;
     }
-    
-    // set fd non-blocking
     if (!badvpn_set_nonblocking(o->netlink_fd)) {
         BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
         goto fail1;
     }
     
-    // bind netlink fd
+    // init event netlink fd
+    if ((o->event_netlink_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+        BLog(BLOG_ERROR, "socket failed");
+        goto fail1;
+    }
+    if (!badvpn_set_nonblocking(o->event_netlink_fd)) {
+        BLog(BLOG_ERROR, "badvpn_set_nonblocking failed");
+        goto fail2;
+    }
+    
+    // build bind address
     struct sockaddr_nl sa;
     memset(&sa, 0, sizeof(sa));
     sa.nl_family = AF_NETLINK;
-    sa.nl_groups = RTMGRP_LINK;
-    if (bind(o->netlink_fd, (void *)&sa, sizeof(sa)) < 0) {
+    sa.nl_groups = 0;
+    if (watch_events & NCDIFMONITOR_WATCH_LINK) sa.nl_groups |= RTMGRP_LINK;
+    if (watch_events & NCDIFMONITOR_WATCH_IPV4_ADDR) sa.nl_groups |= RTMGRP_IPV4_IFADDR;
+    if (watch_events & NCDIFMONITOR_WATCH_IPV6_ADDR) sa.nl_groups |= RTMGRP_IPV6_IFADDR;
+    
+    // bind event netlink fd
+    if (bind(o->event_netlink_fd, (void *)&sa, sizeof(sa)) < 0) {
         BLog(BLOG_ERROR, "bind failed");
-        goto fail1;
+        goto fail2;
     }
     
+    // set dump state
+    o->dump_queue = watch_events;
+    o->dump_seq = 0;
+    
     // init BFileDescriptor
     BFileDescriptor_Init(&o->bfd, o->netlink_fd, (BFileDescriptor_handler)netlink_fd_handler, o);
-    if (!BReactor_AddFileDescriptor(o->reactor, &o->bfd)) {
+    if (!BReactor_AddFileDescriptor(reactor, &o->bfd)) {
         BLog(BLOG_ERROR, "BReactor_AddFileDescriptor failed");
-        goto fail1;
+        goto fail2;
     }
-    BReactor_SetFileDescriptorEvents(o->reactor, &o->bfd, BREACTOR_READ);
+    o->have_bfd = 1;
     
     // set nothing in buffer
     o->buf_left = -1;
     
     // init more job
-    BPending_Init(&o->more_job, BReactor_PendingGroup(o->reactor), (BPending_handler)more_job_handler, o);
+    BPending_Init(&o->more_job, BReactor_PendingGroup(reactor), (BPending_handler)more_job_handler, o);
     
-    DebugObject_Init(&o->d_obj);
+    // send first dump request
+    if (!send_next_dump_request(o)) {
+        goto fail3;
+    }
+    
+    // wait for reading fd
+    BReactor_SetFileDescriptorEvents(reactor, &o->bfd, BREACTOR_READ);
     
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(reactor));
+    DebugObject_Init(&o->d_obj);
     return 1;
     
+fail3:
+    BPending_Free(&o->more_job);
+    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+fail2:
+    close(o->event_netlink_fd);
 fail1:
     close(o->netlink_fd);
 fail0:
@@ -200,20 +394,30 @@ fail0:
 void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o)
 {
     DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
     
     // free more job
     BPending_Free(&o->more_job);
     
     // free BFileDescriptor
-    BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    if (o->have_bfd) {
+        BReactor_RemoveFileDescriptor(o->reactor, &o->bfd);
+    }
+    
+    // close event fd, in case we're still dumping
+    if (o->event_netlink_fd >= 0) {
+        close(o->event_netlink_fd);
+    }
     
-    // close netlink fd
+    // close fd
     close(o->netlink_fd);
 }
 
 void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o)
 {
     DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->have_bfd)
     
     if (o->buf_left >= 0) {
         BPending_Unset(&o->more_job);
@@ -225,6 +429,8 @@ void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o)
 void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o)
 {
     DebugObject_Access(&o->d_obj);
+    DebugError_AssertNoError(&o->d_err);
+    ASSERT(o->have_bfd)
     
     if (o->buf_left >= 0) {
         BPending_Set(&o->more_job);

+ 102 - 6
ncd/NCDInterfaceMonitor.h

@@ -34,29 +34,125 @@
 #include <linux/netlink.h>
 
 #include <misc/debug.h>
+#include <misc/ipaddr.h>
+#include <misc/ipaddr6.h>
+#include <misc/debugerror.h>
 #include <base/DebugObject.h>
 #include <system/BReactor.h>
-#include <base/BPending.h>
-#include <ncd/NCDIfConfig.h>
+#include <system/BNetwork.h>
 
-typedef void (*NCDInterfaceMonitor_handler) (void *user, const char *ifname, int if_flags);
+#define NCDIFMONITOR_WATCH_LINK (1 << 0)
+#define NCDIFMONITOR_WATCH_IPV4_ADDR (1 << 1)
+#define NCDIFMONITOR_WATCH_IPV6_ADDR (1 << 2)
 
+#define NCDIFMONITOR_EVENT_LINK_UP 1
+#define NCDIFMONITOR_EVENT_LINK_DOWN 2
+#define NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED 3
+#define NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED 4
+#define NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED 5
+#define NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED 6
+
+#define NCDIFMONITOR_ADDR_FLAG_DYNAMIC (1 << 0)
+
+struct NCDInterfaceMonitor_event {
+    int event;
+    union {
+        struct {
+            struct ipv4_ifaddr addr;
+        } ipv4_addr;
+        struct {
+            struct ipv6_ifaddr addr;
+            int addr_flags;
+        } ipv6_addr;
+    } u;
+};
+
+/**
+ * Handler called to report an interface event.
+ * Note that the event reporter does not keep any interface state, and as such may
+ * report redundant events. You should therefore handle events in an idempotent
+ * fashion.
+ * 
+ * @param event.event event type. One of:
+ *        - NCDIFMONITOR_EVENT_LINK_UP, NCDIFMONITOR_EVENT_LINK_DOWN,
+ *        - NCDIFMONITOR_EVENT_IPV4_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV4_ADDR_REMOVED,
+ *        - NCDIFMONITOR_EVENT_IPV6_ADDR_ADDED, NCDIFMONITOR_EVENT_IPV6_ADDR_REMOVED.
+ *        Only events that correspont to enabled watch flags are reported.
+ * @param event.ipv4_addr.addr the IPv4 address and prefix length
+ * @param event.ipv6_addr.addr the IPv6 address, prefix length and scope
+ * @param event.ipv6_addr.addr_flags IPv6 address flags. Valid flags:
+ *        - NCDIFMONITOR_ADDR_FLAG_DYNAMIC - this address was assigned dynamically (NDP)
+ */
+typedef void (*NCDInterfaceMonitor_handler) (void *user, struct NCDInterfaceMonitor_event event);
+
+/**
+ * Handler called when an error occurs.
+ * The event reporter must be freed from within the job context of this
+ * handler, and no other operations must be performed.
+ */
+typedef void (*NCDInterfaceMonitor_handler_error) (void *user);
+
+/**
+ * Watches for network interface events using a Linux rtnetlink socket.
+ */
 typedef struct {
+    int ifindex;
+    int watch_events;
     BReactor *reactor;
-    NCDInterfaceMonitor_handler handler;
     void *user;
+    NCDInterfaceMonitor_handler handler;
+    NCDInterfaceMonitor_handler_error handler_error;
     int netlink_fd;
+    int event_netlink_fd;
+    int dump_queue;
+    uint32_t dump_seq;
     BFileDescriptor bfd;
-    uint8_t buf[4096];
+    int have_bfd;
+    union {
+        uint8_t buf[4096];
+        struct nlmsghdr nlh;
+    } buf;
     struct nlmsghdr *buf_nh;
     int buf_left;
     BPending more_job;
+    DebugError d_err;
     DebugObject d_obj;
 } NCDInterfaceMonitor;
 
-int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, BReactor *reactor, NCDInterfaceMonitor_handler handler, void *user) WARN_UNUSED;
+/**
+ * Initializes the event reporter.
+ * The reporter is not paused initially.
+ * {@link BNetwork_GlobalInit} must have been done.
+ * 
+ * @param ifindex index of network interface to report events for
+ * @param watch_events mask specifying what kind of events to report. Valid flags are
+ *        NCDIFMONITOR_WATCH_LINK, NCDIFMONITOR_WATCH_IPV4_ADDR, NCDIFMONITOR_WATCH_IPV6_ADDR.
+ *        At least one flag must be provided.
+ * @param reactor reactor we live in
+ * @param user argument to handlers
+ * @param handler handler to report interface events to
+ * @param handler_error error handler
+ * @return 1 on success, 0 on failure
+ */
+int NCDInterfaceMonitor_Init (NCDInterfaceMonitor *o, int ifindex, int watch_events, BReactor *reactor, void *user,
+                              NCDInterfaceMonitor_handler handler,
+                              NCDInterfaceMonitor_handler_error handler_error) WARN_UNUSED;
+
+/**
+ * Frees the event reporter.
+ */
 void NCDInterfaceMonitor_Free (NCDInterfaceMonitor *o);
+
+/**
+ * Pauses event reporting.
+ * This operation is idempotent.
+ */
 void NCDInterfaceMonitor_Pause (NCDInterfaceMonitor *o);
+
+/**
+ * Resumes event reporting.
+ * This operation is idempotent.
+ */
 void NCDInterfaceMonitor_Continue (NCDInterfaceMonitor *o);
 
 #endif

+ 33 - 21
ncd/modules/net_backend_waitlink.c

@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <misc/get_iface_info.h>
 #include <ncd/NCDModule.h>
 #include <ncd/NCDIfConfig.h>
 #include <ncd/NCDInterfaceMonitor.h>
@@ -46,19 +47,18 @@
 
 struct instance {
     NCDModuleInst *i;
-    const char *ifname;
     NCDInterfaceMonitor monitor;
     int up;
 };
 
-static void monitor_handler (struct instance *o, const char *ifname, int if_flags)
+static void instance_free (struct instance *o);
+
+static void monitor_handler (struct instance *o, struct NCDInterfaceMonitor_event event)
 {
-    if (strcmp(ifname, o->ifname)) {
-        return;
-    }
+    ASSERT(event.event == NCDIFMONITOR_EVENT_LINK_UP || event.event == NCDIFMONITOR_EVENT_LINK_DOWN)
     
     int was_up = o->up;
-    o->up = !!(if_flags & NCDIFCONFIG_FLAG_RUNNING);
+    o->up = (event.event == NCDIFMONITOR_EVENT_LINK_UP);
     
     if (o->up && !was_up) {
         NCDModuleInst_Backend_Up(o->i);
@@ -68,6 +68,14 @@ static void monitor_handler (struct instance *o, const char *ifname, int if_flag
     }
 }
 
+static void monitor_handler_error (struct instance *o)
+{
+    ModuleLog(o->i, BLOG_ERROR, "monitor error");
+    
+    NCDModuleInst_Backend_SetError(o->i);
+    instance_free(o);
+}
+
 static void func_new (NCDModuleInst *i)
 {
     // allocate instance
@@ -76,10 +84,8 @@ static void func_new (NCDModuleInst *i)
         ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
         goto fail0;
     }
-    NCDModuleInst_Backend_SetUser(i, o);
-    
-    // init arguments
     o->i = i;
+    NCDModuleInst_Backend_SetUser(i, o);
     
     // check arguments
     NCDValue *arg;
@@ -91,22 +97,23 @@ static void func_new (NCDModuleInst *i)
         ModuleLog(o->i, BLOG_ERROR, "wrong type");
         goto fail1;
     }
-    o->ifname = NCDValue_StringValue(arg);
+    char *ifname = NCDValue_StringValue(arg);
     
-    // init monitor
-    if (!NCDInterfaceMonitor_Init(&o->monitor, o->i->params->reactor, (NCDInterfaceMonitor_handler)monitor_handler, o)) {
-        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
+    // get interface index
+    int ifindex;
+    if (!get_iface_info(ifname, NULL, NULL, &ifindex)) {
+        ModuleLog(o->i, BLOG_ERROR, "failed to get interface index");
         goto fail1;
     }
     
-    // query initial state
-    o->up = !!(NCDIfConfig_query(o->ifname) & NCDIFCONFIG_FLAG_RUNNING);
-    
-    // signal up if needed
-    if (o->up) {
-        NCDModuleInst_Backend_Up(o->i);
+    // init monitor
+    if (!NCDInterfaceMonitor_Init(&o->monitor, ifindex, NCDIFMONITOR_WATCH_LINK, i->params->reactor, o, (NCDInterfaceMonitor_handler)monitor_handler, (NCDInterfaceMonitor_handler_error)monitor_handler_error)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
+        goto fail1;
     }
     
+    // set not up
+    o->up = 0;
     return;
     
 fail1:
@@ -116,9 +123,8 @@ fail0:
     NCDModuleInst_Backend_Dead(i);
 }
 
-static void func_die (void *vo)
+static void instance_free (struct instance *o)
 {
-    struct instance *o = vo;
     NCDModuleInst *i = o->i;
     
     // free monitor
@@ -130,6 +136,12 @@ static void func_die (void *vo)
     NCDModuleInst_Backend_Dead(i);
 }
 
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    instance_free(o);
+}
+
 static const struct NCDModule modules[] = {
     {
         .type = "net.backend.waitlink",