[PATCH] Support devices with multiple IPv4 addresses
by David Malcolm
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) == ðtool_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(a)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(ðtool_etherinfoIPv6Type);
PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)ðtool_etherinfoIPv6Type);
+ if (PyType_Ready(ðtool_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(a)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,
+ ðtool_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
11 years, 3 months
[PATCH] Eliminate "ret" within _ethtool_etherinfo_getter
by David Malcolm
This variable was confusing cppcheck and potentially could
introduce a read of an uninitialized variable if the code
were carelessly modified - simplify it.
---
python-ethtool/etherinfo_obj.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/python-ethtool/etherinfo_obj.c b/python-ethtool/etherinfo_obj.c
index cf90601..b158a47 100644
--- a/python-ethtool/etherinfo_obj.c
+++ b/python-ethtool/etherinfo_obj.c
@@ -101,7 +101,6 @@ int _ethtool_etherinfo_init(etherinfo_py *self, PyObject *args, PyObject *kwds)
*/
PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
{
- PyObject *ret;
char *attr = PyString_AsString(attr_o);
if( !self || !self->data ) {
@@ -110,24 +109,22 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
}
if( strcmp(attr, "device") == 0 ) {
- ret = RETURN_STRING(self->data->ethinfo->device);
+ return RETURN_STRING(self->data->ethinfo->device);
} else if( strcmp(attr, "mac_address") == 0 ) {
get_etherinfo(self->data, NLQRY_LINK);
- ret = RETURN_STRING(self->data->ethinfo->hwaddress);
+ return 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);
+ return RETURN_STRING(self->data->ethinfo->ipv4_address);
} else if( strcmp(attr, "ipv4_netmask") == 0 ) {
get_etherinfo(self->data, NLQRY_ADDR);
- ret = PyInt_FromLong(self->data->ethinfo->ipv4_netmask);
+ return PyInt_FromLong(self->data->ethinfo->ipv4_netmask);
} else if( strcmp(attr, "ipv4_broadcast") == 0 ) {
get_etherinfo(self->data, NLQRY_ADDR);
- ret = RETURN_STRING(self->data->ethinfo->ipv4_broadcast);
+ return RETURN_STRING(self->data->ethinfo->ipv4_broadcast);
} else {
- ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
+ return PyObject_GenericGetAttr((PyObject *)self, attr_o);
}
-
- return ret;
}
/**
--
1.7.11.4
11 years, 3 months
[PATCH] Fix memory leaks in get_interfaces_info()
by David Malcolm
The first half of get_interfaces_info() potentially allocates fetch_devs
using calloc, setting fetch_devs_len to a value which may or may not be
non-zero.
In particular, given a tuple argument containing all non-strings,
allocation occurs, but fetch_devs_len is set to zero, so it's not correct
to use (fetch_devs_len > 0) as the condition for freeing the memory on the
primary exit path, as this would leak fetch_devs (introduced in
bfdcac6b16806416a6c0295fcfad5d820595d88c)
There are also two error-handling paths that fail to free it (introduced in
4f0295fca2cfd933f4b9b539d5505cb24e4d420c)
Instead of this logic, simply initialize it to NULL, and pass it to free on
every exit route of the second half of the function: free(NULL) is
guaranteed to be a no-op.
Found by Braňo Náter using the "cppcheck" static analyzer.
---
python-ethtool/ethtool.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c
index d377531..997736c 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -247,7 +247,7 @@ static PyObject *get_ipaddress(PyObject *self __unused, PyObject *args)
static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) {
PyObject *devlist = NULL, *ethinf_py = NULL;
PyObject *inargs = NULL;
- char **fetch_devs;
+ char **fetch_devs = NULL;
int i = 0, fetch_devs_len = 0;
if (!PyArg_ParseTuple(args, "|O", &inargs)) {
@@ -301,12 +301,14 @@ static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) {
objdata = calloc(1, sizeof(struct etherinfo_obj_data));
if( !objdata ) {
PyErr_SetString(PyExc_OSError, strerror(errno));
+ free(fetch_devs);
return NULL;
}
objdata->ethinfo = calloc(1, sizeof(struct etherinfo));
if( !objdata->ethinfo ) {
PyErr_SetString(PyExc_OSError, strerror(errno));
+ free(fetch_devs);
return NULL;
}
@@ -334,9 +336,8 @@ static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) {
Py_DECREF(args);
}
}
- if( fetch_devs_len > 0 ) {
- free(fetch_devs);
- }
+
+ free(fetch_devs);
return devlist;
}
--
1.7.11.4
11 years, 3 months
[PATCH] Fix bad loop condition within get_devices()
by David Malcolm
get_devices() includes this loop:
char buffer[256];
...snip...
char *end = buffer;
...snip...
while (end && *end != ':')
end++;
The condition "end" within the while guard will always be true, given that
buffer is on the stack and thus will never be near the zero value. It
should be *end instead, to check for the NUL terminator byte: this code
will likely crash if the string does not contain a colon character.
This appears to have been present in the initial commit of the code
(8d6ad996f5d60d569532cdba4febb19c69bdf488)
Found by Braňo Náter using the "cppcheck" static analyzer.
---
python-ethtool/ethtool.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c
index b31f609..d377531 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -129,7 +129,7 @@ static PyObject *get_devices(PyObject *self __unused, PyObject *args __unused)
if (fgets(buffer, 256, fd) == NULL)
break;
/* find colon */
- while (end && *end != ':')
+ while (*end && *end != ':')
end++;
*end = 0; /* terminate where colon was */
while (*name == ' ')
--
1.7.11.4
11 years, 3 months
[PATCH] Fix buffer overflow in get_module()
by David Malcolm
get_module() includes this scanf call:
if (sscanf(buf, "%*d\t%*s\t%100s\t%*d\t%100s\n", driver, dev) > 0) {
i.e. "%100s" for each of driver and dev. i.e. a maximum field width of
100 for each.
However, this field width does not include the NUL terminator.
Increase the size of driver and dev from 100 to 101 to allow for the NUL byte.
This appears to have been present in the initial commit of the code
(8d6ad996f5d60d569532cdba4febb19c69bdf488)
Found by Braňo Náter using the "cppcheck" static analyzer.
---
python-ethtool/ethtool.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c
index b3fc65b..b31f609 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -500,7 +500,7 @@ static PyObject *get_module(PyObject *self __unused, PyObject *args)
int eno = errno;
FILE *file;
int found = 0;
- char driver[100], dev[100];
+ char driver[101], dev[101];
close(fd);
/* Before bailing, maybe it is a PCMCIA/PC Card? */
--
1.7.11.4
11 years, 3 months
[PATCH] remove redundant material from setup.py
by David Malcolm
Commit d3fd6b84f461a4d7ffbf3f3eae37381150b69e82 introduced an ext_modules
keyword argument to the setup invocation in setup.py, making the
ext_modules=Extension(...)
code at the top of setup.py redundant. Remove the redundant code.
---
setup.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/setup.py b/setup.py
index b8cbe9f..f081aa6 100644
--- a/setup.py
+++ b/setup.py
@@ -6,10 +6,6 @@ import sys
version = '0.7'
-ethtool = Extension('ethtool',
- sources = ['python-ethtool/ethtool.c',
- 'python-ethtool/etherinfo.c', 'python-ethtool/etherinfo_obj.c'])
-
def pkgconfig(pkg):
def _str2list(pkgstr, onlystr):
res = []
--
1.7.11.4
11 years, 3 months