Cuando vamos a establecer una comunicación entre dos máquinas a través de una red TCP/IP, vamos lo que estamos haciendo a diario miles de veces con nuestro ordenador mientras navegamos por Internet, lo hacemos a través de un puerto. Imaginemos que tenemos un sistema de comunicación rudimentario entre 10 amigos, con 5 cables y, cada cable, nos permite hablar con uno de nuestros amigos, pero claro, como sólo tenemos 5 cables, sólo podemos hablar con 5 amigos al mismo tiempo, así que en algún punto del recorrido deberá haber alguien con la capacidad de enchufar y desenchufar esos cables. El caso es que esos cables serían los canales, o puertos de nuestro sistema, alguien deberá enchufar y desenchufar cables para asegurarse de que nos conectemos con quien queremos.

Por lo tanto, yo tengo 5 cables que corresponden con las 5 conexiones que puedo tener simultáneas, ahora, por seguir un orden, si quiero conectarme con Alberto, éste se conectará con mi canal 1, aunque nadie me dice a mí que yo también esté en su canal 1. Ahora bien, conecto con Bruno por el canal 2 y cierro a Alberto, para conectar con Carla, que podemos enchufarla en el 1, ya que está libre… ahora Bruno y Alberto contactarán entre ellos, cada uno por alguno de los canales que tenga libre. Bueno, ¿se capta la idea?

Hablando de TCP/IP tenemos algunos más de 5 puertos (podríamos tener 65535 puertos) y serían las conexiones que podemos tener abiertas al mismo tiempo desde una máquina, o al menos desde una misma dirección IP. Y, hablando de nuestro ordenador, cada programa que tenemos en ejecución y que hace conexiones de red puede estar utilizando determinados puertos para establecer las comunicaciones. Por ejemplo, nuestro navegador, programas de chat, correo, música online, sincronización de información y demás tendrán ciertos puertos abiertos para utilizar dichos servicios. Algunos puertos estarán abiertos en modo escucha (para que otros se conecten con nosotros) y otros puertos serán las conexiones establecidas (nos hemos conectado a un puerto en modo escucha, en nuestro equipo, o en un equipo de la red).

Entonces, las aplicaciones que corren en nuestro sistema pueden tener determinados puertos abiertos para realizar su trabajo.

Nos gusta el cacharreo

Ahora bien, puede que si estamos desarrollando un programa que haga uso de la red, nuestro programa deje un puerto abierto y se haya quedado bloqueado. O también puede ser que estemos intentando poner a prueba la gestión de errores de un software, o estemos haciendo algo de ingeniería inversa. En cualquier modo, lo que queremos es cerrar sin piedad un puerto en nuestro ordenador.

Tenemos que tener cuidado porque, si lo hacemos sin control, puede que algún programa haga cosas muy raras. Además, necesitaremos permisos de root para algunas cosas, por lo que serán acciones privilegiadas.

En mi caso, casi siempre que hago esto es para experimentar. Por ejemplo, ¿cómo se comportaría mi programa si cierro la conexión de repente? Sin que ninguna de las dos partes se lo espere. Como también está bien utilizar iptables para restringir las futuras comunicaciones de mi aplicación (sería algo así como tirar del cable). El objetivo es que nuestro software sea más rubusto y sepa qué hacer en este tipo de casos.

Ver qué programa utiliza qué puerto

Lo primero que debemos hacer es averiguar qué proceso está utilizando el puerto que queremos. Con netstat.



netstat -tanp (No todos los procesos pueden ser identificados, no hay información de propiedad del proceso no se mostrarán, necesita ser superusuario para verlos todos.) Conexiones activas de Internet (servidores y establecidos) Proto Recib Enviad Dirección local Dirección remota Estado PID/Program name tcp 0 0 127.0.0.1:631 0.0.0.0:* ESCUCHAR -- tcp 0 0 0.0.0.0:45847 0.0.0.0:* ESCUCHAR 18904/nc tcp 0 0 0.0.0.0:17500 0.0.0.0:* ESCUCHAR 19556/dropbox tcp 0 0 127.0.0.1:4381 0.0.0.0:* ESCUCHAR 30420/spotify tcp 0 0 192.168.0.7:60892 64.233.167.188:5228 ESTABLECIDO 22144/libpepflashpl tcp 0 0 192.168.0.7:42090 157.55.235.157:40008 ESTABLECIDO 1992/skype tcp 0 0 192.168.0.7:39342 xx.xxx.xxx.xx:28 ESTABLECIDO 7428/ssh tcp 3775 0 127.0.0.1:35381 127.0.0.1:37516 ESTABLECIDO -- tcp 0 0 192.168.0.7:48130 192.30.253.124:443 ESTABLECIDO 24441/firefox tcp 0 1 192.168.0.7:33380 92.189.149.89:35148 FIN_WAIT1 -- tcp 0 1 192.168.0.7:33988 92.189.149.89:35148 FIN_WAIT1 -- tcp 0 1 192.168.0.7:39694 37.35.181.204:63905 FIN_WAIT1 -- tcp 0 0 192.168.0.7:60244 xx.xxx.xxx.xx:143 ESTABLECIDO 3708/thunderbird tcp 0 0 192.168.0.7:60544 138.68.115.83:443 TIME_WAIT -- tcp 0 0 127.0.0.1:50542 127.0.0.1:35381 ESTABLECIDO 7345/emacs tcp 0 0 192.168.0.7:53492 72.21.206.121:443 ESTABLECIDO 24441/firefox tcp 0 0 127.0.0.1:37516 127.0.0.1:35381 ESTABLECIDO 7345/emacs

-t: Sólo muestra conexiones TCP.

-a: Muestra también los puertos que estamos escuchando

-n: No resuelve las direcciones. (Es mucho más rápido, pero veremos sólo IPs y no nombres)

-p: Muestra los procesos que originan dichas conexiones. (pid y nombre, si es posible)

Sólo he puesto una pequeña muestra. Con los argumentos tanp conseguimos:

Vemos que muchos programas no aparecen y eso puede ser debido a los privilegios con los que hemos ejecutado el programa, por lo que si utilizamos sudo deberíamos ver todos o casi todos. Por supuesto, la salida de netstat la podemos pasar por grep, para realizar un filtrado de lo que nos interesa:



netstat -tanp | grep 40008 tcp 0 0 192.168.0.7:42090 157.55.235.157:40008 ESTABLECIDO 1992/skype

Si queremos más información sobre las conexiones abiertas y su asociación con procesos, en lugar de netstat podremos utilizar lsof. Aunque con lsof veremos muchísima información, ya que no sólo se centra en conexiones de red, sino que veremos cada descriptor de archivo abierto en el sistema. Y como las conexiones de red suelen tener un descriptor de archivo asociado, las veremos también . Por lo que, con lsof, podremos visualizar todos los descriptores abiertos por todos los procesos. Por ejemplo:



sudo lsof -n | grep emacs | grep TCP emacs 7345 gaspy 17u IPv4 40529162 0t0 TCP 127.0.0.1:49778->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 18u IPv4 40538567 0t0 TCP 127.0.0.1:50542->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 19u IPv4 40585112 0t0 TCP 127.0.0.1:53658->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 20u IPv4 40595540 0t0 TCP 127.0.0.1:53728->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 21u IPv4 41279086 0t0 TCP 127.0.0.1:37436->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 22u IPv4 41283195 0t0 TCP 127.0.0.1:37516->127.0.0.1:35381 (ESTABLISHED) emacs 7345 gaspy 23u IPv4 41284210 0t0 TCP 127.0.0.1:37578->127.0.0.1:35381 (ESTABLISHED)

Con lo que veremos las conexiones TCP que ha establecido mi aplicación Emacs.

También podemos conocer con fuser, el proceso que origina la conexión. Conociendo el puerto (por ejemplo 35381):



fuser -n tcp 35381 35381/tcp: 7359

Matando procesos

Si queremos ser rápidos, lo más fácil para cerrar la conexión de manera abrupta, y una buena forma para probar nuestros programas es matarlos. Como muchos sabréis podemos hacerlo con kill.



kill 7345

killall emacs

O con, pero tenemos que tener cuidado si hay varios procesos con el mismo nombre.

Aunque tanto kill como killall, por defecto intentan cerrar la aplicación educadamente, y la aplicación puede elegir cerrarse o no. Incluso puede estar bloqueada, por lo que si queremos podemos utilizar el argumento -9 o -SIGKILL , con lo que el programa no tendrá más remedio que terminar, es más, no tiene elección, ya que es el sistema operativo el que lo mata.

De todas formas, una forma algo más segura es matar al proceso que tiene un determinado puerto. Igual que antes veíamos la información con fuser. Este comando también mata aplicaciones, directamente enviando SIGKILL, es decir, matando, sin pedir permiso, las aplicaciones (a no ser que lo especifiquemos en los argumentos):



fuser -k -n tcp 8080 8080/tcp: 23003

Método artesano que sólo cierra conexiones

Me encanta este método. Con él hacemos que el proceso entre en depuración y lo matamos. Una cosa importante a tener en cuenta, como dije antes, son los descriptores. Cada conexión, o fichero abierto tiene un descriptor único para la aplicación en la que se encuentra. Y suelen ser números pequeños y secuenciales. Así como la salida estándar por pantalla suele ser el 1, la salida de error el 2, y la entrada por teclado el 0, cuando abrimos el primer fichero, se le asignará el 3 y cuando creamos una conexión después el 4, y así sucesivamente. Entonces, veamos cómo al ejecutar lsof, vemos en mi cuarta columna, generalmente unos números. Para hacer el ejemplo más legible, he ejecutado netcat para abrir un puerto, así:



nc -l 54321

fuser -n tcp 54321 54321/tcp: 23101

lsof -np 23101 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nc 23101 gaspy cwd DIR 8,22 12288 7602177 /home/gaspy nc 23101 gaspy rtd DIR 8,18 4096 2 / nc 23101 gaspy txt REG 8,18 31248 129397 /bin/nc.openbsd nc 23101 gaspy mem REG 8,18 1868984 426046 /lib/x86_64-linux-gnu/libc-2.23.so nc 23101 gaspy mem REG 8,18 101200 400728 /lib/x86_64-linux-gnu/libresolv-2.23.so nc 23101 gaspy mem REG 8,18 81040 393967 /lib/x86_64-linux-gnu/libbsd.so.0.8.2 nc 23101 gaspy mem REG 8,18 162632 396890 /lib/x86_64-linux-gnu/ld-2.23.so nc 23101 gaspy 0u CHR 136,14 0t0 17 /dev/pts/14 nc 23101 gaspy 1u CHR 136,14 0t0 17 /dev/pts/14 nc 23101 gaspy 2u CHR 136,14 0t0 17 /dev/pts/14 nc 23101 gaspy 3u IPv4 139144920 0t0 TCP *:54321 (LISTEN)

Luego, para averiguar el ID de proceso, lo podemos hacer con fuser, por ejemplo, aunque podemos utilizar cualquier comando de los explicados anteriormente:Ya que tenemos nuestro PID, 23101, vemos los descriptores abiertos por ese proceso:

Al final, veremos 0u, 1u, 2u, 3u (la u indica que es de lectura/escritura; r, sería lectura y w, escritura. Aunque algunas veces tenemos descriptores de lectura/escritura que no son necesariamente así… pero eso es otra historia). Lo que nos interesa es que el descriptor 3 es el que corresponde con la conexión que queremos cerrar. Ahora, podemos utilizar gdb para depurar el programa e introducir un comando close() a dicho descriptor, para cerrarlo (salen muchas letras, pero es sencillo):



sudo gdb -p 23101 GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1 Copyright (C) 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type «show copying» and «show warranty» for details. This GDB was configured as «x86_64-linux-gnu». Type «show configuration» for configuration details. Para las instrucciones de informe de errores, vea: . Find the GDB manual and other documentation resources online at: . For help, type «help». Type «apropos word» to search for commands related to «word». Adjuntando a process 23101 Leyendo símbolos desde /bin/nc.openbsd…(no se encontraron símbolos de depuración)hecho. Leyendo símbolos desde /lib/x86_64-linux-gnu/libbsd.so.0…(no se encontraron símbolos de depuración)hecho. Leyendo símbolos desde /lib/x86_64-linux-gnu/libresolv.so.2…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/libresolv-2.23.so…hecho. hecho. Leyendo símbolos desde /lib/x86_64-linux-gnu/libc.so.6…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/libc-2.23.so…hecho. hecho. Leyendo símbolos desde /lib64/ld-linux-x86-64.so.2…Leyendo símbolos desde /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.23.so…hecho. hecho. 0x00007ff39bffa060 in __accept_nocancel () at ../sysdeps/unix/syscall-template.S:84 84 ../sysdeps/unix/syscall-template.S: No existe el archivo o el directorio. call close(3) 1 = 0 quit Una sesión de depuración está activa. Inferior 1 [process 23355] will be detached. ¿Salir de cualquier modo? (y or n) y Separando desde el programa: /bin/nc.openbsd, process 23355

Eso sí, puede que el programa que estamos corriendo dependa de la conexión, o ese sea su único propósito, por ejemplo netcat o telnet, por lo que una vez cerrada la conexión se cierran. Aunque otros programas utilizan las conexiones de red realizan otras tareas como por ejemplo mantener un entorno gráfico, monitorizar el sistema, actualizar un fichero, etc; deberían poder gestionar correctamente ese cierre de conexión sin sufrir ningún desastre.

killcx, o cerrar conexiones educadamente

Ésta es una de esas herramientas (escrita en Perl) que deben estar en nuestra caja de herramientas de sysadmin. Y sirve para matar conexiones, aunque lo hace de un modo seguro. Es más, es capaz de cerrar una conexión que se ha quedado bloqueada en el sistema. Para utilizarla debemos hacer:



sudo ./killcx 127.0.0.1:54321 lo killcx v1.0.3 -- (c)2009-2011 Jerome Bruandet -- http://killcx.sourceforge.net/ [PARENT] checking connection with [127.0.0.1:54321] [PARENT] found connection with [127.0.0.1:44904] (ESTABLISHED) [PARENT] forking child [CHILD] setting up filter to sniff ACK on [lo] for 5 seconds [PARENT] sending spoofed SYN to [127.0.0.1:44904] with bogus SeqNum [CHILD] hooked ACK from [127.0.0.1:44904] [CHILD] found AckNum [3556219343] and SeqNum [477005426] [CHILD] sending spoofed RST to [127.0.0.1:44904] with SeqNum [3556219343] [CHILD] sending RST to remote host as well with SeqNum [477005426] [CHILD] all done, sending USR1 signal to parent [23768] and exiting [PARENT] received child signal, checking results… => success : connection has been closed !

La IP y puerto variarán según nuestras necesidades, y lo es el dispositivo, que podrá ser eth0, wlan0, etc.

La herramienta podemos descargarla desde aquí.

Foto principal: Alex Lehner