summaryrefslogtreecommitdiffstats
path: root/python-ethtool/etherinfo.c
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 /python-ethtool/etherinfo.c
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
Diffstat (limited to 'python-ethtool/etherinfo.c')
-rw-r--r--python-ethtool/etherinfo.c76
1 files changed, 52 insertions, 24 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);