Initial libmnl porting attempt
authorAndreas Henriksson <andreas@fatal.se>
Mon, 10 Jun 2013 20:41:21 +0000 (22:41 +0200)
committerMarc Fournier <marc.fournier@camptocamp.com>
Thu, 18 Jul 2013 10:04:23 +0000 (12:04 +0200)
src/netlink.c

index 39536d9..40b12e1 100644 (file)
 # include <linux/pkt_sched.h>
 #endif
 
-#if HAVE_LIBNETLINK_H
-# include <libnetlink.h>
-#elif HAVE_IPROUTE_LIBNETLINK_H
-# include <iproute/libnetlink.h>
-#elif HAVE_LINUX_LIBNETLINK_H
-# include <linux/libnetlink.h>
-#endif
+#include <time.h>
+#include <libmnl/libmnl.h>
+//#include <linux/if.h>
+//#include <linux/if_link.h>
 
 typedef struct ir_ignorelist_s
 {
@@ -54,7 +51,7 @@ typedef struct ir_ignorelist_s
 static int ir_ignorelist_invert = 1;
 static ir_ignorelist_t *ir_ignorelist_head = NULL;
 
-static struct rtnl_handle rth;
+static struct mnl_socket *nl;
 
 static char **iflist = NULL;
 static size_t iflist_len = 0;
@@ -204,46 +201,8 @@ static void submit_two (const char *dev, const char *type,
   plugin_dispatch_values (&vl);
 } /* void submit_two */
 
-static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void __attribute__((unused)) *args)
+static int update_iflist(struct ifinfomsg *msg)
 {
-  struct ifinfomsg *msg;
-  int msg_len;
-  struct rtattr *attrs[IFLA_MAX + 1];
-  struct rtnl_link_stats *stats;
-
-  const char *dev;
-
-  if (nmh->nlmsg_type != RTM_NEWLINK)
-  {
-    ERROR ("netlink plugin: link_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
-  }
-
-  msg = NLMSG_DATA (nmh);
-
-  msg_len = nmh->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg));
-  if (msg_len < 0)
-  {
-    ERROR ("netlink plugin: link_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
-  }
-
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, IFLA_MAX, IFLA_RTA (msg), msg_len) != 0)
-  {
-    ERROR ("netlink plugin: link_filter: parse_rtattr failed.");
-    return (-1);
-  }
-
-  if (attrs[IFLA_IFNAME] == NULL)
-  {
-    ERROR ("netlink plugin: link_filter: attrs[IFLA_IFNAME] == NULL");
-    return (-1);
-  }
-  dev = RTA_DATA (attrs[IFLA_IFNAME]);
-
   /* Update the `iflist'. It's used to know which interfaces exist and query
    * them later for qdiscs and classes. */
   if ((msg->ifi_index >= 0) && ((size_t) msg->ifi_index >= iflist_len))
@@ -253,7 +212,7 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     temp = (char **) realloc (iflist, (msg->ifi_index + 1) * sizeof (char *));
     if (temp == NULL)
     {
-      ERROR ("netlink plugin: link_filter: realloc failed.");
+      ERROR ("netlink plugin: update_iflist: realloc failed.");
       return (-1);
     }
 
@@ -269,12 +228,13 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     iflist[msg->ifi_index] = strdup (dev);
   }
 
-  if (attrs[IFLA_STATS] == NULL)
-  {
-    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
-    return (0);
-  }
-  stats = RTA_DATA (attrs[IFLA_STATS]);
+  return (0);
+}
+
+
+static void check_ignorelist_and_submit(const char *dev,
+    struct rtnl_link_stats *stats)
+{
 
   if (check_ignorelist (dev, "interface", NULL) == 0)
   {
@@ -311,158 +271,248 @@ static int link_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
     DEBUG ("netlink plugin: Ignoring %s/if_detail.", dev);
   }
 
-  return (0);
-} /* int link_filter */
+}
 
-static int qos_filter (const struct sockaddr_nl __attribute__((unused)) *sa,
-    struct nlmsghdr *nmh, void *args)
+static int link_filter_cb (const struct nlmsghdr *nlh,
+    void __attribute__((unused)) *args)
 {
-  struct tcmsg *msg;
-  int msg_len;
-  struct rtattr *attrs[TCA_MAX + 1];
+  struct ifinfomsg *ifm = mnl_nlmsg_get_payload (nlh); 
+  struct nlattr *attr;
+  struct rtnl_link_stats *stats = NULL;
+  const char *dev = NULL;
 
-  int wanted_ifindex = *((int *) args);
+  if (nlh->nlmsg_type != RTM_NEWLINK)
+  {
+    ERROR ("netlink plugin: link_filter_cb: Don't know how to handle type %i.",
+       nlh->nlmsg_type);
+    return MNL_CB_ERROR;
+  }
 
-  const char *dev;
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_IFNAME)
+      continue;
 
-  /* char *type_instance; */
-  char *tc_type;
-  char tc_inst[DATA_MAX_NAME_LEN];
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_IFNAME mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
 
-  if (nmh->nlmsg_type == RTM_NEWQDISC)
-    tc_type = "qdisc";
-  else if (nmh->nlmsg_type == RTM_NEWTCLASS)
-    tc_type = "class";
-  else if (nmh->nlmsg_type == RTM_NEWTFILTER)
-    tc_type = "filter";
-  else
+    dev = mnl_attr_get_str (attr);
+    if (update_iflist (ifm) < 0)
+      return MNL_CB_ERROR;
+    break;
+  }
+
+  if (dev == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: Don't know how to handle type %i.",
-       nmh->nlmsg_type);
-    return (-1);
+    ERROR ("netlink plugin: link_filter_cb: dev == NULL");
+    return MNL_CB_ERROR;
   }
 
-  msg = NLMSG_DATA (nmh);
+  mnl_attr_for_each (attr, nlh, sizeof (*ifm))
+  {
+    if (mnl_attr_get_type (attr) != IFLA_STATS)
+      continue;
 
-  msg_len = nmh->nlmsg_len - sizeof (struct tcmsg);
-  if (msg_len < 0)
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof(*stats)) < 0)
+    {
+      ERROR ("netlink plugin: link_filter_cb: IFLA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    stats = mnl_attr_get_payload(attr);
+
+    check_ignorelist_and_submit(dev, stats);
+    break;
+  }
+
+  if (stats == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: msg_len = %i < 0;", msg_len);
-    return (-1);
+    DEBUG ("netlink plugin: link_filter: No statistics for interface %s.", dev);
+    return MNL_CB_OK;
+  }
+
+  return MNL_CB_OK;
+} /* int link_filter_cb */
+
+static int qos_attr_cb (const struct nlattr *attr, void *data)
+{
+  struct gnet_stats_basic *bs = *(struct gnet_stats_basic **)data;
+
+  /* skip unsupported attribute in user-space */ 
+  if (mnl_attr_type_valid(attr, TCA_STATS_MAX) < 0)
+    return MNL_CB_OK;
+
+  if (mnl_attr_get_type(attr) == TCA_STATS_BASIC)
+  {
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*bs)) < 0)
+    {
+      ERROR ("netlink plugin: qos_attr_cb: TCA_STATS_BASIC mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    bs = mnl_attr_get_payload(attr);
+    return MNL_CB_STOP;
   }
 
-  if (msg->tcm_ifindex != wanted_ifindex)
+  return MNL_CB_OK;
+} /* qos_attr_cb */
+
+static int qos_filter_cb (constr struct nlmsghdr *nlh, void *args)
+{
+  struct tcmsg *tm = mnl_nlmsg_get_payload(nlh);
+  int wanted_ifindex = *((int *) args);
+
+  const char *dev;
+  const char *kind = NULL;
+  struct gnet_stats_basic *bs = NULL;
+  struct tc_stats *ts = NULL;
+
+  /* char *type_instance; */
+  char *tc_type;
+  char tc_inst[DATA_MAX_NAME_LEN];
+
+  if (tm->tcm_ifindex != wanted_ifindex)
   {
-    DEBUG ("netlink plugin: qos_filter: Got %s for interface #%i, "
+    DEBUG ("netlink plugin: qos_filter_cb: Got %s for interface #%i, "
        "but expected #%i.",
        tc_type, msg->tcm_ifindex, wanted_ifindex);
-    return (0);
+    return MNL_CB_OK;
+  }
+
+  if (nlh->nlmsg_type == RTM_NEWQDISC)
+    tc_type = "qdisc";
+  else if (nlh->nlmsg_type == RTM_NEWTCLASS)
+    tc_type = "class";
+  else if (nlh->nlmsg_type == RTM_NEWTFILTER)
+    tc_type = "filter";
+  else
+  {
+    ERROR ("netlink plugin: qos_filter_cb: Don't know how to handle type %i.",
+       nlh->nlmsg_type);
+    return MNL_CB_ERROR;
   }
 
-  if ((msg->tcm_ifindex >= 0)
-      && ((size_t) msg->tcm_ifindex >= iflist_len))
+  if ((tm->tcm_ifindex >= 0)
+      && ((size_t) tm->tcm_ifindex >= iflist_len))
   {
-    ERROR ("netlink plugin: qos_filter: msg->tcm_ifindex = %i "
+    ERROR ("netlink plugin: qos_filter_cb: tm->tcm_ifindex = %i "
        ">= iflist_len = %zu",
-       msg->tcm_ifindex, iflist_len);
-    return (-1);
+       tm->tcm_ifindex, iflist_len);
+    return MNL_CB_ERROR;
   }
 
   dev = iflist[msg->tcm_ifindex];
   if (dev == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: iflist[%i] == NULL",
+    ERROR ("netlink plugin: qos_filter_cb: iflist[%i] == NULL",
        msg->tcm_ifindex);
-    return (-1);
+    return MNL_CB_ERROR;
   }
 
-  memset (attrs, '\0', sizeof (attrs));
-  if (parse_rtattr (attrs, TCA_MAX, TCA_RTA (msg), msg_len) != 0)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    ERROR ("netlink plugin: qos_filter: parse_rtattr failed.");
-    return (-1);
+    if (mnl_attr_get_type(attr) != TCA_KIND)
+      continue;
+
+    if (mnl_attr_validate (attr, MNL_TYPE_STRING) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_KIND mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
+
+    kind = mnl_attr_get_str(attr);
+    break;
   }
 
-  if (attrs[TCA_KIND] == NULL)
+  if (kind == NULL)
   {
-    ERROR ("netlink plugin: qos_filter: attrs[TCA_KIND] == NULL");
+    ERROR ("netlink plugin: qos_filter_cb: kind == NULL");
     return (-1);
   }
 
   { /* The the ID */
     uint32_t numberic_id;
 
-    numberic_id = msg->tcm_handle;
+    numberic_id = tm->tcm_handle;
     if (strcmp (tc_type, "filter") == 0)
-      numberic_id = msg->tcm_parent;
+      numberic_id = tm->tcm_parent;
 
     ssnprintf (tc_inst, sizeof (tc_inst), "%s-%x:%x",
-       (const char *) RTA_DATA (attrs[TCA_KIND]),
+       kind,
        numberic_id >> 16,
        numberic_id & 0x0000FFFF);
   }
 
-  DEBUG ("netlink plugin: qos_filter: got %s for %s (%i).",
+  DEBUG ("netlink plugin: qos_filter_cb: got %s for %s (%i).",
       tc_type, dev, msg->tcm_ifindex);
-  
+
   if (check_ignorelist (dev, tc_type, tc_inst))
-    return (0);
+    return MNL_CB_OK;
 
 #if HAVE_TCA_STATS2
-  if (attrs[TCA_STATS2])
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
   {
-    struct rtattr *attrs_stats[TCA_STATS_MAX + 1];
-
-    memset (attrs_stats, '\0', sizeof (attrs_stats));
-    parse_rtattr_nested (attrs_stats, TCA_STATS_MAX, attrs[TCA_STATS2]);
+    if (mnl_attr_get_type(attr) != TCA_STATS2)
+      continue;
 
-    if (attrs_stats[TCA_STATS_BASIC])
+    if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
     {
-      struct gnet_stats_basic bs;
-      char type_instance[DATA_MAX_NAME_LEN];
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS2 mnl_attr_validate failed.");
+      return MNL_CB_ERROR;
+    }
 
-      ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
-         tc_type, tc_inst);
+    mnl_attr_parse_nested(attr, qos_attr_cb, &bs);
 
-      memset (&bs, '\0', sizeof (bs));
-      memcpy (&bs, RTA_DATA (attrs_stats[TCA_STATS_BASIC]),
-         MIN (RTA_PAYLOAD (attrs_stats[TCA_STATS_BASIC]), sizeof(bs)));
+    break;
+  }
 
-      submit_one (dev, "ipt_bytes", type_instance, bs.bytes);
-      submit_one (dev, "ipt_packets", type_instance, bs.packets);
-    }
+  if (bs != NULL)
+  {
+    char type_instance[DATA_MAX_NAME_LEN];
+
+    ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+        tc_type, tc_inst);
+
+    submit_one (dev, "ipt_bytes", type_instance, bs->bytes);
+    submit_one (dev, "ipt_packets", type_instance, bs->packets);
   }
 #endif /* TCA_STATS2 */
-#if HAVE_TCA_STATS && HAVE_TCA_STATS2
-  else
-#endif
+
 #if HAVE_TCA_STATS
-  if (attrs[TCA_STATS] != NULL)
+  mnl_attr_for_each (attr, nlh, sizeof (*tm))
+  {
+    if (mnl_attr_get_type(attr) != TCA_STATS)
+      continue;
+
+    if (mnl_attr_validate2 (attr, MNL_TYPE_UNSPEC, sizeof (*ts)) < 0)
+    {
+      ERROR ("netlink plugin: qos_filter_cb: TCA_STATS mnl_attr_validate2 failed.");
+      return MNL_CB_ERROR;
+    }
+    ts = mnl_attr_get_payload(attr);
+    break;
+  }
+
+  if (bs == NULL && ts != NULL)
   {
-    struct tc_stats ts;
     char type_instance[DATA_MAX_NAME_LEN];
 
     ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
        tc_type, tc_inst);
 
-    memset(&ts, '\0', sizeof (ts));
-    memcpy(&ts, RTA_DATA (attrs[TCA_STATS]),
-       MIN (RTA_PAYLOAD (attrs[TCA_STATS]), sizeof (ts)));
-
-    submit_one (dev, "ipt_bytes", type_instance, ts.bytes);
-    submit_one (dev, "ipt_packets", type_instance, ts.packets);
+    submit_one (dev, "ipt_bytes", type_instance, ts->bytes);
+    submit_one (dev, "ipt_packets", type_instance, ts->packets);
   }
 #endif /* TCA_STATS */
-#if HAVE_TCA_STATS || HAVE_TCA_STATS2
-  else
+
+#if !(HAVE_TCA_STATS && HAVE_TCA_STATS2)
+  DEBUG ("netlink plugin: qos_filter_cb: Have neither TCA_STATS2 nor "
+      "TCA_STATS.");
 #endif
-  {
-    DEBUG ("netlink plugin: qos_filter: Have neither TCA_STATS2 nor "
-       "TCA_STATS.");
-  }
 
-  return (0);
-} /* int qos_filter */
+  return MNL_CB_OK;
+} /* int qos_filter_cb */
 
 static int ir_config (const char *key, const char *value)
 {
@@ -541,11 +591,16 @@ static int ir_config (const char *key, const char *value)
 
 static int ir_init (void)
 {
-  memset (&rth, '\0', sizeof (rth));
+  nl = mnl_socket_open(NETLINK_ROUTE);
+  if (nl == NULL)
+  {
+    ERROR ("netlink plugin: ir_init: mnl_socket_open failed.");
+    return (-1);
+  }
 
-  if (rtnl_open (&rth, 0) != 0)
+  if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
   {
-    ERROR ("netlink plugin: ir_init: rtnl_open failed.");
+    ERROR ("netlink plugin: ir_init: mnl_socket_bind failed.");
     return (-1);
   }
 
@@ -554,35 +609,50 @@ static int ir_init (void)
 
 static int ir_read (void)
 {
-  struct tcmsg tm;
-  int ifindex;
+  char buf[MNL_SOCKET_BUFFER_SIZE];
+  struct nlmsghdr *nlh;
+  struct rtgenmsg *rt;
+  int ret;
+  unsigned int seq, portid;
 
   static const int type_id[] = { RTM_GETQDISC, RTM_GETTCLASS, RTM_GETTFILTER };
   static const char *type_name[] = { "qdisc", "class", "filter" };
 
-  if (rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) < 0)
+  portid = mnl_socket_get_portid(nl);
+
+  nlh = mnl_nlmsg_put_header (buf);
+  nlh->nlmsg_type = RTM_GETLINK;
+  nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+  nlh->nlmsg_seq = seq = time (NULL);
+  rt = mnl_nlmsg_put_extra_header (nlh, sizeof (*rt));
+  rt->rtgen_family = AF_PACKET;
+
+  if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
   {
     ERROR ("netlink plugin: ir_read: rtnl_wilddump_request failed.");
     return (-1);
   }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-  if (rtnl_dump_filter (&rth, link_filter, /* arg1 = */ NULL,
-       NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-  if (rtnl_dump_filter (&rth, link_filter, /* arg = */ NULL) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+  ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+  while (ret > 0)
   {
-    ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
+    ret = mnl_cb_run (buf, ret, seq, portid, link_filter_cb, NULL);
+    if (ret <= MNL_CB_STOP)
+      break;
+    ret = mnl_socket_recvfrom (nl, buf, sizeof(buf));
+  }
+  if (ret < 0)
+  {
+    ERROR ("netlink plugin: ir_read: mnl_socket_recvfrom failed.");
     return (-1);
   }
 
-  /* `link_filter' will update `iflist' which is used here to iterate over all
-   * interfaces. */
+  /* `link_filter_cb' will update `iflist' which is used here to iterate
+   * over all interfaces. */
   for (ifindex = 0; (size_t) ifindex < iflist_len; ifindex++)
   {
+    struct tcmsg *tm;
+    int ifindex;
     size_t type_index;
 
     if (iflist[ifindex] == NULL)
@@ -600,28 +670,34 @@ static int ir_read (void)
       DEBUG ("netlink plugin: ir_read: querying %s from %s (%i).",
          type_name[type_index], iflist[ifindex], ifindex);
 
-      memset (&tm, '\0', sizeof (tm));
-      tm.tcm_family = AF_UNSPEC;
-      tm.tcm_ifindex = ifindex;
+      nlh = mnl_nlmsg_put_header (buf);
+      nlh->nlmsg_type = type_id[type_index];
+      nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+      nlh->nlmsg_seq = seq = time (NULL);
+      tm = mnl_nlmsg_put_extra_header (nlh, sizeof (*tm));
+      tm->tcm_family = AF_PACKET;
+      tm->tcm_ifindex = ifindex;
 
-      if (rtnl_dump_request (&rth, type_id[type_index], &tm, sizeof (tm)) < 0)
+      if (mnl_socket_sendto (nl, nlh, nlh->nlmsg_len) < 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_request failed.");
+        ERROR ("netlink plugin: ir_read: mnl_socket_sendto failed.");
        continue;
       }
 
-#ifdef RTNL_DUMP_FILTER_FIVE_ARGS
-      if (rtnl_dump_filter (&rth, qos_filter, (void *) &ifindex,
-           NULL, NULL) != 0)
-#elif defined(RTNL_DUMP_FILTER_THREE_ARGS)
-      if (rtnl_dump_filter (&rth, qos_filter, /* arg = */ &ifindex) != 0)
-#else
-#error "Failed to determine the number of arguments to 'rtnl_dump_filter'!"
-#endif
+      ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
+      while (ret > 0)
       {
-       ERROR ("netlink plugin: ir_read: rtnl_dump_filter failed.");
-       continue;
+        ret = mnl_cb_run (buf, ret, seq, portid, qos_filter_cb, &ifindex);
+       if (ret <= MNL_CB_STOP)
+          break;
+        ret = mnl_socket_recvfrom (nl, buf, sizeof (buf));
       }
+      if (ret < 0)
+      {
+        ERROR ("netlink plugin: ir_read:mnl_socket_recvfrom failed.");
+        continue;
+      }
+
     } /* for (type_index) */
   } /* for (if_index) */
 
@@ -630,12 +706,12 @@ static int ir_read (void)
 
 static int ir_shutdown (void)
 {
-  if ((rth.fd != 0) || (rth.seq != 0) || (rth.dump != 0))
+  if (nl)
   {
-    rtnl_close(&rth);
-    memset (&rth, '\0', sizeof (rth));
+    mnl_socket_close(nl);
+    nl = NULL;
   }
-  
+
   return (0);
 } /* int ir_shutdown */