Mostrando entradas con la etiqueta network. Mostrar todas las entradas
Mostrando entradas con la etiqueta network. Mostrar todas las entradas

sábado, 6 de mayo de 2017

Understanding SOMAXCONN parameter Part I

This post is kind of a fork of something I worked on the previous week and tries to shed some light on what somaxconn parameter means in the context of Linux Network stack. I've tried to prepare something similar to what I did in TCP Keep Alive - how it works, with examples and some traffic captures so I guess this article will be a bit longer that [I|you]'d like xD, but useful (hopefully).

somaxconn


By definition this value is the maximum number of established connections a socket may have queuing waiting to be accepted by the process holding the socket. This is completely different from tcp_syn_max_backlog since this value is for incomplete connection (connections that haven't been ACKed yet).

To give more context to that statement is necessary to understand a bit more about sockets. When a socket is created, before being able to receive connections two things must happen:
  1. It has to be bind() to a local address.
  2. It has to bet set to listen() for connections.
The listen() syscall is key here, it receives 2 parameters:
Long time ago (before kernel 2.4.25) somaxconn value used to be hardcoded in the kernel, nowadays you can read and update using sysctl as with the rest of the network stack parameters. In most systems the default value is 128, like in my CentOS VM:

[root@server juan]# sysctl -a --pattern net.core.somaxconn
net.core.somaxconn = 128
[root@server juan]#

Test scenario


In order to test and confirm the behavior of somaxconn I have the following setup:
  • Client VM (IP 192.168.0.17) running a C binary (source here) that will start 25 TCP connections to the Server VM on port 8080, in each connection it will send 4 bytes "juan". The binary opens 1 connection per second, and after having opened the 25 connections just waits for 2 minutes to finish.
  • Server VM (IP 192.168.0.26) running ncat (nc, netcat, whatever you like to call it) listening on port 8080, and the maximum open files has been set to 9 (ulimit -n 9) to achieve a condition where we can see established connections being queued easier.
A few more details you should keep in mind:
  • The version of nc shipped with CentOS sets a backlog of 10 on the listen() syscall (using strace):
[juan@server ~]$ ulimit -n 9
[juan@server ~]$ strace nc -lk 8080
execve("/usr/bin/nc", ["nc", "-lk", "8080"], [/* 24 vars */]) = 0
brk(0)                                  = 0x1dd7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe7075e9000
...
bind(3, {sa_family=AF_INET6, sin6_port=htons(8080), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 128) = 0
listen(3, 10)                           = 0
fcntl(3, F_GETFL)
...
  • So we have a backlog of 10 set in the listen syscall and a limit of 9 open files. I will use this value as somaxconn since the meaning is the same.
  • After nc was just started we can see it has already used 5 FD in use (STDIN, STDOUT, STDERR, and two more for the listening sockets on IPv4 and IPv6)
[root@server juan]# lsof -p 14136|grep "CHR\|IPv"
nc      14136 juan    0u   CHR     136,0      0t0        3 /dev/pts/0
nc      14136 juan    1u   CHR     136,0      0t0        3 /dev/pts/0
nc      14136 juan    2u   CHR     136,0      0t0        3 /dev/pts/0
nc      14136 juan    3u  IPv6 488270957      0t0      TCP *:webcache (LISTEN)
nc      14136 juan    4u  IPv4 488270958      0t0      TCP *:webcache (LISTEN)
[root@server juan]#

With all these details in mind and some basic math (2+2=4 :D) we can expect the following behavior:
  • Since 5 out of the 9 available file descriptors are already in use, nc should not be able to receive more than 4 new connections on port 8080. Each new connection will create a new socket that has to be accessible in user space through a file descriptor (have a look at accept() syscall), which can't be created if the max open files limit has been reached.
  • The C binary will issue 25 connections:
    • The first 4 will be answered just fine by nc and the 4 bytes will be read from the socket and printed to STDOUT.
    • The next 10 connections will be indeed accepted by the Network layer of the Server VM, and even the 4 bytes will be received, however nc won't be able to accept the socket and read the 4 bytes. These are the established connections being queued!!!
    •  The remaining 11 connections will look as ESTABLISHED from the client as they have completed the TCP handshake however the 4 bytes won't be ACKnowledged by Network Stack because the queue is full (somaxconn limit has been reached). From the server the connections won't even exist.

Binary execution:

juan@test:~/TCP_connections$ gcc -o tcp_connections tcp_connections.c
juan@test:~/TCP_connections$ ./tcp_connections
Socket opened FD=3, connection number 0
Socket opened FD=4, connection number 1
Socket opened FD=5, connection number 2
Socket opened FD=6, connection number 3
Socket opened FD=7, connection number 4
Socket opened FD=8, connection number 5
Socket opened FD=9, connection number 6
Socket opened FD=10, connection number 7
Socket opened FD=11, connection number 8
Socket opened FD=12, connection number 9
Socket opened FD=13, connection number 10
Socket opened FD=14, connection number 11
Socket opened FD=15, connection number 12
Socket opened FD=16, connection number 13
Socket opened FD=17, connection number 14
Socket opened FD=18, connection number 15
Socket opened FD=19, connection number 16
Socket opened FD=20, connection number 17
Socket opened FD=21, connection number 18
Socket opened FD=22, connection number 19
Socket opened FD=23, connection number 20
Socket opened FD=24, connection number 21
Socket opened FD=25, connection number 22
Socket opened FD=26, connection number 23
Socket opened FD=27, connection number 24
juan@test:~/TCP_connections$

Nc execution on the server


Note: I introduced the new line after the 4th connections just to split them from the rest:
[juan@server ~]$ nc -lvk -p 8080
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Listening on :::8080
Ncat: Listening on 0.0.0.0:8080
Ncat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49026.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49027.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49028.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49029.
juan

Ncat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49030.
Ncat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49031.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49032.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49033.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49034.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49035.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49036.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49037.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49038.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49039.
juanNcat: Connection from 192.168.0.17.
Ncat: Connection from 192.168.0.17:49040.
juanjuan 

Results


To have a  look at what was happening during the test I measured 3 different locations:
  1. TCP connections on the client using ss.
  2. TCP connections on the server using ss.
  3. Captured network traffic on the server using tcpdump.
These 3 locations provided enough information to understand what is going on and to even find something interesting :D.

TCP connections on the client:


I've chopped off a couple of lines for the sake of space and time.  The samples were taken every 2 seconds and only TCP connections to port 8080 were considered:

root@test:/home/juan# for i in {1..100}; do ss -ntpe|grep "State\|8080"; echo ---; sleep 2; done
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
---
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49027         192.168.0.26:8080   users:(("tcp_connections",3342,4)) uid:1000 ino:23350 sk:ffff8800b4914140
ESTAB      0      0              192.168.0.17:49028         192.168.0.26:8080   users:(("tcp_connections",3342,5)) uid:1000 ino:23368 sk:ffff8800b4910e80
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
---
...
---
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49031         192.168.0.26:8080   users:(("tcp_connections",3342,8)) uid:1000 ino:23408 sk:ffff8800b4911d00
ESTAB      0      0              192.168.0.17:49027         192.168.0.26:8080   users:(("tcp_connections",3342,4)) uid:1000 ino:23350 sk:ffff8800b4914140
ESTAB      0      0              192.168.0.17:49029         192.168.0.26:8080   users:(("tcp_connections",3342,6)) uid:1000 ino:23372 sk:ffff8800b4910740
ESTAB      0      0              192.168.0.17:49030         192.168.0.26:8080   users:(("tcp_connections",3342,7)) uid:1000 ino:23390 sk:ffff8800b49115c0
ESTAB      0      0              192.168.0.17:49028         192.168.0.26:8080   users:(("tcp_connections",3342,5)) uid:1000 ino:23368 sk:ffff8800b4910e80
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
ESTAB      0      0              192.168.0.17:49033         192.168.0.26:8080   users:(("tcp_connections",3342,10)) uid:1000 ino:23445 sk:ffff8800b4912b80
ESTAB      0      0              192.168.0.17:49034         192.168.0.26:8080   users:(("tcp_connections",3342,11)) uid:1000 ino:23447 sk:ffff8800b49132c0
ESTAB      0      0              192.168.0.17:49032         192.168.0.26:8080   users:(("tcp_connections",3342,9)) uid:1000 ino:23426 sk:ffff8800b4912440
---
...


After a few seconds, we can see a few ESTABLISHED tcp connections using source ports from 49026 to 49034, 9 connections to be more precise, and they all show 0 bytes in Send-Q which suggests the 4 bytes sent by the application were actually acknowledged by the server.

...
---
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49031         192.168.0.26:8080   users:(("tcp_connections",3342,8)) uid:1000 ino:23408 sk:ffff8800b4911d00
ESTAB      0      0              192.168.0.17:49027         192.168.0.26:8080   users:(("tcp_connections",3342,4)) uid:1000 ino:23350 sk:ffff8800b4914140
ESTAB      0      0              192.168.0.17:49029         192.168.0.26:8080   users:(("tcp_connections",3342,6)) uid:1000 ino:23372 sk:ffff8800b4910740
ESTAB      0      0              192.168.0.17:49030         192.168.0.26:8080   users:(("tcp_connections",3342,7)) uid:1000 ino:23390 sk:ffff8800b49115c0
ESTAB      0      0              192.168.0.17:49028         192.168.0.26:8080   users:(("tcp_connections",3342,5)) uid:1000 ino:23368 sk:ffff8800b4910e80
ESTAB      0      0              192.168.0.17:49037         192.168.0.26:8080   users:(("tcp_connections",3342,14)) uid:1000 ino:23502 sk:ffff8800b4917400
ESTAB      0      0              192.168.0.17:49035         192.168.0.26:8080   users:(("tcp_connections",3342,12)) uid:1000 ino:23465 sk:ffff8800b4913a00
ESTAB      0      0              192.168.0.17:49036         192.168.0.26:8080   users:(("tcp_connections",3342,13)) uid:1000 ino:23484 sk:ffff8800b4915e40
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
ESTAB      0      0              192.168.0.17:49038         192.168.0.26:8080   users:(("tcp_connections",3342,15)) uid:1000 ino:23520 sk:ffff8800b4916580
ESTAB      0      0              192.168.0.17:49033         192.168.0.26:8080   users:(("tcp_connections",3342,10)) uid:1000 ino:23445 sk:ffff8800b4912b80
ESTAB      0      0              192.168.0.17:49034         192.168.0.26:8080   users:(("tcp_connections",3342,11)) uid:1000 ino:23447 sk:ffff8800b49132c0
ESTAB      0      0              192.168.0.17:49040         192.168.0.26:8080   users:(("tcp_connections",3342,17)) uid:1000 ino:23557 sk:ffff8800b4914880
ESTAB      0      0              192.168.0.17:49039         192.168.0.26:8080   users:(("tcp_connections",3342,16)) uid:1000 ino:23539 sk:ffff8800b4915700
ESTAB      0      0              192.168.0.17:49032         192.168.0.26:8080   users:(("tcp_connections",3342,9)) uid:1000 ino:23426 sk:ffff8800b4912440
---
...

A few more seconds later we see even more TCP connections in ESTABLISHED state, including src ports from 49035 to 49040. So we have a total of 15 ESTABLISHED connections with 0 bytes waiting to be ACKnowledged by the server. Remember we were expecting 14 (4 + 10), but instead we got 15 (4 + 11), odd right? I'll get back to this later...

...
---
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49031         192.168.0.26:8080   users:(("tcp_connections",3342,8)) uid:1000 ino:23408 sk:ffff8800b4911d00
ESTAB      0      0              192.168.0.17:49027         192.168.0.26:8080   users:(("tcp_connections",3342,4)) uid:1000 ino:23350 sk:ffff8800b4914140
ESTAB      0      0              192.168.0.17:49029         192.168.0.26:8080   users:(("tcp_connections",3342,6)) uid:1000 ino:23372 sk:ffff8800b4910740
ESTAB      0      0              192.168.0.17:49030         192.168.0.26:8080   users:(("tcp_connections",3342,7)) uid:1000 ino:23390 sk:ffff8800b49115c0
ESTAB      0      0              192.168.0.17:49028         192.168.0.26:8080   users:(("tcp_connections",3342,5)) uid:1000 ino:23368 sk:ffff8800b4910e80
ESTAB      0      0              192.168.0.17:49037         192.168.0.26:8080   users:(("tcp_connections",3342,14)) uid:1000 ino:23502 sk:ffff8800b4917400
ESTAB      0      0              192.168.0.17:49035         192.168.0.26:8080   users:(("tcp_connections",3342,12)) uid:1000 ino:23465 sk:ffff8800b4913a00
ESTAB      0      0              192.168.0.17:49036         192.168.0.26:8080   users:(("tcp_connections",3342,13)) uid:1000 ino:23484 sk:ffff8800b4915e40
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
ESTAB      0      0              192.168.0.17:49038         192.168.0.26:8080   users:(("tcp_connections",3342,15)) uid:1000 ino:23520 sk:ffff8800b4916580
ESTAB      0      0              192.168.0.17:49033         192.168.0.26:8080   users:(("tcp_connections",3342,10)) uid:1000 ino:23445 sk:ffff8800b4912b80
ESTAB      0      0              192.168.0.17:49034         192.168.0.26:8080   users:(("tcp_connections",3342,11)) uid:1000 ino:23447 sk:ffff8800b49132c0
ESTAB      0      0              192.168.0.17:49040         192.168.0.26:8080   users:(("tcp_connections",3342,17)) uid:1000 ino:23557 sk:ffff8800b4914880
ESTAB      0      4              192.168.0.17:49041         192.168.0.26:8080   timer:(on,1.208ms,3) users:(("tcp_connections",3342,18)) uid:1000 ino:23575 sk:ffff8800b4916cc0
ESTAB      0      0              192.168.0.17:49039         192.168.0.26:8080   users:(("tcp_connections",3342,16)) uid:1000 ino:23539 sk:ffff8800b4915700
ESTAB      0      0              192.168.0.17:49032         192.168.0.26:8080   users:(("tcp_connections",3342,9)) uid:1000 ino:23426 sk:ffff8800b4912440
ESTAB      0      4              192.168.0.17:49042         192.168.0.26:8080   timer:(on,608ms,2) users:(("tcp_connections",3342,19)) uid:1000 ino:23593 sk:ffff8800b4910000
---
...

This new sample shows 2 lines that are different than the rest, the ones using src ports 49041 and 49042.  These were the first 2 connections that couldn't fit in the somaxconn queue!!! And even more, you can see how the 4 bytes that were sent from the client haven't been acknowledged by the server and therefore they show up in the Send-Q column. Note how the connection looks as ESTABLISHED for the client though, this is because the 3-way handshake was completed from the client point of view.

...
---
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
ESTAB      0      0              192.168.0.17:49031         192.168.0.26:8080   users:(("tcp_connections",3342,8)) uid:1000 ino:23408 sk:ffff8800b4911d00
ESTAB      0      0              192.168.0.17:49027         192.168.0.26:8080   users:(("tcp_connections",3342,4)) uid:1000 ino:23350 sk:ffff8800b4914140
ESTAB      0      0              192.168.0.17:49029         192.168.0.26:8080   users:(("tcp_connections",3342,6)) uid:1000 ino:23372 sk:ffff8800b4910740
ESTAB      0      4              192.168.0.17:49047         192.168.0.26:8080   timer:(on,460ms,4) users:(("tcp_connections",3342,24)) uid:1000 ino:23632 sk:ffff8800a6efba00
ESTAB      0      4              192.168.0.17:49050         192.168.0.26:8080   timer:(on,132ms,3) users:(("tcp_connections",3342,27)) uid:1000 ino:23635 sk:ffff8800a6efc140
ESTAB      0      0              192.168.0.17:49030         192.168.0.26:8080   users:(("tcp_connections",3342,7)) uid:1000 ino:23390 sk:ffff8800b49115c0
ESTAB      0      0              192.168.0.17:49028         192.168.0.26:8080   users:(("tcp_connections",3342,5)) uid:1000 ino:23368 sk:ffff8800b4910e80
ESTAB      0      4              192.168.0.17:49045         192.168.0.26:8080   timer:(on,4.740ms,5) users:(("tcp_connections",3342,22)) uid:1000 ino:23630 sk:ffff8800a6efcfc0
ESTAB      0      4              192.168.0.17:49049         192.168.0.26:8080   timer:(on,2.336ms,4) users:(("tcp_connections",3342,26)) uid:1000 ino:23634 sk:ffff8800a6efc880
ESTAB      0      0              192.168.0.17:49037         192.168.0.26:8080   users:(("tcp_connections",3342,14)) uid:1000 ino:23502 sk:ffff8800b4917400
ESTAB      0      0              192.168.0.17:49035         192.168.0.26:8080   users:(("tcp_connections",3342,12)) uid:1000 ino:23465 sk:ffff8800b4913a00
ESTAB      0      4              192.168.0.17:49043         192.168.0.26:8080   timer:(on,2.740ms,5) users:(("tcp_connections",3342,20)) uid:1000 ino:23611 sk:ffff8800a6efde40
ESTAB      0      0              192.168.0.17:49036         192.168.0.26:8080   users:(("tcp_connections",3342,13)) uid:1000 ino:23484 sk:ffff8800b4915e40
ESTAB      0      4              192.168.0.17:49044         192.168.0.26:8080   timer:(on,3.732ms,5) users:(("tcp_connections",3342,21)) uid:1000 ino:23616 sk:ffff8800a6efd700
ESTAB      0      4              192.168.0.17:49046         192.168.0.26:8080   timer:(on,5.740ms,5) users:(("tcp_connections",3342,23)) uid:1000 ino:23631 sk:ffff8800a6eff400
ESTAB      0      0              192.168.0.17:49026         192.168.0.26:8080   users:(("tcp_connections",3342,3)) uid:1000 ino:23326 sk:ffff8800b4914fc0
ESTAB      0      4              192.168.0.17:49048         192.168.0.26:8080   timer:(on,1.336ms,4) users:(("tcp_connections",3342,25)) uid:1000 ino:23633 sk:ffff8800a6efecc0
ESTAB      0      0              192.168.0.17:49038         192.168.0.26:8080   users:(("tcp_connections",3342,15)) uid:1000 ino:23520 sk:ffff8800b4916580
ESTAB      0      0              192.168.0.17:49033         192.168.0.26:8080   users:(("tcp_connections",3342,10)) uid:1000 ino:23445 sk:ffff8800b4912b80
ESTAB      0      0              192.168.0.17:49034         192.168.0.26:8080   users:(("tcp_connections",3342,11)) uid:1000 ino:23447 sk:ffff8800b49132c0
ESTAB      0      0              192.168.0.17:49040         192.168.0.26:8080   users:(("tcp_connections",3342,17)) uid:1000 ino:23557 sk:ffff8800b4914880
ESTAB      0      4              192.168.0.17:49041         192.168.0.26:8080   timer:(on,732ms,5) users:(("tcp_connections",3342,18)) uid:1000 ino:23575 sk:ffff8800b4916cc0
ESTAB      0      0              192.168.0.17:49039         192.168.0.26:8080   users:(("tcp_connections",3342,16)) uid:1000 ino:23539 sk:ffff8800b4915700
ESTAB      0      0              192.168.0.17:49032         192.168.0.26:8080   users:(("tcp_connections",3342,9)) uid:1000 ino:23426 sk:ffff8800b4912440
ESTAB      0      4              192.168.0.17:49042         192.168.0.26:8080   timer:(on,1.732ms,5) users:(("tcp_connections",3342,19)) uid:1000 ino:23593 sk:ffff8800b4910000
---
^C
root@test:/home/juan#

The last sample shows the remaining connections from ports 49043 to 49050 all of them in the same condition, 4 bytes not being acknowledged.

From the client side samples we can confirm we got 15 complete TCP connections (src ports from 49026 to 49040) and 10 that even though showed up as ESTABLISHED weren't able to get acknowledged the 4 bytes sent by the client (src ports from 49041 to 49050).

TCP connections on the server


Lets have a look now to the samples taken on the server and see if they match with what we found on the client.

[root@server juan]# for i in {1..100}; do ss -tpn|grep "State\|8080"; echo "---"; sleep 2;done
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49026               users:(("nc",pid=13705,fd=5))
---
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49026               users:(("nc",pid=13705,fd=5))
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49028               users:(("nc",pid=13705,fd=7))
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49027               users:(("nc",pid=13705,fd=6))
---
...

The first samples look ok, nothing fancy there, we can see the first 3 TCP connections using src ports from 49026 to 49028.

...
---
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49026               users:(("nc",pid=13705,fd=5))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49031
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49033
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49029               users:(("nc",pid=13705,fd=8))
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49028               users:(("nc",pid=13705,fd=7))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49032
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49030
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49027               users:(("nc",pid=13705,fd=6))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49034
---
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49036
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49026               users:(("nc",pid=13705,fd=5))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49035
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49031
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49033
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49029               users:(("nc",pid=13705,fd=8))
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49028               users:(("nc",pid=13705,fd=7))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49032
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49030
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49027               users:(("nc",pid=13705,fd=6))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49034
---
...

And here it got interesting! We have now 4 connections (src ports between 49026 and 49029) which besides being ESTABLISHED have a FD associated to our nc process. However we have another 7 ESTABLISHED connections (src ports from 49030 to 49036) with no FD associated (and no process either :D)!!! These 7 connections are the ones waiting on the queue for nc to issue the accept() call. nc is actually issuing the accept calls, but the calls are failing due to nc having reached the maximum open files (because of the "ulimit -n 9").
 
...
---
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49036
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49026               users:(("nc",pid=13705,fd=5))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49038
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49035
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49031
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49033
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49029               users:(("nc",pid=13705,fd=8))
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49028               users:(("nc",pid=13705,fd=7))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49032
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49039
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49030
ESTAB      0      0      192.168.0.26:8080               192.168.0.17:49027               users:(("nc",pid=13705,fd=6))
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49037
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49040
ESTAB      4      0      192.168.0.26:8080               192.168.0.17:49034
---
^C
[root@server juan]#

After the binary issued the 25 connections, on the server we can see 4 ESTABLISHED connections (src ports 49026 to 49029) with corresponding FDs and 11 ESTABLISHED connections (src ports 49030 to 49040) with no FD. So... where are the remaining 10 connections that show up as ESTABLISHED on the client??? Well, these 10 connections didn't reach ESTABLISHED state on the server :D as per the documentation of listen():

The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow.  If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.

Considering we are using TCP , the use case matches exactly with the underlined part.

To be continued...

martes, 15 de marzo de 2016

TCP Keep Alive - how it works

Besides being a short entry this will be the first one in English. Why? mmmm, good question, not sure to be honest. I suppose English will make the entries accessible to more people and that is good reason enough :D to me, I'm not saying all of them will be in English from now on (who knows haha).

So a few days ago I faced an awkward situation that kind of pushed me to see how the TCP keep alive feature works on the Linux Kernel. Nothing life changing, but I wanted to share it here for you out there and for me as well xD, is fun how many times I come back to my old entries looking for commands or answers :D.

The problem was quite simple, on the server side a simple netstat showed more than 20k TCP connections on ESTABLISHED state. However all the clients that had started these connections were shutdown... yes, shutdown, they weren't even online. So, how is this possible? Well, you will understand it after this entry, hopefully XD.

Kernel TCP keep-alive configuration


Our lovely Kernel provides 3 parameters to handle TCP keep-alive behavior:
  • tcp_keepalive_time: number of seconds a connection needs to be idle before keep-alive tests begin. The default value is 7200 (2 h), this parameter is valid ONLY if the option SO_KEEPALIVE is set on the socket.
  • tcp_keepalive_intvl: once the keep-alive tests begin, this value states the number of seconds between each test. The default value is 75 (1min 15 sec).
  • tcp_keepalive_probes: the number of probes that must fail before the connection gets terminated. The default value is 9.
Like every Kernel parameter we can access these 3 guys through the /proc FS, here you have them on one of the test instances:

[root@ip-172-31-24-218 ec2-user]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
[root@ip-172-31-24-218 ec2-user]# cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
[root@ip-172-31-24-218 ec2-user]# cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
[root@ip-172-31-24-218 ec2-user]#


 The test scenario is the following:

The test scenario consists of basically 2 EC2 instances:
  • A, IP 172.31.24.219. This will be the source of the connections.
  • B, IP 172.31.24.218. This will be the so called server instance.
to speed up the tests I reduced, on instance B, tcp_keepalive_time from 7200 to 10, the following way:

[root@ip-172-31-24-218 ec2-user]# echo 10 > /proc/sys/net/ipv4/tcp_keepalive_time
[root@ip-172-31-24-218 ec2-user]# cat /proc/sys/net/ipv4/tcp_keepalive_time
10
[root@ip-172-31-24-218 ec2-user]#


Test 1 "Testing keep-alive"


To simulate the server I used the well known TCP swiss army knife, nc :P (same on the client side nc 172.31.24.218 3333). Basically started nc in background listening on port 3333 on instance B and then started tcpdump to capture the traffic coming to that port from instance A:

[ec2-user@ip-172-31-24-218 ~]$ nc -l 3333 &
[1] 2536
[ec2-user@ip-172-31-24-218 ~]$ sudo tcpdump -nn port 3333
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
19:27:27.477761 IP 172.31.24.219.42115 > 172.31.24.218.3333: Flags [S], seq 2777265429, win 26883, options [mss 8961,sackOK,TS val 260487 ecr 0,nop,wscale 6], length 0
19:27:27.477983 IP 172.31.24.218.3333 > 172.31.24.219.42115: Flags [S.], seq 2747775912, ack 2777265430, win 26847, options [mss 8961,sackOK,TS val 260850 ecr 260487,nop,wscale 6], length 0
19:27:27.478456 IP 172.31.24.219.42115 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 260488 ecr 260850], length 0



^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel

[1]+  Stopped                 nc -l 3333
[ec2-user@ip-172-31-24-218 ~]$ date
Tue Mar 15 19:33:09 UTC 2016
[ec2-user@ip-172-31-24-218 ~]$


mmmm, I've colored with blue the TCP Handshake, and you can see that between the time the connection was established 19:27:27 and the date command 19:33:09 we have more than 5 minutes. Not even one single packet was exchanged between the instances, so why didn't the keep-alive process kick in considering we set it to 20 seconds? Well, perhaps nc is not setting SO_KEEPALIVE on the socket when it opens it? What does strace have to say about it:

[ec2-user@ip-172-31-24-218 ~]$ strace nc -l 3333
execve("/usr/bin/nc", ["nc", "-l", "3333"], [/* 32 vars */]) = 0
brk(0)                                  = 0xff9000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff071850000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
...

blah blah blah
...
blah blah blah
...
set_robust_list(0x7ff071847a20, 24)     = 0
rt_sigaction(SIGRTMIN, {0x7ff070d30780, [], SA_RESTORER|SA_SIGINFO, 0x7ff070d39100}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7ff070d30810, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7ff070d39100}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
brk(0)                                  = 0xff9000
brk(0x101a000)                          = 0x101a000
brk(0)                                  = 0x101a000
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
 

bind(3, {sa_family=AF_INET, sin_port=htons(3333), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                            = 0
accept(3, ^CProcess 2690 detached
 
[1]+  Killed                  nc -l 3333
[ec2-user@ip-172-31-24-218 ~]$


Cool, I've highlighted the lines where the socket is created and you can see SO_KEEPALIVE is not being set as an option. So... what now? Lets get dirty!!!

Test 2 "If you don't like it, change it!"


So, why don't we just force nc to use SO_KEEPALIVE option? Hell yeah!!! I downloaded netcat source from the official website and did the following changes on network.c file:

[ec2-user@ip-172-31-24-218 ~]$ diff netcat-0.7.1/src/network.c netcat-0.7.1_juan/src/network.c
374a375,383
>   sockopt = 1;
>   if (type == SOCK_STREAM){
>      ret = setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&sockopt,sizeof(sockopt));
>      if(ret < 0){
>        close(sock);
>        return -2;
>      }
>   }
>
[ec2-user@ip-172-31-24-218 ~]$


as you can see I just added an if statement that will become true if the socket created is SOCK_STREAM (TCP :D), and will set the SO_KEEPALIVE option on the socket. After that I just issued ./configure and make, now strace shows something more interesting:

[ec2-user@ip-172-31-24-218 netcat-0.7.1_juan]$ strace ./src/netcat -l -p 3333
execve("./src/netcat", ["./src/netcat", "-l", "-p", "3333"], [/* 33 vars */]) = 0
brk(0)                                  = 0x125f000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fef806cb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=21099, ...}) = 0
mmap(NULL, 21099, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fef806c5000
close(3)                                = 0

...
blah blah blah
...
blah blah blah
...
read(3, " # Redwood Chat\npdb             "..., 4096) = 4096
read(3, "      # ContinuStor Monitor Port"..., 4096) = 4096
read(3, "   3107/udp                # Bus"..., 4096) = 4096
read(3, "lfap        3145/tcp            "..., 4096) = 4096
read(3, "\nh2gf-w-2m       3179/udp       "..., 4096) = 4096
read(3, "    3212/tcp                # Su"..., 4096) = 4096
read(3, "eo-fe         3245/tcp          "..., 4096) = 4096
read(3, "80/tcp                # VS Serve"..., 4096) = 4096
read(3, "        # SDT License Manager\nof"..., 4096) = 4096
close(3)                                = 0
munmap(0x7fef806ca000, 4096)            = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_LINGER, {onoff=1, linger=0}, 8) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
 

bind(3, {sa_family=AF_INET, sin_port=htons(3333), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 4)                            = 0
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 4
...

blah blah blah
...
select(4, [3], NULL, NULL, NULL^CProcess 6656 detached
 
[ec2-user@ip-172-31-24-218 netcat-0.7.1_juan]$


so now we do have SO_KEEPALIVE in place, lets see if it works:

[ec2-user@ip-172-31-24-218 netcat-0.7.1_juan]$ ./src/netcat -l -p 3333 &
[1] 6657
[ec2-user@ip-172-31-24-218 netcat-0.7.1_juan]$ sudo tcpdump -nn port 3333
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
20:23:48.747981 IP 172.31.24.219.42122 > 172.31.24.218.3333: Flags [S], seq 723623108, win 26883, options [mss 8961,sackOK,TS val 1105802 ecr 0,nop,wscale 6], length 0
20:23:48.748190 IP 172.31.24.218.3333 > 172.31.24.219.42122: Flags [S.], seq 4243448461, ack 723623109, win 26847, options [mss 8961,sackOK,TS val 1106167 ecr 1105802,nop,wscale 6], length 0
20:23:48.748679 IP 172.31.24.219.42122 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 1105803 ecr 1106167], length 0


20:23:58.764759 IP 172.31.24.218.3333 > 172.31.24.219.42122: Flags [.], ack 1, win 420, options [nop,nop,TS val 1108672 ecr 1105803], length 0
20:23:58.765399 IP 172.31.24.219.42122 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 1108307 ecr 1106167], length 0


20:25:13.773009 IP 172.31.24.218.3333 > 172.31.24.219.42122: Flags [.], ack 1, win 420, options [nop,nop,TS val 1127424 ecr 1108307], length 0
20:25:13.773926 IP 172.31.24.219.42122 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 1127059 ecr 1106167], length 0


20:26:29.036973 IP 172.31.24.218.3333 > 172.31.24.219.42122: Flags [.], ack 1, win 420, options [nop,nop,TS val 1146240 ecr 1127059], length 0
20:26:29.037684 IP 172.31.24.219.42122 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 1145876 ecr 1106167], length 0


so our fancy workaround actually worked!! we can see how after 10 seconds of inactivity  the keep-alive probes kick in and they are sent every 75 seconds. The probes are basically ACK packets with no real content, here we can see how the probes are answered by instance A with a plain ACK as well.

Test 3 "Let the show begin"


Ok, now we have SO_KEEPALIVE enabled, therefore we should be able to test the all the parameters. Again, to speed up the process we'll reduce some of them again to the following values:

[root@ip-172-31-24-218 netcat-0.7.1]# echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes
[root@ip-172-31-24-218 netcat-0.7.1]# echo 20 > /proc/sys/net/ipv4/tcp_keepalive_intvl
[root@ip-172-31-24-218 netcat-0.7.1]# cat /proc/sys/net/ipv4/tcp_keepalive_probes
5
[root@ip-172-31-24-218 netcat-0.7.1]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
20
[root@ip-172-31-24-218 netcat-0.7.1]#


now the probes should be sent every 20 seconds and if 5 of them fail, the connection should be terminated. Ok, but if I run the same test again, the probes won't really fail because the connection is perfectly fine, so I need to cause a problem here.

The easiest way to make sure the connection will be idle and that instance A won't answer the keep-alive probes is by dropping all the packets on instance A right after the connection is ready. I did this by adding the following iptables rules right after the connection was established:

iptables -A OUTPUT --dst 172.31.24.218 -j DROP
iptables -A INPUT --src 172.31.24.218 -j DROP


Note: I dropped everything going out to instance B and everything coming in from instance B.

so there we go...

[ec2-user@ip-172-31-24-218 netcat-0.7.1]$ ./src/netcat -l -p 3333 &
[2] 27020
[ec2-user@ip-172-31-24-218 netcat-0.7.1]$ sudo tcpdump -nn port 3333
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
21:57:41.857514 IP 172.31.24.219.42126 > 172.31.24.218.3333: Flags [S], seq 2971447882, win 26883, options [mss 8961,sackOK,TS val 2514081 ecr 0,nop,wscale 6], length 0
21:57:41.857818 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [S.], seq 1633813684, ack 2971447883, win 26847, options [mss 8961,sackOK,TS val 2514445 ecr 2514081,nop,wscale 6], length 0
21:57:41.858211 IP 172.31.24.219.42126 > 172.31.24.218.3333: Flags [.], ack 1, win 421, options [nop,nop,TS val 2514082 ecr 2514445], length 0



21:57:51.884756 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [.], ack 1, win 420, options [nop,nop,TS val 2516952 ecr 2514082], length 0
21:58:11.948771 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [.], ack 1, win 420, options [nop,nop,TS val 2521968 ecr 2514082], length 0
21:58:31.980765 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [.], ack 1, win 420, options [nop,nop,TS val 2526976 ecr 2514082], length 0
21:58:52.012770 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [.], ack 1, win 420, options [nop,nop,TS val 2531984 ecr 2514082], length 0
21:59:12.044761 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [.], ack 1, win 420, options [nop,nop,TS val 2536992 ecr 2514082], length 0
21:59:32.076768 IP 172.31.24.218.3333 > 172.31.24.219.42126: Flags [R.], seq 1, ack 1, win 420, options [nop,nop,TS val 2542000 ecr 2514082], length 0

^C
9 packets captured
9 packets received by filter
0 packets dropped by kernel

[2]+  Stopped                 ./src/netcat -l -p 3333
[ec2-user@ip-172-31-24-218 netcat-0.7.1]$


interesting, right? keep-alive probes kicked in after 10 seconds as expected, then we have a probe every 20 seconds (no answer from instance A), and after the 5th probe we can see a RST+ACK package going from B to A trying to terminate the connection. This is exactly the behavior we were expecting :D.

So coming back to 20k+ ESTABLISHED connections situation, now we can understand why a situation like that is possible. Basically all the clients, for some awkward and really hard to reproduce reason were crashing and not finishing the connections properly, so on the server side due to the fact that the sockets used for the connections weren't using SO_KEEPALIVE option, the connections remained ESTABLISHED for the eternity.

Could this be a big problem? well it might be:

  • The first thing that comes to my mind is wasted memory. These established connections even though they are not being actively used, they need kernel memory to exist, so do the math :D. 
  • If a new connection comes in to the server and the source IP and source port match with one of the connections in ESTABLISHED state that connection will probably fail.
  • If you have a limit number of sockets available on the server I don't think you can afford having them allocated to this Ghost connections.

So why not having TCP keep-alive enabled by default? Good question... I guess that when we are talking about too many connections, handling the keep-alive could put some overhead on the kernel side. Imagine that for every connection the kernel should have to keep track of the time the last packet came in and then set timers to trigger the keep-alive probes.

I hope I was clear enough, otherwise you can always leave a comment :P.

... it wasn't a short entry after all.

lunes, 8 de junio de 2015

UDP en GNU/Linux Parte II

Esta entrada es la continuación de UDP en GNU/Linux Parte I y la idea es continuar con las pruebas para entender un poco mas cómo funciona el stack UDP en GNU/Linux.

En la publicación anterior quedó pendiente probar packet receive errors  en una condición de buffer insuficiente. Dijimos que esta condición se puede dar en un escenario donde los paquetes arriban al host a una tasa mayor que con la que son retirados del buffer del socket. Para comprobar esto vamos a precisar algunos números y un receptor UDP lento.

Receptor UDP lento

La verdad es que no se me ocurrió ninguna idea maravillosa para hacer un receptor lento de paquetes UDP, así que hurgando un poco en internet armé un pequeño cliente.c (si ¬¬ .c, ¿qué tiene de malo?). El cliente básicamente realiza las siguientes tareas:
  • Abre un socket UDP y escucha en el puerto 5555.
  • Imprime en pantalla el tamaño del buffer del socket.
  • Luego en un loop toma del buffer del socket 1 paquete por segundo e imprime en pantalla un contador de paquetes leidos.
Acá pueden ver una ejecución abortada de ejemplo:
juan@ubuntu:~$ ./cliente
--Socket creado!
-Configuracion del socket tamanio del buffer de recepcion: 163840
^C
juan@ubuntu:~$ 


Como ven el tamaño del buffer por defecto es de 163840 bytes (160Kbytes). Este valor no es aleatorio ni nada que se le parezca, el tamaño está definido por la variable de kernel net.core.rmem_default

root@ubuntu:/home/juan# sysctl -a|grep rmem_default
net.core.rmem_default = 163840
root@ubuntu:/home/juan#


por ejemplo, se podría ampliar de la siguiente manera

root@ubuntu:/home/juan# sysctl -w net.core.rmem_default=1638400
net.core.rmem_default = 1638400
root@ubuntu:/home/juan# sysctl -a|grep rmem_default
net.core.rmem_default = 1638400
root@ubuntu:/home/juan# 


y se vería reflejado en la próxima ejecución de nuestro cliente

root@ubuntu:/home/juan# ./cliente
--Socket creado!
-Configuracion del socket tamanio del buffer de recepcion: 1638400
^C
root@ubuntu:/home/juan#  


Entonces, resumiendo los números:
  • Tamaño del buffer de recepción 163840 bytes.
  • Enviaremos paquetes de 1024 bytes forjados con Hping3. Por lo tanto, 160 paquetes son suficientes para llenar el buffer.
  • Se enviarán 2000 paquetes a una tasa de 10 paquetes por segundo.
  • La aplicación consume 1 paquete cada 1 segundo.
Está claro que si esta situación se prolonga lo suficiente en el tiempo, en algún momento el buffer se llenará y el sistema deberá descartar paquetes. Si bien es una condición un poco exagerada a fines prácticos resulta útil.

Antes de lanzar nuestro cliente lento tomamos los valores de las estadísticas del sistema:

root@ubuntu:/home/juan# netstat -su
Udp:
    20 packets received
    0 packets to unknown port received.
    0 packet receive errors
    22 packets sent
...
root@ubuntu:/home/juan#


lanzamos el cliente

root@ubuntu:/home/juan# ./cliente
--Socket creado!
-Configuracion del socket tamanio del buffer de recepcion: 163840


y apuntamos el cañón hping3 al host:

root@moon:/home/juan/pruebas# hping3 192.168.0.10 -2 -c 2000 --fast -E 1024 -d 1024 -p 5555 -V
using wlan0, addr: 192.168.0.2, MTU: 1500
HPING 192.168.0.10 (wlan0 192.168.0.10): udp mode set, 28 headers + 1024 data bytes
[main] memlockall(): Success
Warning: can't disable memory paging!

--- 192.168.0.10 hping statistic ---
2000 packets transmitted, 0 packets received, 100% packet loss
round-trip min/avg/max = 0.0/0.0/0.0 ms
root@moon:/home/juan/pruebas#


esta vez hay nuevos parámetros en hping3:
  • --fast: le indica que debe mandar 10 paquetes por segundo.
  • --E 1024: indica que debe meter como payload de los paquetes el contenido del archivo llamado 1024 (un archivo con 1024 bytes en 0).
  • -d 1024: este parámetro es el que realmente indica cuántos bytes del archivo indicado por -E se van a mandar como payload. En esta caso se arman datagramas de 1024 bytes.
  • -c 2000: indica que se enviarán 2000 paquetes.
en definitiva se enviaron 2000 paquetes con 1024 bytes como payload cada uno de ellos (en total se enviaron unos 2048000 bytes, claramente superamos el tamaño del buffer).

Vemos la salida de la aplicación (dado que la función de lectura es bloqueante, la aplicación no termina por sus propios medios después de leer el último paquete, luego de unos segundos sin nuevos paquetes la aborté con Ctrl+C)

root@ubuntu:/home/juan# ./cliente
--Socket creado!
-Configuracion del socket tamanio del buffer de recepcion: 163840
Paquete numero 0
Paquete numero 1
Paquete numero 2
Paquete numero 3
Paquete numero 4
Paquete numero 5
Paquete numero 6
Paquete numero 7
...

Paquete numero 279
Paquete numero 280
Paquete numero 281
Paquete numero 282

^C
root@ubuntu:/home/juan#


Entonces se enviaron 2000 paquetes pero nuestra aplicación lenta solo pudo leer 283 de ellos!!! A donde fueron los restantes mmmm 1717 paquetes?

root@ubuntu:/home/juan# netstat -su
Udp:
    303 packets received
    0 packets to unknown port received.
    1717 packet receive errors
    22 packets sent
...
root@ubuntu:/home/juan#


Claramente fueron al cielo de los paquetes. Fueron completamente descartados por el kernel debido a que no había espacio donde almacenarlos.

Dado que leemos 1 paquete por segundo y agregamos 10 por segundo, podríamos decir que llegan un neto de 9 paquetes por segundo al buffer. A 9 paquetes por segundo el buffer se llena en unos 17 segundos aproximadamente. Es decir que, si la matemática no me falla, a partir de los 17 segundos estaríamos perdiendo al menos 9 paquetes por segundo.

Hay dos maneras de enfrentar esta situación, dado que UDP no tiene control de flujo e implementarlo en la capa de aplicación sería un delirio, podemos:
  • Incrementar la eficiencia del consumidor, leyendo mas paquetes por segundo.
  • Aumentando el tamaño del buffer de recepción para poder soportar el asedio de paquetes sin perderlos.
Entonces supongamos que mejoramos el código y multiplicamos por 7 la eficiencia de la aplicación y ahora es capaz de leer 7 paquetes por segundo. Matemáticamente estamos en la misma (en el horno), estarían ingresando un neto de 3 paquetes por segundo, ahora el buffer se llenaría en 53 segundos y volvemos a tener pérdida de paquetes. El paso siguiente es aumentar el tamaño del buffer. Sabemos que vamos a recibir una ráfaga de 2000 paquetes de 1024 bytes, osea 2Mbytes es decir que podríamos configurar un buffer de 2MBytes y quedarnos re tranquilos, pero sería algo extremo.

Con el nuevo código llenamos el buffer en 53 segundos, osea que aún nos quedan 147 segundos con pérdida de paquetes. Pero ahora vamos a ampliar el buffer multiplicándolo por 4 (NOTA: vale aclarar que este nuevo cambio se aplicará a todos los nuevos sockets creados).

root@ubuntu:/home/juan# echo "163840*4"|bc
655360
root@ubuntu:/home/juan# sysctl -w net.core.rmem_default=655360
net.core.rmem_default = 655360

root@ubuntu:/home/juan# sysctl -w net.core.rmem_max=655360
net.core.rmem_max = 655360

root@ubuntu:/home/juan# sysctl -a | grep net.core.rmem_default
net.core.rmem_default = 655360
root@ubuntu:/home/juan# 


En teoría, con este nuevo buffer de solo 4 veces mayor tamaño no deberíamos ver pérdida de paquetes.

root@ubuntu:/home/juan# netstat -su 
Udp:
    2072 packets received
    0 packets to unknown port received.
    0 packet receive errors
    21 packets sent
root@ubuntu:/home/juan#


Ejecución cliente

root@ubuntu:/home/juan# ./cliente
--Socket creado!
-Configuracion del socket tamanio del buffer de recepcion: 655360
Paquete numero 7
Paquete numero 14
Paquete numero 21
Paquete numero 28
Paquete numero 35
Paquete numero 42
Paquete numero 49
Paquete numero 56

...
Paquete numero 1960
Paquete numero 1967
Paquete numero 1974
Paquete numero 1981
Paquete numero 1988
Paquete numero 1995
2000 paquetes leidos!!!

root@ubuntu:/home/juan#

Y corroboramos con las estadísticas:

root@ubuntu:/home/juan# netstat -su
Udp:
    4072 packets received
    0 packets to unknown port received.
    0 packet receive errors
    21 packets sent

root@ubuntu:/home/juan#

El aumento del tamaño del buffer y la mejora en la aplicación permitieron que esta vez no haya pérdida de paquetes.

miércoles, 26 de marzo de 2014

I hunt sys admins... yo no, la NSA!

Acá les traigo un nota de zdnet que habla de como la NSA utiliza los administradores de sistemas como puerta de acceso a las redes de su interés. En esencia todo se trata de como lo llaman ellos SIGINT, recabar información a partir de la interceptación  de comunicaciones, información que luego procesan y asocian a otras para en definitiva utilizarla como entrada a sistemas como DISCOROUTE y QUANTUM.

Estos sistemas desarrollados por ellos mismos se encargan en mayor o menor medida de, a partir de la información (redes, IPs, nombres, cuentas de correo, configuraciones de routers, etc), proporcionar acceso a las terminales de los Sys admins que administran las redes objetivo. Una vez conseguido el acceso los chicos de CNE (Compute Network Exploitation) toman la posta y... el resto es cuento conocido. Parece ficción, ¿cierto?

Para quienes quieran profundizar y ver que NO se trata de ficción, The Intercept publicó el texto de donde se escribió la nota. Una lectura recomendada sin dudas!

Me gustaría rescatar unos puntos particulares que vi en el texto:

  • La NSA no solo recolecta la información que sabe que le servirá para determinada actividad en el corto plazo, sino que recolecta mucha mas información aún, información que luego podría ser eventualmente útil. Este proceso por el cual recolectan volúmenes increíbles de información lo llaman SIGINT.
  • Los Sysadmin no somos el objetivo de la NSA, al menos no en la mayoría de los casos, sino simplemente son quienes tienen acceso a las redes de comunicación en las cuales están interesados. NSA -Nada personal sysadmins...
  • Cuentan con herramientas muy interesantes como QUANTUM a partir de la cual con una determinada entrada de información pueden ganar acceso sencillamente (en la mayoria de los casos) los puestos de trabajo de los Sysadmins. 
  • Otra herramienta que parece aún tener éxito es DISCOROUTE, con la cual obtienen información de los routers de una red a través del uso del conocido " y aun no extinto y no se porque" Telnet. Si... al parecer aún quedan Telnets dando vuelta por la basta Internet.
  • Mencionan también cómo identifican los Sysadmins a través de conexiones SSH, aclaran que si bien no pueden interpretar (será que no?) el contenido de una comunicación de este tipo, si pueden reconocer sesiones SSH exitosas y NO exitosas. Esto parece complicado, pero de hecho no lo es, una sesión SSH fallida tiene un intercambio de paquetes muy limitado y corto, por lo tanto consideran una sesión exitosa (de sysadmin) a toda aquella sesión SSH que tenga una longitud mayor a la de una sesión fallida.

De estos puntos me parece que una buena parte se pueden tratar fácilmente.

  • No conozco un buen motivo para que los routers tengan IPs públicas. Si fuese posible que se administraran por IPs privadas no sería tan sencillo accederlos desde el exterior.
  • Las estaciones de trabajo de los administradores. Lo mismo que los routers, si tuviesen IPs privadas no podrían ser accedidas desde Internet directamente.
  • Telnet, Why in the world would you use it??? Calculo que el principal motivo por el que aún se usan es porque los equipos mas viejos no soportan protocolos seguros como SSH. De vuelta, se podría subsanar utilizando por ejemplo VPNs.
Alguna otra reflexión???

sábado, 3 de abril de 2010

Pequeeeeña intro MDNS (Multicast DNS):

Hoy (01/04/2010), mientras preparaba la próxima entrada del blog "casualmente" tenía wireshark corriendo y me encontré con un paquete que me llamó poderosamente la atención. El protocolo es MDNS, lo había visto antes pero siempre creí que se trataba de alguna de las aplicaciones que estaba corriendo.

Intro:

Googleando un poco encontré de que se trata: "Multicast DNS is a way of using familiar DNS programming interfaces, packet formats and operating semantics, in a small network where no conventional DNS server has been installed." (http://www.multicastdns.org/). Es decir, se trata de una implementación del protocolo de resolución de nombres (DNS) para redes de área local, donde no existe un servidor DNS real. Producto de los grupos Zero Configuration Networking y DNS Extensions, creado con el objetivo de facilitar la configuración de redes locales que no cuentan con un servidor de nombres propio.

Según el draft (http://files.multicastdns.org/draft-cheshire-dnsext-multicastdns.txt) "Clients performing DNS-like queries for DNS-like resource records by sending DNS-like UDP query and response packets over IP Multicast to UDP port 5353." se trata de clientes realizando consultas tipo DNS por registros tipo DNS enviando paquetes (tipo DNS) al puerto 5353 UDP  sobre direcciones IP Multicast (JA!, para los que creen que las clase D no se usan jajaaj).

Las direcciones Multicast o clase D van de 224.0.0.0 a 239.255.255.255, y se caracterizan por: una dirección Multicast está asociada con un grupo de receptores interesados, cuando uno de los interesados envía un paquete a una dirección Multicast todos los demás lo reciben, los routers que se encuentren entre estos interesados deben ser capaces de reenviar estos paquetes a cada uno de ellos.

Al tratarse de una dirección Multicast, todos los asociados reciben todos los paquetes, y de esta manera las caches de nombres se mantienen actualizadas. Cada respuesta a una consulta Multicast DNS trae un campo donde se especifica el tiempo de validez que tiene la respuesta, luego de que expire ese tiempo es necesario volver a efectuar la consulta.

Este servicio de DNS cuenta también con un TLD (Top-Level Domain) propio y es ".local.". Todo FQDN (Fully Qualified Domain Name, nombre completo xD) que finalice con ".local." se lo considera de enlace local, es decir... es un análogo a las direcciones IP privadas, que sólo valen en el ámbito local de la red y de nada sirven fuera de ella. Toda consulta DNS que finalice con ".local." debe ser enviada a la dirección 224.0.0.251 (si se trata de IPv4, o FF02::FB si se trata de IPv6).

Existen 3 tipo de consultas: las consultas convencionales 1-consulta->1-respuesta, 1-consulta->N-respuestas y el ultimo tipo que consiste en consultas continuas.

En fin... eso es mas o menos a modo de introducción :D. Si realmente querés saber mas, está el link del borrador mas arriba, de ahora en mas voy a intentar controlar/activar/desactivar este servicio de mi laptop. Resulta ser que en la mayoría de las distribuciones de GNU/Linux viene instalado por defecto un servicio que implementa este protocolo, y se llama Avahi (http://avahi.org/). Avahi es capaz de publicar y descubrir dispositivos, y servicios disponibles en la red local, sin necesidad de realizar configuración alguna.

Veamos si el servicio está corriendo :D  (hagamos de cuenta que nunca vi el paquete con wireshark xD):
"Lo sospeché desde un principio..." asi es, tengo corriendo como demonio a avahi-daemon



Buscando un poco mas se puede ver que escucha en el puerto UDP 5353 de todas las interfaces disponibles, a la espera de consultas/respuestas que pueden provenir de cualquier dirección de red.



Ahora probemos la facilidad de uso de este protocolo. La lan de prueba está compuesta por 2 hosts, la laptop con direccion 192.168.206.1 (nombre moon) y una vm con dirección 192.168.206.134 (nombre ubuntu). Ambos hosts tienen instalado Ubuntu 9.10, que trae por defecto instalado avahi-daemon versión 0.6.25. Ninguno de los host recibió algún tipo de configuración de este servicio, ambos corren como fueron instalados. En el host moon estará nuestro amigo wireshark capturando los paquetes para ver lo que sucede :D.

Buscando a "ubuntu" desde "moon":

Sabemos que está conectadp a la red un host de nombre "ubuntu", pero no sabemos la dirección IP que le fue asignada, y no contamos con un servidor DNS local :D, maldita sea!, pero para eso está corriendo avahi-daemon. Para conocer la dirección de ese host nos basta con hacer un "ping ubuntu.local.", como muestra la imagen



ahora veamos lo que capturó nuestro amigo :D.
Wireshark nos muestra 4 paquetes MDNS, los dos primeros son la resolución directa del nombre de host "ubuntu.local.".
-192.168.206.1 (moon) envía una consulta MDNS a 224.0.0.251 (Multicast IP Address) preguntando por la dirección de "ubuntu.local.".
-192.168.206.134 (ubuntu) envía una respuesta MDNS a 224.0.0.251 (Multicast IP Address) diciendo que 192.168.206.134 corresponde al "host ubuntu.local.".
Los últimos dos paquetes son la resolución inversa, es decir, intenta averiguar el nombre que le corresponde a la dirección 192.168.206.134, esto debe ser una suerte de mecanismo de seguridad, para ver que sean consistentes:
-192.168.206.1 (moon) envía una consulta inversa a la dirección 224.0.0.251, consultando por el nombre que le corresponde a "134.206.168.192.in-addr.arpa.".
-192.168.206.134 (ubuntu) rsponde a la dirección 224.0.0.251, diciendo que esa dirección le corresponde al nombre "ubuntu.local.".



Exáctamente lo mismo podemos observar si ahora realizamos el ping desde ubuntu hacia moon.local.



Publicando y Descubriendo servicios:

Junto con MDNS se puede utilizar algo llamado DNS-SD (DNS-Service Discovery), que consiste en dar a conocer y encontrar servicios locales utilizando MDNS. De nuevo se trata de algo de simple puesta en marcha. Como ejemplo publicaremos dos servicios en el host de nombre moon (FQDN moon.local.), un servidor web en el puerto 80 y un ftp en el puerto 21. El proceso de publicación de los servicios consiste en crear un archivo con sintaxis XML en el directorio /etc/avahi/services/, con la condición que el nombre del archivo debe finalizar en ".service" (sin las comillas :D), como el siguiente:



Para entender bien la sintaxis basta con dar una breve leída a "man avahi.service" :D. Luego de configurar adecuadamente los servicios que querramos reiniciamos el demonio avahi con "avahi-daemon -r" y listo, toda la magia de la publicación de servicios es esa jajaja. Ahora hay que comprobar que los servicios están siendo publicados, para eso instalé dos porgramas mdns-scan y mzclient, NO instalen mdns-scan es DEMASIADO BASICO (ni siquiera recibe parámetros y solo busca types "_workstation"). Primero veamos los hosts que se encuentran utilizando MDNS ejecutamos mzclient como en la próxima imagen (-r se encarga de resolver los nombres de los hosts):



moon tiene 3 direcciones MAC diferentes porque se trata de las 3 interfaces de red, dos virtuales y la eth0 real. Entonces los hosts siguen bien configurados, ahora busquemos los servicios publicados :D. Para eso usamos la opcion -t, que indica la etiqueta type del archivo de servicios, buscamos primero si hay servicios tipo "_http._tcp", incluimos -r para que resuelva nombres:



Ahora busquemos el ftp :D:



Ambos servicios fueron asociados al nombre "moon.local" con dirección 192.168.206.1, como lo configuramos y como debe ser. Eso es gran parte de la publicación de servicios, para mas información http://avahi.org :P.

Deteniendo el demonioooooooo:

Si nos iteresa detener el demonio solo por un momento o reiniciarlo, lo podemos hacer directamente mediante el script ubicado en /etc/init.d. Ejecutamos sudo /etc/init.d/avahi-daemon stop, o mas elegantemente sudo service avahi-daemon stop, para detenerlo, cambiamos stop por restart, para reiniciarlo y start para iniciarlo cuando está detenido.



Deshabilitando temporalmente al demonio:
Si queremos deshabilitarlo temporalmente pero no borrarlo del sistema podemos recurrir a un pequeño truquillo :D. La idea básicamente consiste en que sencillamente el demonio nunca se inicie xD, y esto lo podemos lograr alterando el script de inicio del demonio, ubicado en "/etc/init/avahi-daemon.conf". Como se ve en la imagen hay que comentar la linea de "start".



En definitiva, se trata de un servicio bastante útil, pero que sinceramente no necesito tener activo en este momento. Posiblemente en una LAN donde todo el tiempo entran y salen dispositivos diferentes sea de suma utilidad, pero no lo veo tan así en una LAN chica con poco cambio de los dispositivos.

sábado, 25 de abril de 2009

Redes IP y Subnetting

Para poder escribir sobre subnettig tengo 2 opciones: partir del supuesto que todos sabemos qué es una red IP o partir explicando qué es una red IP. Como ya terminé los parciales en la facultad (es decir, me sobra un poco de tiempo), voy a empezar por el camino mas largo xD.

Haciendo un mini repaso, sabemos que las direcciones IP están formadas por 32 bits, divididos en 4 octetos que se suelen escribir en decimal para hacer nuestra vida un poco más hermosa xD. Entonces la dirección 11000000101010000000000000000001 la expresamos como 192.168.0.1. En la figura se muestra parte del proceso.


En la siguiente imagen se ven 7 Hosts interconectados, divididos en 3 redes conectadas mediante un router. Todos los Host de una red comparten cierta parte de su dirección IP, precisamente esta parte que comparten se llama dirección de red. En el ejemplo, se ve que los Hosts de la RED 0 comparten los primeros 24 bits de sus direcciones IP, es decir, tienen como dirección de red 190.68.0.0/24. El /24 nos indica que la dirección de red está formada por los primeros 24 bist de la dirección IP, a este número se lo llama máscara de red, y suele representarse en el mismo formato que las direcciones IP, en este caso sería 255.255.255.0.


Con estos nuevos datos ya podemos saber la dirección de red de un interfase si conocemos su máscara de red, el proceso es simple. Hay que realizar la operación AND lógico entre los 32 bits de la dirección IP y los 32 bits de la máscara de red.


Resumiendo un poco todo lo dicho, ahora sabemos que una dirección IP está formada por dos partes, la dirección de red (indicada por la máscara de red) y la del Host.

Antes de olvidarme voy a hacer una aclaración; las direcciones IP se asignan a una interfaz (una interfaz es la frontera entre el Host y el enlace físico, simplificando… la placa de red) de un Host, no al Host en si mismo. ¿Qué quiero decir?, que un mismo Host puede tener mas de una interfaz (como ser el caso de los routers), aunque no sea muy común, y por eso se considera siempre el caso donde los Hosts sólo tienen una interfaz que los une a la red.

En un principio el esquema de direccionamiento IP se había organizado en 5 clases (A, B, C, D y E). Las redes de clase A cuentan con 7 bits para identificar redes y 24 bits para indicar Hosts. Las de clase B 14 bits para redes y 16 para Hosts. Las redes clase C emplean 21 bits para redes y 8 bits para Hosts. Las redes de clase D tienen un propósito especial, y las de clase E son para investigación. En el siguiente cuadro queda bien aclarado este tema de la división entre red y Host.

Este direccionamiento por clases fue pronto dejado de lado, debido a que no era muy elástico y malgastaba grandes cantidades de direcciones. Por ejemplo, una organización que tuviera 500 hosts que direccionar no podría usar una clase C, porque esta clase direcciona solamente 2^8 – 2 hosts (hay 2 direcciones que son de uso especial, las trato mas adelante), osea 254, entonces debería usar una clase B, en este caso contaría con 2^16 - 2 direcciones para sus hosts… pero esto es 65534 direcciones, de las cuales sólo usará 500… es decir malgastaría 65034 direcciones. Por este motivo se malgastaban muchas direcciones y pronto fue necesario re pensar el sistema de direccionamiento IP.

Así fue que surgió un nuevo estándar propuesto por la IETF llamado CIDR (Classless Interdomain Routing), enrutado interdominio sin clases. Con este nuevo estándar ahora las clases fueron dejadas un poco de lado, permitiendo que la longitud de la parte de red de una dirección IP pueda tener cualquier tamaño. Con CIDR nuestra organización del ejemplo ya puede obtener un bloque de direcciones más acotado (y por ende mas barato xD) con 9 bits para hosts tendríamos 2^9 – 2 = 510 direcciones disponibles, lo que es mucho mas preciso. Entonces las direcciones disponibles para la organización serán de la forma a.b.c.d/23 (donde 23 indica la máscara de red).

Otra nota importante! Existe un conjunto de direcciones IPs, llamadas direcciones privadas, que no pertenecen al grupo de direcciones asignadas en Internet o públicas. Estas se separaron para crear las redes locales y de esta forma disminuir el uso de direcciones públicas. Para que una red privada tenga acceso a Internet debe contar con una puerta de enlace que realice NAT o un servidor Proxy que contará con una dirección pública que le dará acceso a Internet a esa red privada. Aca dejo un link donde se listan las direcciones IP privadas y un poco mas de información: http://es.wikipedia.org/wiki/Direcci%C3%B3n_IP

Subnetting:

Es en este punto en el que nace el concepto de Subnetting. Subnetting es el proceso por el cual se toman bits correspondientes a hosts en la dirección IP para armar subredes. De esta forma podemos obtener a partir de una dirección de red varias subredes de menor tamaño. A partir de este momento nuestras direcciones IP tendrán 3 partes, un número de red, un número de subred y un número de Host dentro de la subred.

Veamos un ejemplo para aclarar el tema, supongamos que tenemos una dirección de red como la siguiente 192.168.2.0. Para los perdidos (o los que no leyeron el link que puse antes), esta es una dirección IP privada (NO PUEDE EXISTIR EN INTERNET), y por su formato podemos decir que es de clase C, entonces su máscara de red debe ser /24 (255.255.255.0). Sin aplicar Subnetting estamos frente a una red clase C que nos permite direccionar 2^8 – 2 hosts (interfaces en realidad, recuerden).

Voy a aprovechar para explicar el porqué del “- 2”, dentro de una red (también en las subredes) existen 2 direcciones que no se pueden asignar a una interfaz (o Host) porque son de propósito específico. Estas direcciones son la primera y la última, y se llaman dirección de red y dirección de broadcast respectivamente. La primera indica la red o subred y la última se utiliza para enviar paquetes a todos los miembros de la red o subred. En el caso de nuestro ejemplo la dirección de red es 192.168.2.0 (si!! El resultado de aplicar la máscara con el AND lógico xD) y la dirección de broadcast consiste en poner en 1 todos los bits que se usan para determinar el Host de la red o subred, en nuestro caso es el 4 octeto, entonces nos queda 192.168.2.255 como dirección de broadcast. E ahí la explicación de porque quitar dos direcciones del total, porque esas NO se pueden asignar a interfaz alguna.

Continuando con el ejemplo, ahora supongamos que tenemos una sola red con 100 computadoras repartidas en la organización. Por cierto motivo (privacidad, seguridad, desempeño, tiempo de sobra, etc.) se toma la decisión de dividir esa gran red en 2 redes más pequeñas con 50 computadoras cada una. Las llamaremos Subred Ventas, Subred RRHH. Ahora empezamos con los cálculos: dijimos que queremos armar 2 redes, entonces podríamos tomar 2 bits de los hosts para así poder armar 2^2 - 2 subredes (descartamos la primera y la última subred), es decir 2! Que precisión xD, sólo vamos a desperdiciar una (esto no estaba planeado jaja).

La siguiente imagen muestra el proceso:


De esa forma conformamos las dos redes, donde cada red tiene un espacio de direcciones para 62 interfaces.

El Subnetting se puede hacer sobre cualquier clase de redes, es decir sobre una red Clase A, B, etc. La esencia es que siempre que hagamos Subnetting vamos a obtener varias redes de menor cantidad de hosts (se disminuyen en potencia de 2 por cada bit que tomamos del campo de Host de la dirección IP). La subred entonces queda determinada por esta nueva Máscara de subred.

Como verán en una red de clase B que tenemos 14 bits que definen la red y 16 bits que definen el Host se pueden obtener buenas combinaciones para muchas subredes con muchos hosts, por ejemplo, tomando 6 bits para subredes obtendríamos 62 subredes con 1022 direcciones asignables (jeej me crees?? Probalo).

Cualquier duda, sugerencia, o consulta con respecto al tema, está MAS que bien recibida. Espero que haya expresa bien las ideas ajaja, un saludo. JJ.