[netcf-devel] [PATCH 2/3] Use netlink to get interface IP addresses.
David Lutterkort
lutter at redhat.com
Thu Oct 22 16:49:18 UTC 2009
On Thu, 2009-10-22 at 09:43 -0400, Laine Stump wrote:
> 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.
A couple of issues:
> diff --git a/src/dutil.c b/src/dutil.c
> index 9ae80bf..d12d656 100644
> --- a/src/dutil.c
> +++ b/src/dutil.c
> @@ -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);
That needs to go.
> + 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/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;
When you add an entry here, also add a corresponding message to errmsgs
in netcf.c (which I think we also forgot for NETCF_EIOCTL)
David
More information about the netcf-devel
mailing list