[netcf-devel] [PATCH 2/3] Use netlink to get interface IP addresses.

Laine Stump laine at laine.org
Thu Oct 22 13:43:23 UTC 2009


While the SIOCGIFADDR ioctl can only get the first IPv4 address on an
interface, netlink reports all addresses, both IPv4 and IPv6 on the
interface.

The old function that uses the ioctl has been left in place, renamed,
to make the diffs simpler. It will be removed in the next patch.
---
 src/drv_initscripts.c |   12 +--
 src/dutil.c           |  201 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/dutil.h           |   17 ++++-
 src/netcf.h           |    3 +-
 4 files changed, 223 insertions(+), 10 deletions(-)

diff --git a/src/drv_initscripts.c b/src/drv_initscripts.c
index f72f17b..6d03baa 100644
--- a/src/drv_initscripts.c
+++ b/src/drv_initscripts.c
@@ -494,9 +494,12 @@ int drv_init(struct netcf *ncf) {
     ncf->driver->ioctl_fd = init_ioctl_fd(ncf);
     if (ncf->driver->ioctl_fd < 0)
         goto error;
+    if (netlink_init(ncf) < 0)
+        goto error;
     return 0;
 
  error:
+    netlink_close(ncf);
     if (ncf->driver->ioctl_fd >= 0)
         close(ncf->driver->ioctl_fd);
     FREE(ncf->driver->augeas_xfm_tables);
@@ -508,6 +511,7 @@ void drv_close(struct netcf *ncf) {
     xsltFreeStylesheet(ncf->driver->get);
     xsltFreeStylesheet(ncf->driver->put);
     xmlRelaxNGFree(ncf->driver->rng);
+    netlink_close(ncf);
     if (ncf->driver->ioctl_fd >= 0)
         close(ncf->driver->ioctl_fd);
     aug_close(ncf->driver->augeas);
@@ -765,8 +769,6 @@ char *drv_xml_state(struct netcf_if *nif) {
     xmlNodePtr root;
     xmlAttrPtr prop;
     const char *type;
-    unsigned int ipv4;
-    int prefix;
 
     ncf = nif->ncf;
 
@@ -789,11 +791,7 @@ char *drv_xml_state(struct netcf_if *nif) {
     /* get the current IP address and prefix, and add both to the
      * document.
      */
-    ipv4 = if_ipv4_address(ncf, nif->name);
-    ERR_BAIL(ncf);
-    prefix = if_ipv4_prefix(ncf, nif->name);
-    ERR_BAIL(ncf);
-    add_state_to_xml_doc(ncf_xml, ncf, ipv4, prefix);
+    add_state_to_xml_doc(nif, ncf_xml);
     ERR_BAIL(ncf);
 
     r = xsltSaveResultToString((xmlChar **)&result, &result_len,
diff --git a/src/dutil.c b/src/dutil.c
index 9ae80bf..d12d656 100644
--- a/src/dutil.c
+++ b/src/dutil.c
@@ -46,6 +46,11 @@
 #include "netcf.h"
 #include "dutil.h"
 
+#include <netlink/socket.h>
+#include <netlink/cache.h>
+#include <netlink/route/addr.h>
+#include <netlink/route/link.h>
+
 #include <libxml/parser.h>
 #include <libxml/relaxng.h>
 #include <libxml/tree.h>
@@ -447,6 +452,51 @@ error:
     return -1;
 }
 
+int netlink_init(struct netcf *ncf) {
+
+    ncf->driver->nl_sock = nl_handle_alloc();
+    if (ncf->driver->nl_sock == NULL)
+        goto error;
+    if (nl_connect(ncf->driver->nl_sock, NETLINK_ROUTE) < 0) {
+        goto error;
+    }
+
+    ncf->driver->link_cache = rtnl_link_alloc_cache(ncf->driver->nl_sock);
+    if (ncf->driver->link_cache == NULL) {
+        goto error;
+    }
+    nl_cache_mngt_provide(ncf->driver->link_cache);
+
+    ncf->driver->addr_cache = rtnl_addr_alloc_cache(ncf->driver->nl_sock);
+    if (ncf->driver->addr_cache == NULL) {
+        goto error;
+    }
+    nl_cache_mngt_provide(ncf->driver->addr_cache);
+
+    return 0;
+
+error:
+    netlink_close(ncf);
+    return -1;
+}
+
+int netlink_close(struct netcf *ncf) {
+
+    if (ncf->driver->addr_cache) {
+        nl_cache_free(ncf->driver->addr_cache);
+        ncf->driver->addr_cache = NULL;
+    }
+    if (ncf->driver->link_cache) {
+        nl_cache_free(ncf->driver->link_cache);
+        ncf->driver->link_cache = NULL;
+    }
+    if (ncf->driver->nl_sock) {
+        nl_close(ncf->driver->nl_sock);
+        ncf->driver->nl_sock = NULL;
+    }
+    return 0;
+}
+
 int if_is_active(struct netcf *ncf, const char *intf) {
     struct ifreq ifr;
 
@@ -609,7 +659,7 @@ int dutil_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml) {
 /* Given an xml document that follows interface.rng, add the IP
  * address and prefix under protocol/ip
  */
-void add_state_to_xml_doc(xmlDocPtr doc, struct netcf *ncf ATTRIBUTE_UNUSED,
+void add_ipv4_state_to_xml_doc(xmlDocPtr doc, struct netcf *ncf ATTRIBUTE_UNUSED,
                           unsigned int ipv4, int prefix) {
 
     xmlNodePtr root = NULL, proto = NULL, ip = NULL, cur;
@@ -685,6 +735,155 @@ error:
     return;
 }
 
+/* Data that needs to be preserved between calls to the libnl iterator
+ * callback.
+ */
+struct nl_callback_data {
+    xmlDocPtr doc;
+    xmlNodePtr root;
+    xmlNodePtr protov4;
+    xmlNodePtr protov6;
+    struct netcf_if *nif;
+};
+
+/* add all ip addresses for the given interface to the xml document
+*/
+static void _add_ips_cb(struct nl_object *obj, void *arg) {
+    struct nl_callback_data *cb_data = arg;
+    struct rtnl_addr *addr = (struct rtnl_addr *)obj;
+    struct netcf *ncf = cb_data->nif->ncf;
+
+    struct nl_addr *local_addr;
+    int family, prefix;
+    const char *family_str;
+    char ip_str[48];
+    char prefix_str[16];
+    xmlNodePtr *proto, ip_node, cur;
+    xmlAttrPtr prop = NULL;
+
+    local_addr = rtnl_addr_get_local(addr);
+    family = nl_addr_get_family(local_addr);
+    switch (family) {
+    case AF_INET:
+        family_str = "ipv4";
+        proto = &cb_data->protov4;
+        break;
+    case AF_INET6:
+        family_str = "ipv6";
+        proto = &cb_data->protov6;
+        break;
+
+    default:
+        /* Nothing that interests us in this entry */
+        return;
+    }
+
+    inet_ntop(family, nl_addr_get_binary_addr(local_addr),
+              ip_str, sizeof(ip_str));
+    prefix = nl_addr_get_prefixlen(local_addr);
+
+    if (*proto == NULL) {
+        /* We haven't dont anything with this proto yet. Search for an
+         * existing node.
+         */
+        for (cur = cb_data->root->children; cur != NULL; cur = cur->next) {
+            if ((cur->type == XML_ELEMENT_NODE) &&
+                xmlStrEqual(cur->name, BAD_CAST "protocol")) {
+                xmlChar *node_family = xmlGetProp(cur, BAD_CAST "family");
+                if (node_family != NULL) {
+                    if (xmlStrEqual(node_family, BAD_CAST family_str))
+                        *proto = cur;
+                    xmlFree(node_family);
+                    if (*proto != NULL) {
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    if (*proto == NULL) {
+        /* No node exists for this protocol family. Create one.
+         */
+        *proto = xmlNewDocNode(cb_data->doc, NULL, BAD_CAST "protocol", NULL);
+        ERR_NOMEM(*proto == NULL, ncf);
+
+        cur = xmlAddChild(cb_data->root, *proto);
+        if (cur == NULL) {
+            xmlFreeNode(*proto);
+            *proto = NULL;
+            report_error(ncf, NETCF_ENOMEM, NULL);
+            goto error;
+        }
+        prop = xmlSetProp(*proto, BAD_CAST "family", BAD_CAST family_str);
+        ERR_NOMEM(prop == NULL, ncf);
+
+    }
+
+    /* Create a new ip node for this address/prefix, and set the
+     * properties
+     */
+    ip_node = xmlNewDocNode(cb_data->doc, NULL, BAD_CAST "ip", NULL);
+    ERR_NOMEM((ip_node == NULL), ncf);
+    cur = xmlAddChild(*proto, ip_node);
+    if (cur == NULL) {
+        xmlFreeNode(ip_node);
+        report_error(ncf, NETCF_ENOMEM, NULL);
+        goto error;
+    }
+    prop = xmlSetProp(ip_node, BAD_CAST "address", BAD_CAST ip_str);
+    ERR_NOMEM((prop == NULL), ncf);
+    snprintf(prefix_str, sizeof(prefix_str), "%d", prefix);
+    prop = xmlSetProp(ip_node, BAD_CAST "prefix", BAD_CAST prefix_str);
+    ERR_NOMEM((prop == NULL), ncf);
+
+error:
+    return;
+}
+
+void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc) {
+
+    struct nl_callback_data cb_data = { doc, NULL, NULL, NULL, nif };
+    struct rtnl_addr *filter_addr = NULL;
+    int ifindex, code;
+
+    cb_data.root = xmlDocGetRootElement(doc);
+    ERR_THROW((cb_data.root == NULL), nif->ncf, EINTERNAL,
+              "failed to get document root element");
+    ERR_THROW(!xmlStrEqual(cb_data.root->name, BAD_CAST "interface"),
+              nif->ncf, EINTERNAL, "root document is not an interface");
+
+    /* Build an rtnl_addr with the interface name set. This is used by
+     * the iterator to filter the contents of the address cache.
+     */
+    filter_addr = rtnl_addr_alloc();
+    ERR_NOMEM((filter_addr == NULL), nif->ncf);
+
+    code = nl_cache_refill(nif->ncf->driver->nl_sock,
+                           nif->ncf->driver->link_cache);
+    ERR_THROW((code < 0), nif->ncf, ENETLINK,
+              "failed to refill interface index cache");
+    code = nl_cache_refill(nif->ncf->driver->nl_sock,
+                           nif->ncf->driver->addr_cache);
+    ERR_THROW((code < 0), nif->ncf, ENETLINK,
+              "failed to refill interface address cache");
+
+    ifindex = rtnl_link_name2i(nif->ncf->driver->link_cache, nif->name);
+    ERR_THROW((ifindex == RTNL_LINK_NOT_FOUND), nif->ncf, ENETLINK,
+              "Could find ifindex for interface `%s`", nif->name);
+    rtnl_addr_set_ifindex(filter_addr, ifindex);
+
+printf("found index %d for %s\n", ifindex, nif->name);
+
+    nl_cache_foreach_filter(nif->ncf->driver->addr_cache,
+                            OBJ_CAST(filter_addr), _add_ips_cb,
+                            &cb_data);
+error:
+    if (filter_addr)
+        rtnl_addr_put(filter_addr);
+    return;
+}
+
 /*
  * Bringing interfaces up/down
  */
diff --git a/src/dutil.h b/src/dutil.h
index 74d0023..f664a05 100644
--- a/src/dutil.h
+++ b/src/dutil.h
@@ -25,6 +25,7 @@
 
 #include <libxml/relaxng.h>
 #include <libxslt/xsltInternals.h>
+#include <netlink/netlink.h>
 
 struct driver {
     struct augeas     *augeas;
@@ -32,6 +33,9 @@ struct driver {
     xsltStylesheetPtr  get;
     xmlRelaxNGPtr      rng;
     int                ioctl_fd;
+    struct nl_handle  *nl_sock;
+    struct nl_cache   *link_cache;
+    struct nl_cache   *addr_cache;
     unsigned int       load_augeas : 1;
     unsigned int       copy_augeas_xfm : 1;
     unsigned int       augeas_xfm_num_tables;
@@ -109,6 +113,12 @@ char *xml_prop(xmlNodePtr node, const char *name);
 /* Get a file descriptor to a ioctl socket */
 int init_ioctl_fd(struct netcf *ncf);
 
+/* setup the netlink socket */
+int netlink_init(struct netcf *ncf);
+
+/*shutdown the netlink socket and release its resources */
+int netlink_close(struct netcf *ncf);
+
 /* Check if the interface INTF is up using an ioctl call */
 int if_is_active(struct netcf *ncf, const char *intf);
 
@@ -138,7 +148,12 @@ int dutil_get_aug(struct netcf *ncf, const char *ncf_xml, char **aug_xml);
 int dutil_put_aug(struct netcf *ncf, const char *aug_xml, char **ncf_xml);
 
 /* add the given state (currently IP + netmask) to the interface's xml document */
-void add_state_to_xml_doc(xmlDocPtr doc, struct netcf *ncf, unsigned int ipv4, int prefix);
+void add_ipv4_state_to_xml_doc(xmlDocPtr doc, struct netcf *ncf, unsigned int ipv4, int prefix);
+
+/* Add the state of the interface (currently all addresses + netmasks)
+ * to its xml document.
+ */
+void add_state_to_xml_doc(struct netcf_if *nif, xmlDocPtr doc);
 
 /* Run the program PROG with the single argument ARG */
 void run1(struct netcf *ncf, const char *prog, const char *arg);
diff --git a/src/netcf.h b/src/netcf.h
index 4219cc1..a8619f5 100644
--- a/src/netcf.h
+++ b/src/netcf.h
@@ -52,7 +52,8 @@ typedef enum {
                           * used by other data structures */
     NETCF_EXSLTFAILED,   /* XSLT transformation failed */
     NETCF_EFILE,         /* Some file access failed */
-    NETCF_EIOCTL         /* An ioctl call failed */
+    NETCF_EIOCTL,        /* An ioctl call failed */
+    NETCF_ENETLINK       /* something related to the netlink socket failed */
 } netcf_errcode_t;
 
 
-- 
1.6.5.15.gc274d



More information about the netcf-devel mailing list