11/* etherinfo.c - Retrieve ethernet interface info via NETLINK
22 *
3- * Copyright (C) 2009-2010 Red Hat Inc.
3+ * Copyright (C) 2009-2011 Red Hat Inc.
44 *
55 * David Sommerseth <davids@redhat.com>
66 * Parts of this code is based on ideas and solutions in iproute2
2727#include <netlink/route/rtnl.h>
2828#include <assert.h>
2929#include <errno.h>
30+ #include <pthread.h>
3031#include "etherinfo_struct.h"
3132#include "etherinfo.h"
3233
34+ pthread_mutex_t nlc_counter_mtx = PTHREAD_MUTEX_INITIALIZER ;
35+
3336/*
3437 *
3538 * Internal functions for working with struct etherinfo
@@ -274,15 +277,25 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr)
274277 *
275278 * @return Returns 1 on success, otherwise 0.
276279 */
277- int get_etherinfo (struct etherinfo * ethinf , struct nl_handle * nlc , nlQuery query )
280+ int get_etherinfo (struct etherinfo_obj_data * data , nlQuery query )
278281{
279282 struct nl_cache * link_cache ;
280283 struct nl_cache * addr_cache ;
281284 struct rtnl_addr * addr ;
282285 struct rtnl_link * link ;
286+ struct etherinfo * ethinf = NULL ;
283287 int ret = 0 ;
284288
285- if ( !ethinf || !nlc ) {
289+ if ( !data || !data -> ethinfo ) {
290+ return 0 ;
291+ }
292+ ethinf = data -> ethinfo ;
293+
294+ /* Open a NETLINK connection on-the-fly */
295+ if ( !open_netlink (data ) ) {
296+ PyErr_Format (PyExc_RuntimeError ,
297+ "Could not open a NETLINK connection for %s" ,
298+ ethinf -> device );
286299 return 0 ;
287300 }
288301
@@ -291,7 +304,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
291304 * interface index if we have that
292305 */
293306 if ( ethinf -> index < 0 ) {
294- link_cache = rtnl_link_alloc_cache (nlc );
307+ link_cache = rtnl_link_alloc_cache (* data -> nlc );
295308 ethinf -> index = rtnl_link_name2i (link_cache , ethinf -> device );
296309 if ( ethinf -> index < 0 ) {
297310 return 0 ;
@@ -303,7 +316,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
303316 switch ( query ) {
304317 case NLQRY_LINK :
305318 /* Extract MAC/hardware address of the interface */
306- link_cache = rtnl_link_alloc_cache (nlc );
319+ link_cache = rtnl_link_alloc_cache (* data -> nlc );
307320 link = rtnl_link_alloc ();
308321 rtnl_link_set_ifindex (link , ethinf -> index );
309322 nl_cache_foreach_filter (link_cache , (struct nl_object * )link , callback_nl_link , ethinf );
@@ -314,7 +327,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
314327
315328 case NLQRY_ADDR :
316329 /* Extract IP address information */
317- addr_cache = rtnl_addr_alloc_cache (nlc );
330+ addr_cache = rtnl_addr_alloc_cache (* data -> nlc );
318331 addr = rtnl_addr_alloc ();
319332 rtnl_addr_set_ifindex (addr , ethinf -> index );
320333
@@ -337,3 +350,75 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
337350 return ret ;
338351}
339352
353+
354+ /**
355+ * Connects to the NETLINK interface. This will be called
356+ * for each etherinfo object being generated, and it will
357+ * keep a separate file descriptor open for each object
358+ *
359+ * @param data etherinfo_obj_data structure
360+ *
361+ * @return Returns 1 on success, otherwise 0.
362+ */
363+ int open_netlink (struct etherinfo_obj_data * data )
364+ {
365+ if ( !data ) {
366+ return 0 ;
367+ }
368+
369+ /* Reuse already established NETLINK connection, if a connection exists */
370+ if ( * data -> nlc ) {
371+ /* If this object has not used NETLINK earlier, tag it as a user */
372+ if ( !data -> nlc_active ) {
373+ pthread_mutex_lock (& nlc_counter_mtx );
374+ (* data -> nlc_users )++ ;
375+ pthread_mutex_unlock (& nlc_counter_mtx );
376+ }
377+ data -> nlc_active = 1 ;
378+ return 1 ;
379+ }
380+
381+ /* No earlier connections exists, establish a new one */
382+ * data -> nlc = nl_handle_alloc ();
383+ nl_connect (* data -> nlc , NETLINK_ROUTE );
384+ if ( (* data -> nlc != NULL ) ) {
385+ /* Tag this object as an active user */
386+ pthread_mutex_lock (& nlc_counter_mtx );
387+ (* data -> nlc_users )++ ;
388+ pthread_mutex_unlock (& nlc_counter_mtx );
389+ data -> nlc_active = 1 ;
390+ return 1 ;
391+ } else {
392+ return 0 ;
393+ }
394+ }
395+
396+
397+ /**
398+ * Closes the NETLINK connection. This should be called automatically whenever
399+ * the corresponding etherinfo object is deleted.
400+ *
401+ * @param ptr Pointer to the pointer of struct nl_handle, which contains the NETLINK connection
402+ */
403+ void close_netlink (struct etherinfo_obj_data * data )
404+ {
405+ if ( !data || !(* data -> nlc ) ) {
406+ return ;
407+ }
408+
409+ /* Untag this object as a NETLINK user */
410+ data -> nlc_active = 0 ;
411+ pthread_mutex_lock (& nlc_counter_mtx );
412+ (* data -> nlc_users )-- ;
413+ pthread_mutex_unlock (& nlc_counter_mtx );
414+
415+ /* Don't close the connection if there are more users */
416+ if ( * data -> nlc_users > 0 ) {
417+ return ;
418+ }
419+
420+ /* Close NETLINK connection */
421+ nl_close (* data -> nlc );
422+ nl_handle_destroy (* data -> nlc );
423+ * data -> nlc = NULL ;
424+ }
0 commit comments