summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sommerseth <davids@redhat.com>2010-07-30 19:30:30 +0200
committerDavid Sommerseth <davids@redhat.com>2010-07-30 19:30:30 +0200
commite9aa46ab32a45bd7fc0ad32573d1db84f5049554 (patch)
treea736740bf5c38a84cf0f7868d97bfc02fdf5aaed
parent1d4b0d894dd833cd9ac9e62cbdc400f24a11e32b (diff)
downloadpython-ethtool-e9aa46ab32a45bd7fc0ad32573d1db84f5049554.tar.gz
python-ethtool-e9aa46ab32a45bd7fc0ad32573d1db84f5049554.tar.xz
python-ethtool-e9aa46ab32a45bd7fc0ad32573d1db84f5049554.zip
Improved IPv6 support
As the IPv6 protocol allows a single device to have more than one IPv6 address, the previous implementation did not provide all IPv6 information. It would reject all except the last parsed IPv6 address. NOTE: This implementation will break the previous API. This change removes the ethtool.etherinfo.ipv6_address and ethtool.etherinfo.ipv6_netmask members. A new member is added, ethtool.etherinfo.ipv6_addresses (in plural). This contains a tupple list containing of ethtool.etherinfo_ipv6addr objects, one object for each configured IPv6 address on the device. These objects have the following members available: .address - The IPv6 address .netmask - The IPv6 netmask (in bit notation) .scope - A string with the IPv6 address scope Example code: import ethtool devs = ethtool.get_interfaces_info('eth0') for ip6 in devs[0].ipv6_addresses: print "[%s] %s/%i" % (ip6.scope, ip6.address, ip6.netmask) Signed-off-by: David Sommerseth <davids@redhat.com>
-rw-r--r--python-ethtool/etherinfo.c77
-rw-r--r--python-ethtool/etherinfo.h2
-rw-r--r--python-ethtool/etherinfo_ipv6_obj.c215
-rw-r--r--python-ethtool/etherinfo_obj.c60
-rw-r--r--python-ethtool/etherinfo_struct.h25
-rw-r--r--python-ethtool/ethtool.c7
-rw-r--r--setup.py3
7 files changed, 350 insertions, 39 deletions
diff --git a/python-ethtool/etherinfo.c b/python-ethtool/etherinfo.c
index 7ae5798..68cc2e6 100644
--- a/python-ethtool/etherinfo.c
+++ b/python-ethtool/etherinfo.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <asm/types.h>
#include <sys/socket.h>
+#include <netlink/route/rtnl.h>
#include <assert.h>
#include <errno.h>
#include "etherinfo_struct.h"
@@ -34,6 +35,30 @@
* Internal functions for working with struct etherinfo
*
*/
+#define SET_STR_VALUE(dst, src) { \
+ if( dst ) { \
+ free(dst); \
+ }; \
+ dst = strdup(src); \
+ }
+
+
+void free_ipv6addresses(struct ipv6address *ptr) {
+ struct ipv6address *ipv6ptr = ptr;
+
+ if( !ptr ) {
+ return;
+ }
+
+ while( ipv6ptr ) {
+ struct ipv6address *tmp = ipv6ptr->next;
+
+ free(ipv6ptr->address);
+ free(ipv6ptr);
+ ipv6ptr = tmp;
+ }
+}
+
void free_etherinfo(struct etherinfo *ptr)
{
if( ptr == NULL ) { // Just for safety
@@ -51,25 +76,35 @@ void free_etherinfo(struct etherinfo *ptr)
if( ptr->ipv4_broadcast ) {
free(ptr->ipv4_broadcast);
}
- if( ptr->ipv6_address ) {
- free(ptr->ipv6_address);
+ if( ptr->ipv6_addresses ) {
+ free_ipv6addresses(ptr->ipv6_addresses);
}
free(ptr);
}
+struct ipv6address * etherinfo_add_ipv6(struct ipv6address *addrptr, const char *addr, int netmask, int scope) {
+ struct ipv6address *newaddr = NULL;
+
+ newaddr = calloc(1, sizeof(struct ipv6address)+2);
+ if( !newaddr ) {
+ fprintf(stderr, "** ERROR ** Could not allocate memory for a new IPv6 address record (%s/%i [%i])",
+ addr, netmask, scope);
+ return addrptr;
+ }
+
+ SET_STR_VALUE(newaddr->address, addr);
+ newaddr->netmask = netmask;
+ newaddr->scope = scope;
+ newaddr->next = addrptr;
+ return newaddr;
+}
+
/*
* libnl callback functions
*
*/
-#define SET_STR_VALUE(dst, src) { \
- if( dst ) { \
- free(dst); \
- }; \
- dst = strdup(src); \
- }
-
static void callback_nl_link(struct nl_object *obj, void *arg)
{
struct etherinfo *ethi = (struct etherinfo *) arg;
@@ -134,8 +169,10 @@ static void callback_nl_address(struct nl_object *obj, void *arg)
SET_STR_VALUE(ethi->ipv4_broadcast, brdcst_str);
}
} else {
- SET_STR_VALUE(ethi->ipv6_address, ip_str);
- ethi->ipv6_netmask = rtnl_addr_get_prefixlen((struct rtnl_addr*) obj);
+ ethi->ipv6_addresses = etherinfo_add_ipv6(ethi->ipv6_addresses,
+ ip_str,
+ rtnl_addr_get_prefixlen((struct rtnl_addr*) obj),
+ rtnl_addr_get_scope((struct rtnl_addr*) obj));
}
return;
default:
@@ -167,9 +204,17 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr)
}
fprintf(fp, "\n");
}
- if( ptr->ipv6_address ) {
- fprintf(fp, "\tIPv6 address: %s/%i\n",
- ptr->ipv6_address, ptr->ipv6_netmask);
+ if( ptr->ipv6_addresses ) {
+ struct ipv6address *ipv6 = ptr->ipv6_addresses;
+
+ fprintf(fp, "\tIPv6 addresses:\n");
+ for(; ipv6; ipv6 = ipv6->next) {
+ char scope[66];
+
+ rtnl_scope2str(ipv6->scope, scope, 64);
+ fprintf(fp, "\t [%s] %s/%i\n",
+ scope, ipv6->address, ipv6->netmask);
+ }
}
fprintf(fp, "\n");
}
@@ -214,6 +259,10 @@ int get_etherinfo(struct etherinfo *ethinf, struct _nlconnection *nlc, nlQuery q
break;
case NLQRY_ADDR:
+ /* Remove old IPv6 information we might have */
+ free_ipv6addresses(ethinf->ipv6_addresses);
+ ethinf->ipv6_addresses = NULL;
+
/* Extract IP address information */
addr_cache = rtnl_addr_alloc_cache(nlc->nlrt_handle);
addr = rtnl_addr_alloc();
diff --git a/python-ethtool/etherinfo.h b/python-ethtool/etherinfo.h
index 1b8c425..0d441a3 100644
--- a/python-ethtool/etherinfo.h
+++ b/python-ethtool/etherinfo.h
@@ -30,4 +30,6 @@ int get_etherinfo(struct etherinfo *ethinf, struct _nlconnection *nlc, nlQuery q
void free_etherinfo(struct etherinfo *ptr);
void dump_etherinfo(FILE *, struct etherinfo *);
+void free_ipv6addresses(struct ipv6address *ptr);
+
#endif
diff --git a/python-ethtool/etherinfo_ipv6_obj.c b/python-ethtool/etherinfo_ipv6_obj.c
new file mode 100644
index 0000000..cec5248
--- /dev/null
+++ b/python-ethtool/etherinfo_ipv6_obj.c
@@ -0,0 +1,215 @@
+/**
+ * @file etherinfo_ipv6_obj.c
+ * @author David Sommerseth <davids@redhat.com>
+ * @date Thu Jul 29 17:51:28 2010
+ *
+ * @brief Python ethtool.etherinfo class functions.
+ *
+ */
+
+#include <Python.h>
+#include "structmember.h"
+
+#include <netlink/route/rtnl.h>
+#include "etherinfo_struct.h"
+#include "etherinfo.h"
+
+
+/**
+ * ethtool.etherinfo deallocator - cleans up when a object is deleted
+ *
+ * @param self etherinfo_ipv6_py object structure
+ */
+void _ethtool_etherinfo_ipv6_dealloc(etherinfo_ipv6_py *self)
+{
+ if( self->addrdata ) {
+ free_ipv6addresses(self->addrdata);
+ }
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+
+/**
+ * ethtool.etherinfo function, creating a new etherinfo object
+ *
+ * @param type
+ * @param args
+ * @param kwds
+ *
+ * @return Returns in PyObject with the new object on success, otherwise NULL
+ */
+PyObject *_ethtool_etherinfo_ipv6_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ etherinfo_ipv6_py *self;
+
+ self = (etherinfo_ipv6_py *)type->tp_alloc(type, 0);
+ return (PyObject *)self;
+}
+
+
+/**
+ * ethtool.etherinfo init (constructor) method. Makes sure the object is initialised correctly.
+ *
+ * @param self
+ * @param args
+ * @param kwds
+ *
+ * @return Returns 0 on success.
+ */
+int _ethtool_etherinfo_ipv6_init(etherinfo_ipv6_py *self, PyObject *args, PyObject *kwds)
+{
+ static char *etherinfo_kwlist[] = {"etherinfo_ipv6_ptr", NULL};
+ PyObject *ethinf_ptr = NULL;
+
+ if( !PyArg_ParseTupleAndKeywords(args, kwds, "O", etherinfo_kwlist, &ethinf_ptr)) {
+ PyErr_SetString(PyExc_AttributeError, "Invalid data pointer to constructor");
+ return -1;
+ }
+ self->addrdata = (struct ipv6address *) PyCObject_AsVoidPtr(ethinf_ptr);
+ return 0;
+}
+
+/**
+ * ethtool.etherinfo function for retrieving data from a Python object.
+ *
+ * @param self
+ * @param attr_o contains the object member request (which element to return)
+ *
+ * @return Returns a PyObject with the value requested on success, otherwise NULL
+ */
+PyObject *_ethtool_etherinfo_ipv6_getter(etherinfo_ipv6_py *self, PyObject *attr_o)
+{
+ PyObject *ret;
+ char *attr = PyString_AsString(attr_o);
+
+ if( !self || !self->addrdata ) {
+ PyErr_SetString(PyExc_AttributeError, "No data available");
+ return NULL;
+ }
+
+ if( strcmp(attr, "address") == 0 ) {
+ ret = RETURN_STRING(self->addrdata->address);
+ } else if( strcmp(attr, "netmask") == 0 ) {
+ ret = PyInt_FromLong(self->addrdata->netmask);
+ } else if( strcmp(attr, "scope") == 0 ) {
+ char scope[66];
+
+ rtnl_scope2str(self->addrdata->scope, scope, 66);
+ ret = PyString_FromString(scope);
+ } else {
+ ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
+ }
+ return ret;
+}
+
+
+/**
+ * ethtool.etherinfo function for setting a value to a object member. This feature is
+ * disabled by always returning -1, as the values are read-only by the user.
+ *
+ * @param self
+ * @param attr_o
+ * @param val_o
+ *
+ * @return Returns always -1 (failure).
+ */
+int _ethtool_etherinfo_ipv6_setter(etherinfo_ipv6_py *self, PyObject *attr_o, PyObject *val_o)
+{
+ PyErr_SetString(PyExc_AttributeError, "etherinfo_ipv6addr member values are read-only.");
+ return -1;
+}
+
+
+/**
+ * Creates a human readable format of the information when object is being treated as a string
+ *
+ * @param self
+ *
+ * @return Returns a PyObject with a string with all of the information
+ */
+PyObject *_ethtool_etherinfo_ipv6_str(etherinfo_ipv6_py *self)
+{
+ char scope[66];
+
+ if( !self || !self->addrdata ) {
+ PyErr_SetString(PyExc_AttributeError, "No data available");
+ return NULL;
+ }
+
+ rtnl_scope2str(self->addrdata->scope, scope, 64);
+ return PyString_FromFormat("[%s] %s/%i\n",
+ scope,
+ self->addrdata->address,
+ self->addrdata->netmask);
+}
+
+
+/**
+ * This is required by Python, which lists all accessible methods
+ * in the object. But no methods are provided.
+ *
+ */
+static PyMethodDef _ethtool_etherinfo_ipv6_methods[] = {
+ {NULL} /**< No methods defined */
+};
+
+/**
+ * Defines all accessible object members
+ *
+ */
+static PyMemberDef _ethtool_etherinfo_ipv6_members[] = {
+ {"address", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
+ "IPv6 address"},
+ {"netmask", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
+ "IPv6 netmask"},
+ {"scope", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
+ "IPv6 IP address scope"},
+ {NULL} /* End of member list */
+};
+
+/**
+ * Definition of the functions a Python class/object requires.
+ *
+ */
+PyTypeObject ethtool_etherinfoIPv6Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "ethtool.etherinfo_ipv6addr", /*tp_name*/
+ sizeof(etherinfo_ipv6_py), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)_ethtool_etherinfo_ipv6_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ (reprfunc)_ethtool_etherinfo_ipv6_str, /*tp_str*/
+ (getattrofunc)_ethtool_etherinfo_ipv6_getter, /*tp_getattro*/
+ (setattrofunc)_ethtool_etherinfo_ipv6_setter, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "IPv6 address information", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ _ethtool_etherinfo_ipv6_methods, /* tp_methods */
+ _ethtool_etherinfo_ipv6_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)_ethtool_etherinfo_ipv6_init, /* tp_init */
+ 0, /* tp_alloc */
+ _ethtool_etherinfo_ipv6_new, /* tp_new */
+};
+
diff --git a/python-ethtool/etherinfo_obj.c b/python-ethtool/etherinfo_obj.c
index 814da78..384e915 100644
--- a/python-ethtool/etherinfo_obj.c
+++ b/python-ethtool/etherinfo_obj.c
@@ -10,9 +10,11 @@
#include <Python.h>
#include "structmember.h"
+#include <netlink/route/rtnl.h>
#include "etherinfo_struct.h"
#include "etherinfo.h"
+extern PyTypeObject ethtool_etherinfoIPv6Type;
/**
* ethtool.etherinfo deallocator - cleans up when a object is deleted
@@ -72,15 +74,6 @@ int _ethtool_etherinfo_init(etherinfo_py *self, PyObject *args, PyObject *kwds)
}
/**
- * NULL safe PyString_FromString() wrapper. If input string is NULL, None will be returned
- *
- * @param str Input C string (char *)
- *
- * @return Returns a PyObject with either the input string wrapped up, or a Python None value.
- */
-#define RETURN_STRING(str) (str ? PyString_FromString(str) : Py_None);
-
-/**
* ethtool.etherinfo function for retrieving data from a Python object.
*
* @param self
@@ -112,12 +105,29 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
} else if( strcmp(attr, "ipv4_broadcast") == 0 ) {
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
ret = RETURN_STRING(self->data->ethinfo->ipv4_broadcast);
- } else if( strcmp(attr, "ipv6_address") == 0 ) {
- get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
- ret = RETURN_STRING(self->data->ethinfo->ipv6_address);
- } else if( strcmp(attr, "ipv6_netmask") == 0 ) {
+ } else if( strcmp(attr, "ipv6_addresses") == 0 ) {
+ struct ipv6address *ipv6 = NULL;
+ int i = 0;
+ ret = PyTuple_New(1);
+
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
- ret = PyInt_FromLong(self->data->ethinfo->ipv6_netmask);
+ ipv6 = self->data->ethinfo->ipv6_addresses;
+ while( ipv6 ) {
+ PyObject *ipv6_pyobj = NULL, *ipv6_pydata = NULL, *args = NULL;
+ struct ipv6address *next = ipv6->next;
+
+ ipv6->next = NULL;
+ ipv6_pydata = PyCObject_FromVoidPtr(ipv6, NULL);
+ args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, ipv6_pydata);
+ ipv6_pyobj = PyObject_CallObject((PyObject *)&ethtool_etherinfoIPv6Type, args);
+ if( ipv6_pyobj ) {
+ PyTuple_SetItem(ret, i++, ipv6_pyobj);
+ _PyTuple_Resize(&ret, i+1);
+ }
+ ipv6 = next;
+ }
+ _PyTuple_Resize(&ret, i);
} else {
ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
}
@@ -180,11 +190,19 @@ PyObject *_ethtool_etherinfo_str(etherinfo_py *self)
PyString_Concat(&ret, tmp);
}
- if( self->data->ethinfo->ipv6_address ) {
- PyObject *tmp = PyString_FromFormat("\tIPv6 address: %s/%i\n",
- self->data->ethinfo->ipv6_address,
- self->data->ethinfo->ipv6_netmask);
+ if( self->data->ethinfo->ipv6_addresses ) {
+ struct ipv6address *ipv6 = self->data->ethinfo->ipv6_addresses;
+ PyObject *tmp = PyString_FromFormat("\tIPv6 addresses:\n");
PyString_Concat(&ret, tmp);
+
+ for( ; ipv6; ipv6 = ipv6->next) {
+ char scope[66];
+
+ rtnl_scope2str(ipv6->scope, scope, 64);
+ PyObject *addr = PyString_FromFormat("\t [%s] %s/%i\n",
+ scope, ipv6->address, ipv6->netmask);
+ PyString_Concat(&ret, addr);
+ }
}
return ret;
}
@@ -214,10 +232,8 @@ static PyMemberDef _ethtool_etherinfo_members[] = {
"IPv4 netmask in bits"},
{"ipv4_broadcast", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
"IPv4 broadcast address"},
- {"ipv6_address", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
- "IPv6 address"},
- {"ipv6_netmask", T_INT, offsetof(etherinfo_py, data), 0,
- "IPv6 netmask in bits"},
+ {"ipv6_addresses", T_OBJECT_EX, offsetof(etherinfo_py, data), 0,
+ "Returns a list of associated IPv6 addresses"},
{NULL} /* End of member list */
};
diff --git a/python-ethtool/etherinfo_struct.h b/python-ethtool/etherinfo_struct.h
index b09b49c..8f9e2e5 100644
--- a/python-ethtool/etherinfo_struct.h
+++ b/python-ethtool/etherinfo_struct.h
@@ -18,8 +18,14 @@ struct etherinfo {
char *ipv4_address;
int ipv4_netmask;
char *ipv4_broadcast;
- char *ipv6_address;
- int ipv6_netmask;
+ struct ipv6address *ipv6_addresses;
+};
+
+struct ipv6address {
+ char *address;
+ int netmask;
+ int scope;
+ struct ipv6address *next;
};
/*
@@ -45,4 +51,19 @@ typedef struct {
struct etherinfo_obj_data *data;
} etherinfo_py;
+
+typedef struct {
+ PyObject_HEAD
+ struct ipv6address *addrdata;
+} etherinfo_ipv6_py;
+
+/**
+ * NULL safe PyString_FromString() wrapper. If input string is NULL, None will be returned
+ *
+ * @param str Input C string (char *)
+ *
+ * @return Returns a PyObject with either the input string wrapped up, or a Python None value.
+ */
+#define RETURN_STRING(str) (str ? PyString_FromString(str) : Py_None)
+
#endif
diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c
index c6a9af4..7115fb0 100644
--- a/python-ethtool/ethtool.c
+++ b/python-ethtool/ethtool.c
@@ -32,6 +32,7 @@
static struct _nlconnection nlconnection;
extern PyTypeObject ethtool_etherinfoType;
+extern PyTypeObject ethtool_etherinfoIPv6Type;
#ifndef IFF_DYNAMIC
#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
@@ -1025,6 +1026,12 @@ PyMODINIT_FUNC initethtool(void)
Py_INCREF(&ethtool_etherinfoType);
PyModule_AddObject(m, "etherinfo", (PyObject *)&ethtool_etherinfoType);
+ // Prepare the ethtool.etherinfo_ipv6addr class
+ if (PyType_Ready(&ethtool_etherinfoIPv6Type) < 0)
+ return;
+ Py_INCREF(&ethtool_etherinfoIPv6Type);
+ PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)&ethtool_etherinfoIPv6Type);
+
// Prepare an internal netlink connection object
if( open_netlink(&nlconnection) ) {
PyModule_AddObject(m, "__nlconnection",
diff --git a/setup.py b/setup.py
index aed2186..71b8c70 100644
--- a/setup.py
+++ b/setup.py
@@ -59,7 +59,8 @@ setup(name='ethtool',
sources = [
'python-ethtool/ethtool.c',
'python-ethtool/etherinfo.c',
- 'python-ethtool/etherinfo_obj.c'],
+ 'python-ethtool/etherinfo_obj.c',
+ 'python-ethtool/etherinfo_ipv6_obj.c'],
include_dirs = libnl['include'],
library_dirs = libnl['libdirs'],
libraries = libnl['libs']