[PATCH] Support devices with multiple IPv4 addresses

David Malcolm dmalcolm at redhat.com
Wed Jan 16 03:14:41 UTC 2013


Add a get_ipv4_addresses() method to ethtool.etherinfo to support devices
with multiple IPv4 addresses (rhbz#759150)

Previously, get_etherinfo() made queries to NETLINK with NLQRY_ADDR, and
callback_nl_address handled responses of family AF_INET (IPv4) by writing to
fields within a struct etherinfo.

If multiple AF_INET responses came back, each overwrote the last, and the
last one won.

This patch generalizes things by moving the relevant fields:
   char *ipv4_address;                 /**< Configured IPv4 address */
   int ipv4_netmask;                   /**< Configured IPv4 netmask */
   char *ipv4_broadcast;
from (struct etherinfo) into a new Python class, currently named
PyNetlinkIPv4Address.

This object has a sane repr():
>>> ethtool.get_interfaces_info('eth1')[0].get_ipv4_addresses()
[ethtool.NetlinkIPv4Address(address='192.168.1.10', netmask=24, broadcast='192.168.1.255')]

and attributes:
>>> print [iface.address for iface in ethtool.get_interfaces_info('eth1')[0].get_ipv4_addresses()]
['192.168.1.10']
>>> print [iface.netmask for iface in ethtool.get_interfaces_info('eth1')[0].get_ipv4_addresses()]
[24]
>>> print [iface.broadcast for iface in ethtool.get_interfaces_info('eth1')[0].get_ipv4_addresses()]
['192.168.1.255']

The (struct etherinfo) then gains a new field:
   PyObject *ipv4_addresses;        /**< list of PyNetlinkIPv4Address instances */

which is created before starting the query, and populated by the callback as
responses come in.

All direct usage of the old fields (which assumed a single IPv4 address)
are changed to use the last entry in the list (if any), to mimic the old
behavior.

dump_etherinfo() and _ethtool_etherinfo_str() are changed to loop over all
of the IPv4 addresses when outputting, rather than just outputting one.

Caveats:
* the exact terminology is probably incorrect: I'm not a networking
  specialist
* the relationship between each of devices, get_interfaces_info() results,
  and addresses seems both unclear and messy to me: how changable is the
  API?

>>> ethtool.get_interfaces_info('eth1')[0].get_ipv4_addresses()
[ethtool.NetlinkIPv4Address(address='192.168.1.10', netmask=24, broadcast='192.168.1.255')]

It seems that an etherinfo object relates to a device: perhaps it should be
named as such?  But it may be too late to make this change.

Notes:
The _ethtool_etherinfo_members array within python-ethtool/etherinfo_obj.c
was broken: it defined 4 attributes of type PyObject*, to be extracted from
etherinfo_py->data, which is of a completed different type.  If these
PyMemberDef fields were ever used, Python would segfault.  Thankfully
_ethtool_etherinfo_getter() has handlers for these attributes, and gets
called first.

This is a modified version of the patch applied downstream in RHEL 6.4
within python-ethtool-0.6-3.el6:
  python-ethtool-0.6-add-get_ipv4_addresses-method.patch
ported to take account of 508ffffbb3c48eeeb11eeab2bf971180fe4e1940
---
 python-ethtool/etherinfo.c        |  76 ++++++++++++------
 python-ethtool/etherinfo_obj.c    | 131 +++++++++++++++++++++---------
 python-ethtool/etherinfo_struct.h |  19 ++++-
 python-ethtool/ethtool.c          |   3 +
 python-ethtool/netlink-address.c  | 162 ++++++++++++++++++++++++++++++++++++++
 setup.py                          |   3 +-
 6 files changed, 330 insertions(+), 64 deletions(-)
 create mode 100644 python-ethtool/netlink-address.c

diff --git a/python-ethtool/etherinfo.c b/python-ethtool/etherinfo.c
index 2cebdfb..3f2a3e6 100644
--- a/python-ethtool/etherinfo.c
+++ b/python-ethtool/etherinfo.c
@@ -92,12 +92,8 @@ void free_etherinfo(struct etherinfo *ptr)
 	if( ptr->hwaddress ) {
 		free(ptr->hwaddress);
 	}
-	if( ptr->ipv4_address ) {
-		free(ptr->ipv4_address);
-	}
-	if( ptr->ipv4_broadcast ) {
-		free(ptr->ipv4_broadcast);
-	}
+	Py_XDECREF(ptr->ipv4_addresses);
+
 	if( ptr->ipv6_addresses ) {
 		free_ipv6addresses(ptr->ipv6_addresses);
 	}
@@ -171,6 +167,36 @@ static void callback_nl_link(struct nl_object *obj, void *arg)
 	SET_STR_VALUE(ethi->hwaddress, hwaddr);
 }
 
+/**
+ * For use by callback_nl_address
+ * Returns 0 for success; -1 for error (though this is currently ignored)
+ */
+static int
+append_object_for_netlink_address(struct etherinfo *ethi,
+                                  struct nl_object *obj,
+                                  struct rtnl_addr *addr)
+{
+	PyObject *addr_obj;
+
+	assert(ethi);
+	assert(ethi->ipv4_addresses);
+	assert(addr);
+
+	addr_obj = make_python_address_from_rtnl_addr(obj, addr);
+	if (!addr_obj) {
+	  return -1;
+	}
+
+	if (-1 == PyList_Append(ethi->ipv4_addresses, addr_obj)) {
+	  Py_DECREF(addr_obj);
+	  return -1;
+	}
+
+	Py_DECREF(addr_obj);
+
+	/* Success */
+	return 0;
+}
 
 /**
  *  libnl callback function.  Does the real parsing of a record returned by NETLINK.  This function
@@ -199,17 +225,7 @@ static void callback_nl_address(struct nl_object *obj, void *arg)
 		inet_ntop(family, nl_addr_get_binary_addr(addr), (char *)&ip_str, 64);
 
 		if( family == AF_INET ) {
-			struct nl_addr *brdcst = rtnl_addr_get_broadcast((struct rtnl_addr *)obj);
-			char brdcst_str[66];
-
-			SET_STR_VALUE(ethi->ipv4_address, ip_str);
-			ethi->ipv4_netmask = rtnl_addr_get_prefixlen((struct rtnl_addr*) obj);
-
-			if( brdcst ) {
-				memset(&brdcst_str, 0, 66);
-				inet_ntop(family, nl_addr_get_binary_addr(brdcst), (char *)&brdcst_str, 64);
-				SET_STR_VALUE(ethi->ipv4_broadcast, brdcst_str);
-			}
+                        (void)append_object_for_netlink_address(ethi, obj, (struct rtnl_addr*) addr);
 		} else {
 			ethi->ipv6_addresses = etherinfo_add_ipv6(ethi->ipv6_addresses,
 								  ip_str,
@@ -244,13 +260,18 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr)
 		fprintf(fp, "MAC address: %s", ptr->hwaddress);
 	}
 	fprintf(fp, "\n");
-	if( ptr->ipv4_address ) {
-		fprintf(fp, "\tIPv4 Address: %s/%i",
-			ptr->ipv4_address, ptr->ipv4_netmask);
-		if( ptr->ipv4_broadcast ) {
-			fprintf(fp, "  -  Broadcast: %s", ptr->ipv4_broadcast);
-		}
-		fprintf(fp, "\n");
+	if( ptr->ipv4_addresses ) {
+		Py_ssize_t i;
+		for (i = 0; i < PyList_Size(ptr->ipv4_addresses); i++) {
+			PyNetlinkIPv4Address *addr = (PyNetlinkIPv4Address *)PyList_GetItem(ptr->ipv4_addresses, i);
+			fprintf(fp, "\tIPv4 Address: %s/%i",
+                                PyString_AsString(addr->ipv4_address),
+                                addr->ipv4_netmask);
+                        if( addr->ipv4_broadcast ) {
+                              fprintf(fp, "  -  Broadcast: %s", PyString_AsString(addr->ipv4_broadcast));
+                        }
+                        fprintf(fp, "\n");
+                }
 	}
 	if( ptr->ipv6_addresses ) {
 		struct ipv6address *ipv6 = ptr->ipv6_addresses;
@@ -338,6 +359,13 @@ int get_etherinfo(struct etherinfo_obj_data *data, nlQuery query)
                         ethinf->ipv6_addresses = NULL;
                 }
 
+                /* Likewise for IPv4 addresses: */
+                Py_XDECREF(ethinf->ipv4_addresses);
+                ethinf->ipv4_addresses = PyList_New(0);
+                if (!ethinf->ipv4_addresses) {
+                        return 0;
+                }
+
                 /* Retrieve all address information */
 		nl_cache_foreach_filter(addr_cache, (struct nl_object *)addr, callback_nl_address, ethinf);
 		rtnl_addr_put(addr);
diff --git a/python-ethtool/etherinfo_obj.c b/python-ethtool/etherinfo_obj.c
index cf90601..21e2d50 100644
--- a/python-ethtool/etherinfo_obj.c
+++ b/python-ethtool/etherinfo_obj.c
@@ -91,6 +91,41 @@ int _ethtool_etherinfo_init(etherinfo_py *self, PyObject *args, PyObject *kwds)
 	return 0;
 }
 
+/*
+  The old approach of having a single IPv4 address per device meant each result
+  that came in from netlink overwrote the old result.
+
+  Mimic it by returning the last entry in the list (if any).
+
+  The return value is a *borrowed reference* (or NULL)
+*/
+static PyNetlinkIPv4Address*
+get_last_address(etherinfo_py *self)
+{
+	Py_ssize_t size;
+	PyObject *list;
+
+	assert(self);
+	list = self->data->ethinfo->ipv4_addresses;
+	if (!list) {
+		return NULL;
+	}
+
+	if (!PyList_Check(list)) {
+		return NULL;
+	}
+
+	size = PyList_Size(list);
+	if (size > 0) {
+		PyObject *item = PyList_GetItem(list, size - 1);
+		if (Py_TYPE(item) == &ethtool_netlink_ipv4_address_Type) {
+			return (PyNetlinkIPv4Address*)item;
+		}
+	}
+
+	return NULL;
+}
+
 /**
  * ethtool.etherinfo function for retrieving data from a Python object.
  *
@@ -103,6 +138,7 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
 {
 	PyObject *ret;
 	char *attr = PyString_AsString(attr_o);
+	PyNetlinkIPv4Address *py_addr;
 
 	if( !self || !self->data ) {
 		PyErr_SetString(PyExc_AttributeError, "No data available");
@@ -116,13 +152,32 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
 		ret = RETURN_STRING(self->data->ethinfo->hwaddress);
 	} else if( strcmp(attr, "ipv4_address") == 0 ) {
 		get_etherinfo(self->data, NLQRY_ADDR);
-		ret = RETURN_STRING(self->data->ethinfo->ipv4_address);
+		/* For compatiblity with old approach, return last IPv4 address: */
+		py_addr = get_last_address(self);
+		if (py_addr) {
+		  if (py_addr->ipv4_address) {
+		      Py_INCREF(py_addr->ipv4_address);
+		      return py_addr->ipv4_address;
+		  }
+		}
+		Py_RETURN_NONE;
 	} else if( strcmp(attr, "ipv4_netmask") == 0 ) {
 		get_etherinfo(self->data, NLQRY_ADDR);
-		ret = PyInt_FromLong(self->data->ethinfo->ipv4_netmask);
+		py_addr = get_last_address(self);
+		if (py_addr) {
+		  return PyInt_FromLong(py_addr->ipv4_netmask);
+		}
+		return PyInt_FromLong(0);
 	} else if( strcmp(attr, "ipv4_broadcast") == 0 ) {
 		get_etherinfo(self->data, NLQRY_ADDR);
-		ret = RETURN_STRING(self->data->ethinfo->ipv4_broadcast);
+		py_addr = get_last_address(self);
+		if (py_addr) {
+		  if (py_addr->ipv4_broadcast) {
+		      Py_INCREF(py_addr->ipv4_broadcast);
+		      return py_addr->ipv4_broadcast;
+		  }
+		}
+		Py_RETURN_NONE;
 	} else {
 		ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
 	}
@@ -173,19 +228,21 @@ PyObject *_ethtool_etherinfo_str(etherinfo_py *self)
 		Py_DECREF(tmp);
 	}
 
-	if( self->data->ethinfo->ipv4_address ) {
-		PyObject *tmp = PyString_FromFormat("\tIPv4 address: %s/%i",
-						   self->data->ethinfo->ipv4_address,
-						   self->data->ethinfo->ipv4_netmask);
-		if( self->data->ethinfo->ipv4_broadcast ) {
-			PyObject *tmp2 = PyString_FromFormat("	  Broadcast: %s",
-							     self->data->ethinfo->ipv4_broadcast);
-			PyString_Concat(&tmp, tmp2);
-			Py_DECREF(tmp2);
-		}
-		PyString_Concat(&tmp, PyString_FromString("\n"));
-		PyString_Concat(&ret, tmp);
-		Py_DECREF(tmp);
+	if( self->data->ethinfo->ipv4_addresses ) {
+               Py_ssize_t i;
+               for (i = 0; i < PyList_Size(self->data->ethinfo->ipv4_addresses); i++) {
+                       PyNetlinkIPv4Address *py_addr = (PyNetlinkIPv4Address *)PyList_GetItem(self->data->ethinfo->ipv4_addresses, i);
+                       PyObject *tmp = PyString_FromFormat("\tIPv4 address: ");
+                       PyString_Concat(&tmp, py_addr->ipv4_address);
+                       PyString_ConcatAndDel(&tmp, PyString_FromFormat("/%d", py_addr->ipv4_netmask));
+                       if (py_addr->ipv4_broadcast ) {
+                                PyString_ConcatAndDel(&tmp,
+                                                      PyString_FromString("	  Broadcast: "));
+                                PyString_Concat(&tmp, py_addr->ipv4_broadcast);
+                       }
+                       PyString_ConcatAndDel(&tmp, PyString_FromString("\n"));
+                       PyString_ConcatAndDel(&ret, tmp);
+               }
 	}
 
 	if( self->data->ethinfo->ipv6_addresses ) {
@@ -206,6 +263,24 @@ PyObject *_ethtool_etherinfo_str(etherinfo_py *self)
 	return ret;
 }
 
+static PyObject *
+_ethtool_etherinfo_get_ipv4_addresses(etherinfo_py *self, PyObject *notused) {
+	PyObject *ret;
+
+	if( !self || !self->data ) {
+		PyErr_SetString(PyExc_AttributeError, "No data available");
+		return NULL;
+	}
+
+	get_etherinfo(self->data, NLQRY_ADDR);
+
+	/* Transfer ownership of reference: */
+	ret = self->data->ethinfo->ipv4_addresses;
+	self->data->ethinfo->ipv4_addresses = NULL;
+
+	return ret;
+}
+
 
 /**
  * Returns a tuple list of ethertool.etherinfo_ipv6addr objects, containing configured
@@ -279,30 +354,14 @@ PyObject * _ethtool_etherinfo_get_ipv6_addresses(etherinfo_py *self, PyObject *n
  *
  */
 static PyMethodDef _ethtool_etherinfo_methods[] = {
-	{"get_ipv6_addresses", _ethtool_etherinfo_get_ipv6_addresses, METH_NOARGS,
+	{"get_ipv4_addresses", (PyCFunction)_ethtool_etherinfo_get_ipv4_addresses, METH_NOARGS,
+	 "Retrieve configured IPv4 addresses.  Returns a list of NetlinkIP4Address objects"},
+	{"get_ipv6_addresses", (PyCFunction)_ethtool_etherinfo_get_ipv6_addresses, METH_NOARGS,
 	 "Retrieve configured IPv6 addresses.  Returns a tuple list of etherinfo_ipv6addr objects"},
 	{NULL}  /**< No methods defined */
 };
 
 /**
- * Defines all accessible object members
- *
- */
-static PyMemberDef _ethtool_etherinfo_members[] = {
-    {"device", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
-     "Device name of the interface"},
-    {"mac_address", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
-     "MAC address / hardware address of the interface"},
-    {"ipv4_address", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
-     "IPv4 address"},
-    {"ipv4_netmask", T_INT, offsetof(etherinfo_py, data), 0,
-     "IPv4 netmask in bits"},
-    {"ipv4_broadcast", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
-     "IPv4 broadcast address"},
-    {NULL}  /* End of member list */
-};
-
-/**
  * Definition of the functions a Python class/object requires.
  *
  */
@@ -336,7 +395,7 @@ PyTypeObject ethtool_etherinfoType = {
     0,		               /* tp_iter */
     0,		               /* tp_iternext */
     _ethtool_etherinfo_methods,            /* tp_methods */
-    _ethtool_etherinfo_members,            /* tp_members */
+    0,                         /* tp_members */
     0,                         /* tp_getset */
     0,                         /* tp_base */
     0,                         /* tp_dict */
diff --git a/python-ethtool/etherinfo_struct.h b/python-ethtool/etherinfo_struct.h
index f294637..bcb692d 100644
--- a/python-ethtool/etherinfo_struct.h
+++ b/python-ethtool/etherinfo_struct.h
@@ -13,6 +13,8 @@
  * General Public License for more details.
  */
 
+#include <netlink/route/addr.h>
+
 /**
  * @file   etherinfo_struct.h
  * @author David Sommerseth <dsommers at wsdsommers.usersys.redhat.com>
@@ -33,12 +35,18 @@ struct etherinfo {
 	char *device;                       /**< Device name */
 	int index;                          /**< NETLINK index reference */
 	char *hwaddress;                    /**< HW address / MAC address of device */
-	char *ipv4_address;                 /**< Configured IPv4 address */
-	int ipv4_netmask;                   /**< Configured IPv4 netmask */
-	char *ipv4_broadcast;               /**< Configured IPv4 broadcast address */
+	PyObject *ipv4_addresses;        /**< list of PyNetlinkIPv4Address instances */
 	struct ipv6address *ipv6_addresses; /**< Configured IPv6 addresses (as a pointer chain) */
 };
 
+/* Python object containing data baked from a (struct rtnl_addr) */
+typedef struct PyNetlinkIPv4Address {
+	PyObject_HEAD
+	PyObject *ipv4_address;		/**< string: Configured IPv4 address */
+	int ipv4_netmask;		/**< int: Configured IPv4 netmask */
+	PyObject *ipv4_broadcast;	/**< string: Configured IPv4 broadcast address */
+} PyNetlinkIPv4Address;
+extern PyTypeObject ethtool_netlink_ipv4_address_Type;
 
 /**
  * Pointer chain with IPv6 addresses associated with a ethernet interface.  Used
@@ -91,4 +99,9 @@ typedef struct {
  */
 #define RETURN_STRING(str) (str ? PyString_FromString(str) : (Py_INCREF(Py_None), Py_None))
 
+PyObject *
+make_python_address_from_rtnl_addr(struct nl_object *obj,
+                                   struct rtnl_addr *addr);
+
+
 #endif
diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c
index b3fc65b..244fb86 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -998,6 +998,9 @@ PyMODINIT_FUNC initethtool(void)
 	Py_INCREF(&ethtool_etherinfoIPv6Type);
 	PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)&ethtool_etherinfoIPv6Type);
 
+	if (PyType_Ready(&ethtool_netlink_ipv4_address_Type))
+		return;
+
 	// Setup constants
 	PyModule_AddIntConstant(m, "IFF_UP", IFF_UP);			/* Interface is up. */
 	PyModule_AddIntConstant(m, "IFF_BROADCAST", IFF_BROADCAST);	/* Broadcast address valid. */
diff --git a/python-ethtool/netlink-address.c b/python-ethtool/netlink-address.c
new file mode 100644
index 0000000..21976be
--- /dev/null
+++ b/python-ethtool/netlink-address.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011, 2012 Red Hat Inc.
+ *
+ * David Malcolm <dmalcolm at redhat.com>
+ *
+ * This application is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2.
+ *
+ * This application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * General Public License for more details.
+ */
+
+/* Python object corresponding to a (struct rtnl_addr) */
+#include <Python.h>
+#include "structmember.h"
+
+#include <netlink/route/rtnl.h>
+#include "etherinfo_struct.h"
+#include "etherinfo.h"
+
+/* IPv4 Addresses: */
+static PyObject *
+PyNetlinkIPv4Address_from_rtnl_addr(struct nl_object *nl_obj, struct rtnl_addr *addr)
+{
+	PyNetlinkIPv4Address *py_obj;
+	char buf[INET_ADDRSTRLEN+1];
+	struct nl_addr *brdcst;
+
+	py_obj = PyObject_New(PyNetlinkIPv4Address,
+			      &ethtool_netlink_ipv4_address_Type);
+	if (!py_obj) {
+		return NULL;
+	}
+
+	/* Set ipv4_address: */
+	memset(&buf, 0, sizeof(buf));
+	if (!inet_ntop(AF_INET, nl_addr_get_binary_addr((struct nl_addr *)addr),
+		       buf, sizeof(buf))) {
+		PyErr_SetFromErrno(PyExc_RuntimeError);
+		goto error;
+	}
+	py_obj->ipv4_address = PyString_FromString(buf);
+	if (!py_obj->ipv4_address) {
+		goto error;
+	}
+
+	/* Set ipv4_netmask: */
+	py_obj->ipv4_netmask = rtnl_addr_get_prefixlen((struct rtnl_addr*)nl_obj);
+
+	/* Set ipv4_broadcast: */
+	py_obj->ipv4_broadcast = NULL;
+	brdcst = rtnl_addr_get_broadcast((struct rtnl_addr*)nl_obj);
+	if( brdcst ) {
+		memset(&buf, 0, sizeof(buf));
+		if (!inet_ntop(AF_INET, nl_addr_get_binary_addr(brdcst),
+			       buf, sizeof(buf))) {
+			PyErr_SetFromErrno(PyExc_RuntimeError);
+			goto error;
+		}
+		py_obj->ipv4_broadcast = PyString_FromString(buf);
+		if (!py_obj->ipv4_broadcast) {
+			goto error;
+		}
+	}
+
+	return (PyObject*)py_obj;
+
+ error:
+	Py_DECREF(py_obj);
+	return NULL;
+}
+
+static void
+netlink_ipv4_address_dealloc(PyNetlinkIPv4Address *obj)
+{
+	Py_DECREF(obj->ipv4_address);
+	Py_XDECREF(obj->ipv4_broadcast);
+
+	/* We can call PyObject_Del directly rather than calling through
+	   tp_free since the type is not subtypable (Py_TPFLAGS_BASETYPE is
+	   not set): */
+	PyObject_Del(obj);
+}
+
+static PyObject*
+netlink_ipv4_address_repr(PyNetlinkIPv4Address *obj)
+{
+	PyObject *result = PyString_FromString("ethtool.NetlinkIPv4Address(address='");
+	PyString_Concat(&result, obj->ipv4_address);
+	PyString_ConcatAndDel(&result,
+			      PyString_FromFormat("', netmask=%d",
+						  obj->ipv4_netmask));
+	if (obj->ipv4_broadcast) {
+		PyString_ConcatAndDel(&result, PyString_FromString(", broadcast='"));
+		PyString_Concat(&result, obj->ipv4_broadcast);
+		PyString_ConcatAndDel(&result, PyString_FromString("'"));
+	}
+	PyString_ConcatAndDel(&result, PyString_FromString(")"));
+	return result;
+}
+
+static PyMemberDef _ethtool_netlink_ipv4_address_members[] = {
+	{"address",
+	 T_OBJECT_EX,
+	 offsetof(PyNetlinkIPv4Address, ipv4_address),
+	 0,
+	 NULL},
+	{"netmask",
+	 T_INT,
+	 offsetof(PyNetlinkIPv4Address, ipv4_netmask),
+	 0,
+	 NULL},
+	{"broadcast",
+	 T_OBJECT, /* can be NULL */
+	 offsetof(PyNetlinkIPv4Address, ipv4_broadcast),
+	 0,
+	 NULL},
+	{NULL}  /* End of member list */
+};
+
+PyTypeObject ethtool_netlink_ipv4_address_Type = {
+	PyVarObject_HEAD_INIT(0, 0)
+	.tp_name = "ethtool.NetlinkIPv4Address",
+	.tp_basicsize = sizeof(PyNetlinkIPv4Address),
+	.tp_dealloc = (destructor)netlink_ipv4_address_dealloc,
+	.tp_repr = (reprfunc)netlink_ipv4_address_repr,
+	.tp_members = _ethtool_netlink_ipv4_address_members,
+};
+
+/* Factory function, in case we want to generalize this to add IPv6 support */
+PyObject *
+make_python_address_from_rtnl_addr(struct nl_object *obj,
+				   struct rtnl_addr *addr)
+{
+	int family;
+	assert(addr);
+
+	family = nl_addr_get_family((struct nl_addr *)addr);
+
+	switch( family ) {
+
+	case AF_INET:
+		return PyNetlinkIPv4Address_from_rtnl_addr(obj, addr);
+
+		/*
+		  For now, we just support IPv4 addresses.
+		*/
+
+	default:
+		return PyErr_SetFromErrno(PyExc_RuntimeError);
+	}
+}
+
+/*
+Local variables:
+c-basic-offset: 8
+indent-tabs-mode: y
+End:
+*/
diff --git a/setup.py b/setup.py
index f081aa6..e1c3411 100644
--- a/setup.py
+++ b/setup.py
@@ -59,7 +59,8 @@ setup(name='ethtool',
                 'python-ethtool/ethtool.c',
                 'python-ethtool/etherinfo.c',
                 'python-ethtool/etherinfo_obj.c',
-                'python-ethtool/etherinfo_ipv6_obj.c'],
+                'python-ethtool/etherinfo_ipv6_obj.c',
+                'python-ethtool/netlink-address.c'],
             include_dirs = libnl['include'],
             library_dirs = libnl['libdirs'],
             libraries = libnl['libs'],
-- 
1.7.11.4




More information about the python-ethtool-devel mailing list