Skip to content

Commit e9aa46a

Browse files
author
David Sommerseth
committed
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>
1 parent 1d4b0d8 commit e9aa46a

File tree

7 files changed

+350
-39
lines changed

7 files changed

+350
-39
lines changed

python-ethtool/etherinfo.c

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <stdlib.h>
2525
#include <asm/types.h>
2626
#include <sys/socket.h>
27+
#include <netlink/route/rtnl.h>
2728
#include <assert.h>
2829
#include <errno.h>
2930
#include "etherinfo_struct.h"
@@ -34,6 +35,30 @@
3435
* Internal functions for working with struct etherinfo
3536
*
3637
*/
38+
#define SET_STR_VALUE(dst, src) { \
39+
if( dst ) { \
40+
free(dst); \
41+
}; \
42+
dst = strdup(src); \
43+
}
44+
45+
46+
void free_ipv6addresses(struct ipv6address *ptr) {
47+
struct ipv6address *ipv6ptr = ptr;
48+
49+
if( !ptr ) {
50+
return;
51+
}
52+
53+
while( ipv6ptr ) {
54+
struct ipv6address *tmp = ipv6ptr->next;
55+
56+
free(ipv6ptr->address);
57+
free(ipv6ptr);
58+
ipv6ptr = tmp;
59+
}
60+
}
61+
3762
void free_etherinfo(struct etherinfo *ptr)
3863
{
3964
if( ptr == NULL ) { // Just for safety
@@ -51,25 +76,35 @@ void free_etherinfo(struct etherinfo *ptr)
5176
if( ptr->ipv4_broadcast ) {
5277
free(ptr->ipv4_broadcast);
5378
}
54-
if( ptr->ipv6_address ) {
55-
free(ptr->ipv6_address);
79+
if( ptr->ipv6_addresses ) {
80+
free_ipv6addresses(ptr->ipv6_addresses);
5681
}
5782
free(ptr);
5883
}
5984

85+
struct ipv6address * etherinfo_add_ipv6(struct ipv6address *addrptr, const char *addr, int netmask, int scope) {
86+
struct ipv6address *newaddr = NULL;
87+
88+
newaddr = calloc(1, sizeof(struct ipv6address)+2);
89+
if( !newaddr ) {
90+
fprintf(stderr, "** ERROR ** Could not allocate memory for a new IPv6 address record (%s/%i [%i])",
91+
addr, netmask, scope);
92+
return addrptr;
93+
}
94+
95+
SET_STR_VALUE(newaddr->address, addr);
96+
newaddr->netmask = netmask;
97+
newaddr->scope = scope;
98+
newaddr->next = addrptr;
99+
return newaddr;
100+
}
101+
60102

61103
/*
62104
* libnl callback functions
63105
*
64106
*/
65107

66-
#define SET_STR_VALUE(dst, src) { \
67-
if( dst ) { \
68-
free(dst); \
69-
}; \
70-
dst = strdup(src); \
71-
}
72-
73108
static void callback_nl_link(struct nl_object *obj, void *arg)
74109
{
75110
struct etherinfo *ethi = (struct etherinfo *) arg;
@@ -134,8 +169,10 @@ static void callback_nl_address(struct nl_object *obj, void *arg)
134169
SET_STR_VALUE(ethi->ipv4_broadcast, brdcst_str);
135170
}
136171
} else {
137-
SET_STR_VALUE(ethi->ipv6_address, ip_str);
138-
ethi->ipv6_netmask = rtnl_addr_get_prefixlen((struct rtnl_addr*) obj);
172+
ethi->ipv6_addresses = etherinfo_add_ipv6(ethi->ipv6_addresses,
173+
ip_str,
174+
rtnl_addr_get_prefixlen((struct rtnl_addr*) obj),
175+
rtnl_addr_get_scope((struct rtnl_addr*) obj));
139176
}
140177
return;
141178
default:
@@ -167,9 +204,17 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr)
167204
}
168205
fprintf(fp, "\n");
169206
}
170-
if( ptr->ipv6_address ) {
171-
fprintf(fp, "\tIPv6 address: %s/%i\n",
172-
ptr->ipv6_address, ptr->ipv6_netmask);
207+
if( ptr->ipv6_addresses ) {
208+
struct ipv6address *ipv6 = ptr->ipv6_addresses;
209+
210+
fprintf(fp, "\tIPv6 addresses:\n");
211+
for(; ipv6; ipv6 = ipv6->next) {
212+
char scope[66];
213+
214+
rtnl_scope2str(ipv6->scope, scope, 64);
215+
fprintf(fp, "\t [%s] %s/%i\n",
216+
scope, ipv6->address, ipv6->netmask);
217+
}
173218
}
174219
fprintf(fp, "\n");
175220
}
@@ -214,6 +259,10 @@ int get_etherinfo(struct etherinfo *ethinf, struct _nlconnection *nlc, nlQuery q
214259
break;
215260

216261
case NLQRY_ADDR:
262+
/* Remove old IPv6 information we might have */
263+
free_ipv6addresses(ethinf->ipv6_addresses);
264+
ethinf->ipv6_addresses = NULL;
265+
217266
/* Extract IP address information */
218267
addr_cache = rtnl_addr_alloc_cache(nlc->nlrt_handle);
219268
addr = rtnl_addr_alloc();

python-ethtool/etherinfo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ int get_etherinfo(struct etherinfo *ethinf, struct _nlconnection *nlc, nlQuery q
3030
void free_etherinfo(struct etherinfo *ptr);
3131
void dump_etherinfo(FILE *, struct etherinfo *);
3232

33+
void free_ipv6addresses(struct ipv6address *ptr);
34+
3335
#endif
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/**
2+
* @file etherinfo_ipv6_obj.c
3+
* @author David Sommerseth <davids@redhat.com>
4+
* @date Thu Jul 29 17:51:28 2010
5+
*
6+
* @brief Python ethtool.etherinfo class functions.
7+
*
8+
*/
9+
10+
#include <Python.h>
11+
#include "structmember.h"
12+
13+
#include <netlink/route/rtnl.h>
14+
#include "etherinfo_struct.h"
15+
#include "etherinfo.h"
16+
17+
18+
/**
19+
* ethtool.etherinfo deallocator - cleans up when a object is deleted
20+
*
21+
* @param self etherinfo_ipv6_py object structure
22+
*/
23+
void _ethtool_etherinfo_ipv6_dealloc(etherinfo_ipv6_py *self)
24+
{
25+
if( self->addrdata ) {
26+
free_ipv6addresses(self->addrdata);
27+
}
28+
self->ob_type->tp_free((PyObject*)self);
29+
}
30+
31+
32+
/**
33+
* ethtool.etherinfo function, creating a new etherinfo object
34+
*
35+
* @param type
36+
* @param args
37+
* @param kwds
38+
*
39+
* @return Returns in PyObject with the new object on success, otherwise NULL
40+
*/
41+
PyObject *_ethtool_etherinfo_ipv6_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
42+
{
43+
etherinfo_ipv6_py *self;
44+
45+
self = (etherinfo_ipv6_py *)type->tp_alloc(type, 0);
46+
return (PyObject *)self;
47+
}
48+
49+
50+
/**
51+
* ethtool.etherinfo init (constructor) method. Makes sure the object is initialised correctly.
52+
*
53+
* @param self
54+
* @param args
55+
* @param kwds
56+
*
57+
* @return Returns 0 on success.
58+
*/
59+
int _ethtool_etherinfo_ipv6_init(etherinfo_ipv6_py *self, PyObject *args, PyObject *kwds)
60+
{
61+
static char *etherinfo_kwlist[] = {"etherinfo_ipv6_ptr", NULL};
62+
PyObject *ethinf_ptr = NULL;
63+
64+
if( !PyArg_ParseTupleAndKeywords(args, kwds, "O", etherinfo_kwlist, &ethinf_ptr)) {
65+
PyErr_SetString(PyExc_AttributeError, "Invalid data pointer to constructor");
66+
return -1;
67+
}
68+
self->addrdata = (struct ipv6address *) PyCObject_AsVoidPtr(ethinf_ptr);
69+
return 0;
70+
}
71+
72+
/**
73+
* ethtool.etherinfo function for retrieving data from a Python object.
74+
*
75+
* @param self
76+
* @param attr_o contains the object member request (which element to return)
77+
*
78+
* @return Returns a PyObject with the value requested on success, otherwise NULL
79+
*/
80+
PyObject *_ethtool_etherinfo_ipv6_getter(etherinfo_ipv6_py *self, PyObject *attr_o)
81+
{
82+
PyObject *ret;
83+
char *attr = PyString_AsString(attr_o);
84+
85+
if( !self || !self->addrdata ) {
86+
PyErr_SetString(PyExc_AttributeError, "No data available");
87+
return NULL;
88+
}
89+
90+
if( strcmp(attr, "address") == 0 ) {
91+
ret = RETURN_STRING(self->addrdata->address);
92+
} else if( strcmp(attr, "netmask") == 0 ) {
93+
ret = PyInt_FromLong(self->addrdata->netmask);
94+
} else if( strcmp(attr, "scope") == 0 ) {
95+
char scope[66];
96+
97+
rtnl_scope2str(self->addrdata->scope, scope, 66);
98+
ret = PyString_FromString(scope);
99+
} else {
100+
ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
101+
}
102+
return ret;
103+
}
104+
105+
106+
/**
107+
* ethtool.etherinfo function for setting a value to a object member. This feature is
108+
* disabled by always returning -1, as the values are read-only by the user.
109+
*
110+
* @param self
111+
* @param attr_o
112+
* @param val_o
113+
*
114+
* @return Returns always -1 (failure).
115+
*/
116+
int _ethtool_etherinfo_ipv6_setter(etherinfo_ipv6_py *self, PyObject *attr_o, PyObject *val_o)
117+
{
118+
PyErr_SetString(PyExc_AttributeError, "etherinfo_ipv6addr member values are read-only.");
119+
return -1;
120+
}
121+
122+
123+
/**
124+
* Creates a human readable format of the information when object is being treated as a string
125+
*
126+
* @param self
127+
*
128+
* @return Returns a PyObject with a string with all of the information
129+
*/
130+
PyObject *_ethtool_etherinfo_ipv6_str(etherinfo_ipv6_py *self)
131+
{
132+
char scope[66];
133+
134+
if( !self || !self->addrdata ) {
135+
PyErr_SetString(PyExc_AttributeError, "No data available");
136+
return NULL;
137+
}
138+
139+
rtnl_scope2str(self->addrdata->scope, scope, 64);
140+
return PyString_FromFormat("[%s] %s/%i\n",
141+
scope,
142+
self->addrdata->address,
143+
self->addrdata->netmask);
144+
}
145+
146+
147+
/**
148+
* This is required by Python, which lists all accessible methods
149+
* in the object. But no methods are provided.
150+
*
151+
*/
152+
static PyMethodDef _ethtool_etherinfo_ipv6_methods[] = {
153+
{NULL} /**< No methods defined */
154+
};
155+
156+
/**
157+
* Defines all accessible object members
158+
*
159+
*/
160+
static PyMemberDef _ethtool_etherinfo_ipv6_members[] = {
161+
{"address", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
162+
"IPv6 address"},
163+
{"netmask", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
164+
"IPv6 netmask"},
165+
{"scope", T_OBJECT_EX, offsetof(etherinfo_ipv6_py, addrdata), 0,
166+
"IPv6 IP address scope"},
167+
{NULL} /* End of member list */
168+
};
169+
170+
/**
171+
* Definition of the functions a Python class/object requires.
172+
*
173+
*/
174+
PyTypeObject ethtool_etherinfoIPv6Type = {
175+
PyObject_HEAD_INIT(NULL)
176+
0, /*ob_size*/
177+
"ethtool.etherinfo_ipv6addr", /*tp_name*/
178+
sizeof(etherinfo_ipv6_py), /*tp_basicsize*/
179+
0, /*tp_itemsize*/
180+
(destructor)_ethtool_etherinfo_ipv6_dealloc,/*tp_dealloc*/
181+
0, /*tp_print*/
182+
0, /*tp_getattr*/
183+
0, /*tp_setattr*/
184+
0, /*tp_compare*/
185+
0, /*tp_repr*/
186+
0, /*tp_as_number*/
187+
0, /*tp_as_sequence*/
188+
0, /*tp_as_mapping*/
189+
0, /*tp_hash */
190+
0, /*tp_call*/
191+
(reprfunc)_ethtool_etherinfo_ipv6_str, /*tp_str*/
192+
(getattrofunc)_ethtool_etherinfo_ipv6_getter, /*tp_getattro*/
193+
(setattrofunc)_ethtool_etherinfo_ipv6_setter, /*tp_setattro*/
194+
0, /*tp_as_buffer*/
195+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
196+
"IPv6 address information", /* tp_doc */
197+
0, /* tp_traverse */
198+
0, /* tp_clear */
199+
0, /* tp_richcompare */
200+
0, /* tp_weaklistoffset */
201+
0, /* tp_iter */
202+
0, /* tp_iternext */
203+
_ethtool_etherinfo_ipv6_methods, /* tp_methods */
204+
_ethtool_etherinfo_ipv6_members, /* tp_members */
205+
0, /* tp_getset */
206+
0, /* tp_base */
207+
0, /* tp_dict */
208+
0, /* tp_descr_get */
209+
0, /* tp_descr_set */
210+
0, /* tp_dictoffset */
211+
(initproc)_ethtool_etherinfo_ipv6_init, /* tp_init */
212+
0, /* tp_alloc */
213+
_ethtool_etherinfo_ipv6_new, /* tp_new */
214+
};
215+

0 commit comments

Comments
 (0)