En una instalación de collectd con soporte para el plugin iptables, me encontré con un problema de símbolos: "undefined symbol: iptc_strerror". Tal como explica el artículo Recolectar estadísticas de iptables con collectd, el plugin iptables de collectd requiere del paquete iptables-dev, el cual provee la librería libiptc.

En este artículo voy a demostrar cómo es posible depurar errores con símbolos de librerías empleando el utilitario objdump provisto por el paquete binutils.



El paquete iptables-dev había sido instalado y la configuración del collectd (./configure) pasó con éxito:

    iptables  . . . . . . yes

Sin embargo, al iniciar el servicio ocurría el error detallado en el log del sistema Debian (/var/log/syslog):

root@devuan:~#  tail /var/log/syslog
Feb 21 08:57:43 devuan collectd[557]: dlopen("/opt/collectd/lib/collectd/iptables.so") failed: /opt/collectd/lib/collectd/iptables.so: undefined symbol: iptc_strerror. The most common cause for this problem is missing dependencies. Use ldd(1) to check the dependencies of the plugin / shared object.

El plugin iptables de collectd lamentablemente suele traer problemas, tal como aclara la sección de dependencias del mismo en la Wiki oficial:

Linking with the libiptc has not been easy, unfortunately. Because that library used to be meant for internal use only, it was only available as a static library on many distributions.

Los problemas con la libiptc están relacionados a que se trata de una librería pensada para uso interno solamente.

En este sistema Devuan 2.0 ASCII de 64 bit, la librería libiptc queda instalada en el directorio /usr/lib/x86_64-linux-gnu:

root@devuan:~#  find /usr -name "libiptc.so"
/usr/lib/x86_64-linux-gnu/libiptc.so

Según el log al iniciar el servicio, el linker falla al incluir la librería de collectd iptables.so debido a que no se encuentra el símbolo "iptc_strerror" provisto por libiptc. Los símbolos son nombres simbólicos que se asocian a una dirección de memoria para que el linker pueda enlazar a una librería. Típicamente representan la ubicación en memoria (dentro de la librería) de una función o procedimiento.

Veamos qué símbolos provee esta librería. La opción -T de objdump permite volcar la tabla de símbolos de una librería:

root@devuan:~#  objdump -T /usr/lib/x86_64-linux-gnu/libiptc.so

/usr/lib/x86_64-linux-gnu/libiptc.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*	0000000000000000              _ITM_deregisterTMCloneTable
0000000000000000  w   D  *UND*	0000000000000000              __gmon_start__
0000000000000000  w   D  *UND*	0000000000000000              _Jv_RegisterClasses
0000000000000000  w   D  *UND*	0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w   DF *UND*	0000000000000000  GLIBC_2.2.5 __cxa_finalize
0000000000201008 g    D  .data	0000000000000000  Base        _edata
0000000000201010 g    D  .bss	0000000000000000  Base        _end
0000000000201008 g    D  .bss	0000000000000000  Base        __bss_start
00000000000004c0 g    DF .init	0000000000000000  Base        _init
0000000000000600 g    DF .fini	0000000000000000  Base        _fini


Tal como se observa, prácticamente no hay símbolos en esta librería, está casi vacía. Esto se debe a que Debian la ha separado en dos librerías independientes específicas para IPv4 e IPv6:

root@devuan:~#  ll /usr/lib/x86_64-linux-gnu/libip*
lrwxrwxrwx 1 root root    17 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip4tc.so -> libip4tc.so.0.1.0
lrwxrwxrwx 1 root root    17 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip4tc.so.0 -> libip4tc.so.0.1.0
-rw-r--r-- 1 root root 27088 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip4tc.so.0.1.0
lrwxrwxrwx 1 root root    17 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip6tc.so -> libip6tc.so.0.1.0
lrwxrwxrwx 1 root root    17 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip6tc.so.0 -> libip6tc.so.0.1.0
-rw-r--r-- 1 root root 31184 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libip6tc.so.0.1.0
lrwxrwxrwx 1 root root    16 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libiptc.so -> libiptc.so.0.0.0
lrwxrwxrwx 1 root root    16 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libiptc.so.0 -> libiptc.so.0.0.0
-rw-r--r-- 1 root root  5832 Apr 12  2017 /usr/lib/x86_64-linux-gnu/libiptc.so.0.0.0

Se trata de libip4tc e libip6tc respectivamente. Es posible comprobar que el símbolo "iptc_strerror", reportado como desconocido por el linker al enlazar dinámicamente, se encuentra en la librería libip4tc:

root@devuan:~#  objdump -T /usr/lib/x86_64-linux-gnu/libip4tc.so | grep iptc_strerror
0000000000001a80 g    DF .text	00000000000000af  Base        iptc_strerror

La solución a este error es conocida y fue reportada por primera vez hace bastante tiempo en el repositorio en GitHub de collectd, habiendo ocurrido en un sistema Ubuntu (ver Referencias). Consiste en implementar un wrapper para el binario collectd que precargue ambas librerías libip4tc y libip6tc:

root@devuan:~#  nano /opt/collectd/sbin/collectd.wrapper
#!/bin/sh

LIBS="/usr/lib/x86_64-linux-gnu/libip4tc.so /usr/lib/x86_64-linux-gnu/libip6tc.so"

export LD_PRELOAD="$LD_PRELOAD $LIBS"
exec /opt/collectd/sbin/collectd "$@"

El truco consiste en indicarle al linker, a través de la variable de entorno LD_PRELOAD, que debe cargar ambas librerías antes que el resto de los objetos. Los ítems en esta variable de entorno pueden estar separados por espacios o comas.

Luego se debe otorgar permisos de ejecución al wrapper:

root@devuan:~#  chmod +x /opt/collectd/sbin/collectd.wrapper

Y modificar al script de inicio del servicio para que lance al wrapper en lugar del binario /opt/collectd/sbin/collectd:

root@devuan:~#  nano /etc/init.d/collectd

En el script provisto en mi repositorio de GitHub basta con alterar la variable NAME de la siguiente forma:


    NAME=collectd.wrapper

Al reiniciar collectd, el plugin iptables funciona correctamente y levanta los contadores del firewall con éxito.

Referencias


Tal vez pueda interesarte


Compartí este artículo