[SSSD] [PATCH] libnl integration improvements

Jakub Hrozek jhrozek at redhat.com
Tue Sep 6 10:50:28 UTC 2011


[PATCH 1/3] Change libnl monitor callback to only signal going online
The feature to signal going offline was not used and would probably
never be used, because it is much safer to rely on online actions to time
out. Moreover, it would make implementing the new features more complex.

[PATCH 2/3] Discard carrier messages from non-ethernet devices
IFF_LOWER_UP has no meaning for wireless interfaces, it can mean that an
association has been made with an access point, but it does not mean
that an addressing has been completed. 

Using IFF_LOWER_UP meant that we were generating spurious "go online"
messages.

This patch discards "carrier up" messages from interfaces that do not
look like ethernet devices, be it wifi, PPP, WIMAX, ...

[PATCH 3/3] Subscribe to netlink route and addr messages
https://fedorahosted.org/sssd/ticket/955

In addition to carrier up messages, also subscribe to any messages
describing that an address has been added or removed or routing table
changed.
-------------- next part --------------
From 463249faa0b19c8eeb0fab72155613e61e14d497 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sat, 3 Sep 2011 18:50:35 +0200
Subject: [PATCH 1/3] Change libnl monitor callback to only signal going
 online

This feature was not used and would probably never be used, because it
is much safer to rely on online actions to time out. Moreover, it would
make implementing the new features more complex.
---
 src/monitor/monitor.c         |    8 +++-----
 src/monitor/monitor.h         |    7 +------
 src/monitor/monitor_netlink.c |    4 +---
 3 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 0e16b78..4cd1e99 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -154,15 +154,13 @@ static int mark_service_as_started(struct mt_svc *svc);
 
 static int monitor_cleanup(void);
 
-static void network_status_change_cb(enum network_change state,
-                                     void *cb_data)
+static void network_status_change_cb(void *cb_data)
 {
     struct mt_svc *iter;
     struct mt_ctx *ctx = (struct mt_ctx *) cb_data;
 
-    if (state != NL_ROUTE_UP) return;
-
-    DEBUG(9, ("A new route has appeared, signaling providers to reset offline status\n"));
+    DEBUG(SSSDBG_TRACE_INTERNAL, ("A networking status change detected "
+          "signaling providers to reset offline status\n"));
     for (iter = ctx->svc_list; iter; iter = iter->next) {
         /* Don't signal services, only providers */
         if (iter->provider) {
diff --git a/src/monitor/monitor.h b/src/monitor/monitor.h
index 7323442..ab23e00 100644
--- a/src/monitor/monitor.h
+++ b/src/monitor/monitor.h
@@ -43,12 +43,7 @@ int monitor_process_init(struct mt_ctx *ctx,
 /* from monitor_netlink.c */
 struct netlink_ctx;
 
-enum network_change {
-    NL_ROUTE_UP,
-    NL_ROUTE_DOWN
-};
-
-typedef void (*network_change_cb)(enum network_change, void *);
+typedef void (*network_change_cb)(void *);
 
 int setup_netlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
                   network_change_cb change_cb, void *cb_data,
diff --git a/src/monitor/monitor_netlink.c b/src/monitor/monitor_netlink.c
index 33be7c8..cbf717f 100644
--- a/src/monitor/monitor_netlink.c
+++ b/src/monitor/monitor_netlink.c
@@ -250,9 +250,7 @@ static void link_msg_handler(struct nl_object *obj, void *arg)
 
     /* IFF_LOWER_UP is the indicator of carrier status */
     if (flags & IFF_LOWER_UP) {
-        ctx->change_cb(NL_ROUTE_UP, ctx->cb_data);
-    } else {
-        ctx->change_cb(NL_ROUTE_DOWN, ctx->cb_data);
+        ctx->change_cb(ctx->cb_data);
     }
 }
 
-- 
1.7.6

-------------- next part --------------
From b813d4968610bceec9e006989654dab15781ad3e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 23 Aug 2011 16:59:04 +0200
Subject: [PATCH 2/3] Discard carrier messages from non-ethernet devices

IFF_LOWER_UP has no meaning for wireless interfaces, it can mean that an
association has been made with an access point, but it does not mean
that an addressing has been completed.

This patch discards "carrier up" messages from interfaces that do not
look like ethernet devices.
---
 src/monitor/monitor_netlink.c |  158 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 155 insertions(+), 3 deletions(-)

diff --git a/src/monitor/monitor_netlink.c b/src/monitor/monitor_netlink.c
index cbf717f..737266b 100644
--- a/src/monitor/monitor_netlink.c
+++ b/src/monitor/monitor_netlink.c
@@ -24,6 +24,7 @@
 #include <talloc.h>
 #include <tevent.h>
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #define __USE_GNU /* needed for struct ucred */
 #include <sys/socket.h>
 #include <unistd.h>
@@ -36,6 +37,7 @@
 #include <linux/if.h>
 #include <linux/socket.h>
 #include <linux/rtnetlink.h>
+#include <linux/wireless.h>
 #include <netlink/netlink.h>
 #include <netlink/utils.h>
 #include <netlink/route/addr.h>
@@ -55,6 +57,14 @@
 #define nlw_object_match nl_object_match_filter
 #define NLW_OK NL_OK
 
+#define SYSFS_IFACE_TEMPLATE "/sys/class/net/%s/type"
+#define SYSFS_IFACE_PATH_MAX (21+IFNAMSIZ)
+
+#define PHY_80211_SUBDIR   "phy80211"
+#define SYSFS_SUBDIR_PATH_MAX (SYSFS_IFACE_PATH_MAX+9)
+
+#define BUFSIZE 8
+
 struct netlink_ctx {
 #ifdef HAVE_LIBNL
     struct nl_handle *nlh;
@@ -76,6 +86,141 @@ static int netlink_ctx_destructor(void *ptr)
 }
 
 /*******************************************************************
+ *                      Utility functions
+ *******************************************************************/
+
+static bool has_wireless_extension(const char *ifname)
+{
+    int s;
+    errno_t ret;
+    struct iwreq iwr;
+
+    s = socket(PF_INET, SOCK_DGRAM, 0);
+    if (s == -1) {
+        ret = errno;
+        DEBUG(2, ("Could not open socket: [%d] %s\n", ret, strerror(ret)));
+        return false;
+    }
+
+    strncpy(iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ);
+    /* Does the interface support a wireless extension? */
+    ret = ioctl(s, SIOCGIWNAME, &iwr);
+    close(s);
+
+    return ret == 0;
+}
+
+static bool has_ethernet_encapsulation(const char *sysfs_path)
+{
+    errno_t ret;
+    int fd = -1;
+    char buf[BUFSIZE];
+
+    fd = open(sysfs_path, O_RDONLY);
+    if (fd == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not open sysfs file %s: [%d] %s\n",
+              sysfs_path, ret, strerror(ret)));
+        return false;
+    }
+
+    memset(buf, 0, BUFSIZE);
+    while ((ret = read(fd, buf, BUFSIZE)) != 0) {
+        if (ret == -1) {
+            ret = errno;
+            if (ret == EINTR || ret == EAGAIN) {
+                continue;
+            }
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("read failed [%d][%s].\n", ret, strerror(ret)));
+            close(fd);
+            return false;
+        }
+    }
+    close(fd);
+
+    return strncmp(buf, "1\n", BUFSIZE) == 0;
+}
+
+static bool has_phy_80211_subdir(const char *sysfs_path)
+{
+    char phy80211_path[SYSFS_IFACE_PATH_MAX];
+    struct stat statbuf;
+    errno_t ret;
+
+    ret = snprintf(phy80211_path, SYSFS_SUBDIR_PATH_MAX,
+                   "%s/%s", sysfs_path, PHY_80211_SUBDIR);
+    if (ret < 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("snprintf failed"));
+        return false;
+    } else if (ret >= SYSFS_SUBDIR_PATH_MAX) {
+        DEBUG(SSSDBG_OP_FAILURE, ("path too long?!?!\n"));
+        return false;
+    }
+
+    errno = 0;
+    ret = stat(phy80211_path, &statbuf);
+    if (ret == -1) {
+        ret = errno;
+        if (ret == ENOENT || ret == ENOTDIR) {
+            DEBUG(SSSDBG_TRACE_LIBS, ("No %s directory in sysfs, probably "
+                  "not a wireless interface\n", PHY_80211_SUBDIR));
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE, ("stat failed: [%d] %s\n",
+                  ret, strerror(ret)));
+        }
+        return false;
+    }
+
+    if (statbuf.st_mode & S_IFDIR) {
+        DEBUG(SSSDBG_TRACE_LIBS, ("Directory %s found in sysfs, looks like "
+              "a wireless iface\n", PHY_80211_SUBDIR));
+        return true;
+    }
+
+    return false;
+}
+
+static bool discard_iff_up(const char *ifname)
+{
+    char path[SYSFS_IFACE_PATH_MAX];
+    errno_t ret;
+
+    /* This catches most of the new 80211 drivers */
+    if (has_wireless_extension(ifname)) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("%s has a wireless extension\n", ifname));
+        return true;
+    }
+
+    ret = snprintf(path, SYSFS_IFACE_PATH_MAX, SYSFS_IFACE_TEMPLATE, ifname);
+    if (ret < 0) {
+        DEBUG(SSSDBG_OP_FAILURE, ("snprintf failed"));
+        return false;
+    } else if (ret >= SYSFS_IFACE_PATH_MAX) {
+        DEBUG(SSSDBG_OP_FAILURE, ("path too long?!?!\n"));
+        return false;
+    }
+
+    /* This will filter PPP and such. Both wired and wireless
+     * interfaces have the encapsulation. */
+    if (!has_ethernet_encapsulation(path)) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("%s does not have ethernet encapsulation, "
+              "filtering out\n", ifname));
+        return true;
+    }
+
+    /* This captures old WEXT drivers, the new mac8011 would
+     * be caught by the ioctl check */
+    if (has_phy_80211_subdir(path)) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("%s has a 802_11 subdir, filtering out\n",
+              ifname));
+        return true;
+    }
+
+    return false;
+}
+
+/*******************************************************************
  * Wrappers for different capabilities of different libnl versions
  *******************************************************************/
 
@@ -237,8 +382,10 @@ static void link_msg_handler(struct nl_object *obj, void *arg)
 {
     struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
     struct rtnl_link *link_obj;
-    int flags;
+    unsigned int flags;
+    char str_flags[512];
     int ifidx;
+    const char *ifname;
 
     if (!nlw_is_link_object(obj)) return;
 
@@ -246,10 +393,15 @@ static void link_msg_handler(struct nl_object *obj, void *arg)
     flags = rtnl_link_get_flags(link_obj);
     ifidx = rtnl_link_get_ifindex(link_obj);
 
-    DEBUG(8, ("netlink link message: iface idx %d flags 0x%X\n", ifidx, flags));
+    rtnl_link_flags2str(flags, str_flags, 512);
+
+    ifname = rtnl_link_get_name(link_obj);
+    DEBUG(SSSDBG_TRACE_LIBS, ("netlink link message: iface idx %u (%s) "
+          "flags 0x%X (%s)\n", ifidx, ifname, flags, str_flags));
 
     /* IFF_LOWER_UP is the indicator of carrier status */
-    if (flags & IFF_LOWER_UP) {
+    if ((flags & IFF_RUNNING) && (flags & IFF_LOWER_UP) &&
+         !discard_iff_up(ifname)) {
         ctx->change_cb(ctx->cb_data);
     }
 }
-- 
1.7.6

-------------- next part --------------
From bbd46b4eceeaa3fb6d8476b4598337f72a245744 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 4 Sep 2011 21:15:43 +0200
Subject: [PATCH 3/3] Subscribe to netlink route and addr messages

https://fedorahosted.org/sssd/ticket/955

In addition to carrier up messages, also subscribe to any messages
describing that an address has been added or removed or routing table
changed.
---
 src/monitor/monitor_netlink.c |  249 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 244 insertions(+), 5 deletions(-)

diff --git a/src/monitor/monitor_netlink.c b/src/monitor/monitor_netlink.c
index 737266b..7bc90ef 100644
--- a/src/monitor/monitor_netlink.c
+++ b/src/monitor/monitor_netlink.c
@@ -27,6 +27,8 @@
 #include <sys/ioctl.h>
 #define __USE_GNU /* needed for struct ucred */
 #include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <unistd.h>
 #include <fcntl.h>
 
@@ -43,6 +45,7 @@
 #include <netlink/route/addr.h>
 #include <netlink/route/link.h>
 #include <netlink/route/rtnl.h>
+#include <netlink/route/route.h>
 #include <netlink/handlers.h>
 #endif
 
@@ -65,6 +68,13 @@
 
 #define BUFSIZE 8
 
+enum nlw_msg_type {
+    NLW_LINK,
+    NLW_ROUTE,
+    NLW_ADDR,
+    NLW_OTHER
+};
+
 struct netlink_ctx {
 #ifdef HAVE_LIBNL
     struct nl_handle *nlh;
@@ -220,6 +230,26 @@ static bool discard_iff_up(const char *ifname)
     return false;
 }
 
+static void nladdr_to_string(struct nl_addr *nl, char *buf, size_t bufsize)
+{
+    int addr_family;
+    void *addr;
+
+    addr_family = nl_addr_get_family(nl);
+    if (addr_family != AF_INET && addr_family != AF_INET6) {
+        strncpy(buf, "unknown", bufsize);
+        return;
+    }
+
+    addr = nl_addr_get_binary_addr(nl);
+    if (!addr) return;
+
+    if (inet_ntop(addr_family, addr, buf, bufsize) == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("inet_ntop failed\n"));
+        snprintf(buf, bufsize, "unknown");
+    }
+}
+
 /*******************************************************************
  * Wrappers for different capabilities of different libnl versions
  *******************************************************************/
@@ -257,6 +287,48 @@ static bool nlw_accept_message(struct nl_handle *nlh,
     return accept_msg;
 }
 
+static bool nlw_is_addr_object(struct nl_object *obj)
+{
+    bool is_addr_object = true;
+    struct rtnl_addr *filter;
+
+    filter = rtnl_addr_alloc();
+    if (!filter) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Allocation error!\n"));
+        is_addr_object = false;
+    }
+
+    /* Ensure it's an addr object */
+    if (!nlw_object_match(obj, OBJ_CAST(filter))) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Not an addr object\n"));
+        is_addr_object = false;
+    }
+
+    rtnl_addr_put(filter);
+    return is_addr_object;
+}
+
+static bool nlw_is_route_object(struct nl_object *obj)
+{
+    bool is_route_object = true;
+    struct rtnl_route *filter;
+
+    filter = rtnl_route_alloc();
+    if (!filter) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Allocation error!\n"));
+        is_route_object = false;
+    }
+
+    /* Ensure it's a route object */
+    if (!nlw_object_match(obj, OBJ_CAST(filter))) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Not a route object\n"));
+        is_route_object = false;
+    }
+
+    rtnl_route_put(filter);
+    return is_route_object;
+}
+
 static bool nlw_is_link_object(struct nl_object *obj)
 {
     bool is_link_object = true;
@@ -287,19 +359,18 @@ static int nlw_enable_passcred(struct nl_handle *nlh)
 #endif
 }
 
-static int nlw_group_subscribe(struct nl_handle *nlh)
+static int nlw_group_subscribe(struct nl_handle *nlh, int group)
 {
     int ret;
 
 #ifdef HAVE_NL_SOCKET_ADD_MEMBERSHIP
-    ret = nl_socket_add_membership(nlh, RTNLGRP_LINK);
+    ret = nl_socket_add_membership(nlh, group);
     if (ret != 0) {
         DEBUG(1, ("Unable to add membership: %s\n", nl_geterror()));
         return ret;
     }
 #else
      int nlfd = nlw_get_fd(nlh);
-     int group = RTNLGRP_LINK;
 
      errno = 0;
      ret = setsockopt(nlfd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
@@ -314,6 +385,19 @@ static int nlw_group_subscribe(struct nl_handle *nlh)
      return 0;
 }
 
+static int nlw_groups_subscribe(struct nl_handle *nlh, int *groups)
+{
+    int ret;
+    int i;
+
+    for (i=0; groups[i]; i++) {
+        ret = nlw_group_subscribe(nlh, groups[i]);
+        if (ret != EOK) return ret;
+    }
+
+    return EOK;
+}
+
 /*******************************************************************
  * Callbacks for validating and receiving messages
  *******************************************************************/
@@ -343,10 +427,50 @@ static int event_msg_recv(struct nl_msg *msg, void *arg)
 }
 
 static void link_msg_handler(struct nl_object *obj, void *arg);
+static void route_msg_handler(struct nl_object *obj, void *arg);
+static void addr_msg_handler(struct nl_object *obj, void *arg);
+
+static enum nlw_msg_type message_type(struct nlmsghdr *hdr)
+{
+    DEBUG(SSSDBG_FUNC_DATA, ("netlink Message type: %d\n", hdr->nlmsg_type));
+    switch (hdr->nlmsg_type) {
+        /* network interface added */
+        case RTM_NEWLINK:
+            return NLW_LINK;
+        /* routing table changed */
+        case RTM_NEWROUTE:
+        case RTM_DELROUTE:
+            return NLW_ROUTE;
+        /* IP address added or deleted */
+        case RTM_NEWADDR:
+        case RTM_DELADDR:
+            return NLW_ADDR;
+        /* Something else happened, but we don't care (typically RTM_GET* ) */
+        default:
+            return NLW_OTHER;
+    }
+
+    return NLW_OTHER;
+}
 
 static int event_msg_ready(struct nl_msg *msg, void *arg)
 {
-    nl_msg_parse(msg, &link_msg_handler, arg);
+    struct nlmsghdr *hdr = nlmsg_hdr(msg);
+
+    switch (message_type(hdr)) {
+        case NLW_LINK:
+            nl_msg_parse(msg, &link_msg_handler, arg);
+            break;
+        case NLW_ROUTE:
+            nl_msg_parse(msg, &route_msg_handler, arg);
+            break;
+        case NLW_ADDR:
+            nl_msg_parse(msg, &addr_msg_handler, arg);
+            break;
+        default:
+            return EOK; /* Don't care */
+    }
+
     return NLW_OK;
 }
 
@@ -378,6 +502,119 @@ static int nlw_set_callbacks(struct nl_handle *nlh, void *data)
     return ret;
 }
 
+static void route_msg_debug_print(struct rtnl_route *route_obj)
+{
+    int prefixlen;
+    char buf[INET6_ADDRSTRLEN];
+    struct nl_addr *nl;
+
+    nl = rtnl_route_get_dst(route_obj);
+    if (nl) {
+        nladdr_to_string(nl, buf, INET6_ADDRSTRLEN);
+        prefixlen = nl_addr_get_prefixlen(nl);
+    } else {
+        strncpy(buf, "unknown", INET6_ADDRSTRLEN);
+        prefixlen = 0;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("route idx %d flags %#X family %d addr %s/%d\n",
+          rtnl_route_get_oif(route_obj), rtnl_route_get_flags(route_obj),
+          rtnl_route_get_family(route_obj), buf, prefixlen));
+}
+
+/*
+ * If a bridge interface is configured it sets up a timer to requery for
+ * multicast group memberships periodically. We need to discard such
+ * messages.
+ */
+static bool route_is_multicast(struct rtnl_route *route_obj)
+{
+    struct nl_addr *nl;
+    struct in6_addr *addr6 = NULL;
+    struct in_addr *addr4 = NULL;
+
+    nl = rtnl_route_get_dst(route_obj);
+    if (!nl) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("A route with no destination?\n"));
+        return false;
+    }
+
+    if (nl_addr_get_family(nl) == AF_INET) {
+        addr4 = nl_addr_get_binary_addr(nl);
+        if (!addr4) {
+            return false;
+        }
+
+        return IN_MULTICAST(addr4->s_addr);
+    } else if (nl_addr_get_family(nl) == AF_INET6) {
+        addr6 = nl_addr_get_binary_addr(nl);
+        if (!addr6) {
+            return false;
+        }
+
+        return IN6_IS_ADDR_MULTICAST(addr6);
+    }
+
+    DEBUG(SSSDBG_MINOR_FAILURE, ("Unknown route address family\n"));
+    return false;
+}
+
+static void route_msg_handler(struct nl_object *obj, void *arg)
+{
+    struct rtnl_route *route_obj;
+    struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
+
+    if (!nlw_is_route_object(obj)) return;
+
+    route_obj = (struct rtnl_route *) obj;
+
+    if (route_is_multicast(route_obj)) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              ("Discarding multicast route message\n"));
+        return;
+    }
+
+    if (debug_level & SSSDBG_TRACE_LIBS) {
+        route_msg_debug_print(route_obj);
+    }
+
+    ctx->change_cb(ctx->cb_data);
+}
+
+static void addr_msg_debug_print(struct rtnl_addr *addr_obj)
+{
+    unsigned int flags;
+    char str_flags[512];
+    int ifidx;
+    struct nl_addr *local_addr;
+    char buf[INET6_ADDRSTRLEN];
+
+    flags = rtnl_addr_get_flags(addr_obj);
+    ifidx = rtnl_addr_get_ifindex(addr_obj);
+    local_addr = rtnl_addr_get_local(addr_obj);
+
+    rtnl_addr_flags2str(flags, str_flags, 512);
+    nladdr_to_string(local_addr, buf, INET6_ADDRSTRLEN);
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("netlink addr message: iface idx %u "
+          "addr %s flags 0x%X (%s)\n", ifidx, buf, flags, str_flags));
+}
+
+static void addr_msg_handler(struct nl_object *obj, void *arg)
+{
+    struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
+    struct rtnl_addr *addr_obj;
+
+    if (!nlw_is_addr_object(obj)) return;
+
+    addr_obj = (struct rtnl_addr *) obj;
+    if (debug_level & SSSDBG_TRACE_LIBS) {
+        addr_msg_debug_print(addr_obj);
+    }
+
+    ctx->change_cb(ctx->cb_data);
+}
+
 static void link_msg_handler(struct nl_object *obj, void *arg)
 {
     struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
@@ -436,6 +673,8 @@ int setup_netlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     int ret;
     int nlfd;
     unsigned flags;
+    int groups[] = { RTNLGRP_LINK, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE,
+                     RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0 };
 
     nlctx = talloc_zero(mem_ctx, struct netlink_ctx);
     if (!nlctx) return ENOMEM;
@@ -477,7 +716,7 @@ int setup_netlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     }
 
     /* Subscribe to the LINK group for internal carrier signals */
-    ret = nlw_group_subscribe(nlctx->nlh);
+    ret = nlw_groups_subscribe(nlctx->nlh, groups);
     if (ret != 0) {
         DEBUG(1, ("Unable to subscribe to netlink monitor\n"));
         ret = EIO;
-- 
1.7.6



More information about the sssd-devel mailing list