summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2013-01-15 22:10:09 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2013-01-30 13:41:37 -0500
commit7ff5e2746b3955e50c92ffe61bc7390e6aecbfda (patch)
tree21373cb9d72b18f0c1579e964fb1de436eb5d939
parentd33ad02be5a459a26451a1ae3c6506040894bee4 (diff)
downloadpython-ethtool-7ff5e2746b3955e50c92ffe61bc7390e6aecbfda.tar.gz
python-ethtool-7ff5e2746b3955e50c92ffe61bc7390e6aecbfda.tar.xz
python-ethtool-7ff5e2746b3955e50c92ffe61bc7390e6aecbfda.zip
Support devices with multiple IPv4 addresses
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 come 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
-rw-r--r--python-ethtool/etherinfo.c76
-rw-r--r--python-ethtool/etherinfo_obj.c131
-rw-r--r--python-ethtool/etherinfo_struct.h19
-rw-r--r--python-ethtool/ethtool.c3
-rw-r--r--python-ethtool/netlink-address.c162
-rw-r--r--setup.py3
6 files changed, 330 insertions, 64 deletions
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 b158a47..dad426b 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.
*
@@ -102,6 +137,7 @@ int _ethtool_etherinfo_init(etherinfo_py *self, PyObject *args, PyObject *kwds)
PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
{
char *attr = PyString_AsString(attr_o);
+ PyNetlinkIPv4Address *py_addr;
if( !self || !self->data ) {
PyErr_SetString(PyExc_AttributeError, "No data available");
@@ -115,13 +151,32 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
return RETURN_STRING(self->data->ethinfo->hwaddress);
} else if( strcmp(attr, "ipv4_address") == 0 ) {
get_etherinfo(self->data, NLQRY_ADDR);
- return 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);
- return 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);
- return 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 {
return PyObject_GenericGetAttr((PyObject *)self, attr_o);
}
@@ -170,19 +225,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 ) {
@@ -203,6 +260,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
@@ -276,30 +351,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.
*
*/
@@ -333,7 +392,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@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 997736c..d565e1c 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -999,6 +999,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@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'],