Understanding Linux network internals
Quan es balanceja transit els administradors sovint desitgen evitar haver de reordenar frames Ethernet. Per exemple, el protocol TCP sofreix una certa sobrecàrrega si ha de lidiar amb paquets desordenats. Aquest objectiu se sol assolir enviant tots els frames associats a una sessió particular a través del mateix link físic (el mateix port i cable). La implementació més comuna és utilitzar hashes de nivell 3 (L3 hashes), per exemple basats en la adreça IP, de forma que s'assegura que el mateix fluxe de dades és sempre enviat sencer pel mateix enllaç físic. El principal problema és però que depenent del transit, això pot resultar que no hi haguí distribució de la càrrega real entre els enllaços físics ja que de fet l'ampla de banda no es suma, sinó que per a un mateix client i sessió el màxim de velocitat que es pot obtenir està limitat a la velocitat màxima de l'únic l'enllaç físic que acaba utilitzant aquell client i aquella sessió.
Aquesta és la raó principal per la que normalment una distribució de la carrega 50/50 no s'assoleix gaire bé mai si no més aviat un 70/30 és més habitual.
Alguns switchos avançats poden utilitzar un L4 hash (p.ex. els números de port TCP/UDP) que permet millorar l'experiència de balanceigi apropar-la al 50/50.
La clau és remarcar els paquets que són d'una mateixa connexió!!!:
Trànsit de sortida a diferents gateways!!!:
-A PREROUTING -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark
Trànsit d'entrada a un servidor, sempre s'ha de tornar per la mateixa connexió d'entrada:
-A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark
Compte amb rp_filter:
echo 0 > /proc/sys/net/ipv4/conf/YOURINTERFACE/rp_filter
Consulteu rp_filter.
Multirouter rules are a tool that enables PCs in a network to use several Internet connections transparently. This is useful if, for example, an office has several ADSL connections and the entire bandwidth available is to be used without having to worry about distributing the work of the hosts manually between both routers, so that the load is shared automatically between them.
Basic load balancing evenly distributes the packets transferred from eBox to the Internet. The simplest form of configuration involves establishing different weights for each router so that, if the connections available have different capacities, they can be used optimally.
Multirouter rules allow for certain traffic types to be sent permanently by the same router, where required. Common examples include sending emails through a certain router or ensuring that a certain subnet is always routed from the Internet through the same router.
eBox uses the iproute2 and iptables tools for the configuration required for the multirouter function. iproute2 informs the kernel of the availability of several routers. For multirouter rules, iptables is used to mark the packets of interest. These marks can be used from iproute2 to determine the router through which a packet must be sent.
There are several possible problems that must be considered. Firstly, the connection concept does not exist in iproute2. Therefore, with no other type of configuration, the packets belonging to the same connection could end up being sent by different routers, making communications impossible. To solve this, iptables is used to identify the different connections and ensure that all the packets of a connection are sent via the same router.
The same applies to any incoming connections established. All response packets for a connection must be sent using the same router through which that connection was received.
To establish a multirouter configuration with load balancing in eBox, as many routers as required must be defined in Network ‣ Routers. Using the weight parameter when configuring a router, it is possible to determine the proportion of packets that each one will send. Where two routers are available and weights of 5 and 10, respectively, are established, 5 of every 15 packets will be sent through the first router, while the the remaining 10 will be sent via the second.
És necessària una ruta tonta a la taula per defecte?
ifconfig dummy0 1.2.3.4 ip route add default dev dummy0
Disable rp_filter
Why aren't the packets coming back? I was scratching my head for a little while with this one. Correctly addressed packets going out the interface, yet nothing coming back. Setting the default route of the main table to point out the interface instantly fixes this, but that sort of defeats the purpose really, doesn't it? If I've understood correctly, this problem happens because the test done on incoming packets as to whether they are bogus is applied from the main table, and no others. Your main table will point to at most one place, so the others won't, by default, work. Here's how you fix it:
echo 0 > /proc/sys/net/ipv4/conf/YOURINTERFACE/rp_filter
$ sudo apt-get install balance
Exemple d'ús
$ balance smtp host1.test.net host2.test.net Connection to the local SMTP port will be forwarded alterating to the SMTP port on host1 and host2. Balance runs automatically in back‐ ground.
Vegeu:
$ man balance
Provat a guifi.net per crear un proxy virtual que balanceja la connexió a diferents proxies:
Kilian ha configurado un balanceo sobre una ip virtual con una > aplicacion llamanda balance, que se desprende de una suite de balanceo > mas compleja llamada balanceng. > > Si tu apuntas a esa ip virtual y a ese puerto , por detras hay un > algoritmo de reparticion de conexiones (round robin) entre varios proxys > de Terrassa y si alguno cae, de manera transparente te conectas al > siguiente. > > De esta manera no has de recordar todas las ips, y con que te sepas la > virtual ya te vale (aunque esta claro que puedes seguir conectandote > directamente a un proxy determinado si asi lo quieres) > > Slds!
Recursos:
Planteemos la situación:
Mi empresa tiene 2 adsl, repartidos como dios le dio a entender, es decir, cada uno tiene configurado de puerta de enlace uno de los dos pero de manera aleatoria. Como os podeis imaginar esto no es solución, ya que cuando se caia un adsl todo el mundo que tubiera ese adsl como puerta de enlace dejaba de tener internet. Tenemos un adsl de 4 megas y otro de 20 megas (bueno teoricos).
Bien, la solución fue el balanceo de carga con los dos adsl, y que ademas asignara diferentes pesos a cada una de las conexiones, para dar mas carga a un adsl que otro. No me voy a parar mucho en explicar los conceptos de redes, asi que puede ser algo "duro" de leer...
Yo pense que esto iba a ser complicado, pero no fue asi. Cogi una maquina antigua, un Pentium III a 733 con 1 Gb y 3 tarjetas ethernet. Instalé una ubuntu como servidor (al final usaremos Debian, ya que recomiendan un Kernel 2.4 para la estabilidad o algo asi... pero con Ubuntu funciona igual de bien). Hay que instalar el paquete "iproute2", con esto tendremos un monton de comandos nuevos que permiten trastear a nivel 3, vamos a nivel IP, con tu linux. A destacar el comando "tc" que te permite hacer un QoS de manera muy sencillita.
Bueno a lo que ibamos:
1.) Tenemos 3 tarjetas ethernet: eth0, eth1, eth2. donde:
eth0 (192.168.0.x) -> red privada (192.168.0.0/24) eth1 (10.10.10.2) -> adsl1 (10.10.10.1) eth2 (10.10.11.2) -> adsl2 (10.10.11.1)
Mi maquina que funcionara para balancear carga tiene esta topologia de red, como veis usa 3 subredes. Hay que destacar que la conexion entre los routers de adsl y la maquina, pueden ser cables directamente conectados, o usar un switch... eso depende de los cacharros que tengas por alli algooooo
2.) Comprobamos que tenemos el comando "ip", si no es asi, mira si has instalado el paquete "iproute". Este comando nos permitirá hacer las rutas y el balanceo de carga.
3.) Inserta esto:
# echo 200 adsl1 >> /etc/iproute2/rt_tables # echo 201 adsl2 >> /etc/iproute2/rt_tables
Esto nos introduce dos nuevas tablas de enrutamiento llamadas "adsl1" y "adsl2", con esos numeros asociados (mira los que ya estan en uso haciendo un "more /etc/iproute2/rt_tables" y asi no machacar ninguna tabla). Asi podremos definir reglas diferente para cada uno de los adsl sin hacer todo un lio, y ademas poder saber en todo momento que estamos haciendo.
4.) Definimos el enrutamiento de adsl1:
# ip route add 10.10.10.0/24 dev eth1 src 10.10.10.2 table adsl1 # ip route add default via 10.10.10.1 table adsl1
Esto le dice que la red 10.10.10.0 es la red de mi interfaz eth1 con la salida por el adsl. Compruebalo mediante: ip route show table adsl1
5.) Definimos el enrutamiento del adsl2:
# ip route add 10.10.11.0/24 dev eth2 src 10.10.11.2 table adsl2 # ip route add default via 10.10.11.1 table adsl2
Idem a lo anterior.
6.) Poner las rutas principales:
# ip route add 10.10.10.0/24 dev eth1 src 10.10.10.2 # ip route add 10.10.11.0/24 dev eth2 src 10.10.11.2
Date cuenta que ahora no decimos en que tabla meterlo, por lo que se mete en la tabla principal. Lo anterior era para cuando el trafico ya esta en una de las dos subredes de salida y necesitamos saber como enrutarlo. En cambio las segundas son las primeras reglas para que funcione y las envie dondo debe.
7.) ¿Donde mando esto?
# ip rule add from 10.10.10.2 table adsl1 # ip rule add from 10.10.11.2 table adsl2
Esto sirve para aplicar las reglas almacenadas en cada una de las tablas cuando se cumple que vienen de esas ips.
8.) La madre del cordero: Balanceo de carga.
ip route add default scope global nexthop via 10.10.10.2 dev eth1 weight 1 nexthop via 10.10.11.2 dev eth2 weight 2
Como ves, le he dicho: "Usa eth1 1 de cada 3 veces, sino usa eth2 2 de cada 3 veces".
Solo existe un problema. Las rutas son cacheadas, es decir, si tu pides "www.google.es" y se enruta por adsl1, la siguiente vez, hasta que caduque el cacheo, ira por adsl1... Esto es malo... Ya que si adsl1 se cae, las rutas cacheadas fallaran, aunque adsl2 este disponible, si no estan cacheadas iran por adsl2.
Para solucionarlo, habria que parchear el kernel, segun estos parches: http://www.ssi.bg/~ja/#routes Aunque con hacer: ip route flush se podria arreglar de manera manual.
#!/bin/bash #Copyright Angsuman Chakraborty, Taragana. Permission is granted for personal, non-commercial use. #The script may not be re-distributed in any form without written permission from Angsuman Chakraborty ( [email protected] ). #The script may be modified for personal use. #THIS SOFTWARE IS PROVIDED ``AS IS AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR ACCEPTS NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. # Conventionally 0 indicates success in this script. # Time between checks in seconds SLEEPTIME=10 #IP Address or domain name to ping. The script relies on the domain being #pingable and always available TESTIP=www.yahoo.com #Ping timeout in seconds TIMEOUT=2 # External interfaces EXTIF1=eth1 EXTIF2=eth2 #IP address of external interfaces. This is not the gateway address. IP1=192.168.1.10 IP2=192.168.0.10 #Gateway IP addresses. This is the first (hop) gateway, could be your router IP #address if it has been configured as the gateway GW1=192.168.1.1 GW2=192.168.0.1 # Relative weights of routes. Keep this to a low integer value. I am using 4 # for TATA connection because it is 4 times faster W1=1 W2=4 # Broadband providers name; use your own names here. NAME1=BSNL NAME2=TATA #No of repeats of success or failure before changing status of connection SUCCESSREPEATCOUNT=4 FAILUREREPEATCOUNT=1 # Do not change anything below this line # Last link status indicates the macro status of the link we determined. This is down initially to force routing change upfront. Don't change these values . LLS1=1 LLS2=1 # Last ping status. Don't change these values. LPS1=1 LPS2=1 # Current ping status. Don't change these values. CPS1=1 CPS2=1 # Change link status indicates that the link needs to be changed. Don't change these values. CLS1=1 CLS2=1 # Count of repeated up status or down status. Don't change these values. COUNT1=0 COUNT2=0 while : ; do ping -W $TIMEOUT -I $IP1 -c 1 $TESTIP > /dev/null 2>&1 RETVAL=$? if [ $RETVAL -ne 0 ]; then echo $NAME1 Down CPS1=1 else CPS1=0 fi
if [ $LPS1 -ne $CPS1 ]; then
echo Ping status changed for $NAME1 from $LPS1 to $CPS1 COUNT1=1 else if [ $LPS1 -ne $LLS1 ]; then COUNT1=`expr $COUNT1 + 1` fi fi if | ($LLS1 -eq 0 && $COUNT1 -ge $FAILUREREPEATCOUNT) ; then echo Uptime status will be changed for $NAME1 from $LLS1 CLS1=0 COUNT1=0 if [ $LLS1 -eq 1 ]; then LLS1=0 else LLS1=1 fi else CLS1=1 fi LPS1=$CPS1 ping -W $TIMEOUT -I $IP2 -c 1 $TESTIP > /dev/null 2>&1 RETVAL=$? if [ $RETVAL -ne 0 ]; then echo $NAME2 Down CPS2=1 else CPS2=0 fi if [ $LPS2 -ne $CPS2 ]; then echo Ping status changed for $NAME2 from $LPS2 to $CPS2 COUNT2=1 else if [ $LPS2 -ne $LLS2 ]; then COUNT2=`expr $COUNT2 + 1` fi fi if | ($LLS2 -eq 0 && $COUNT2 -ge $FAILUREREPEATCOUNT) ; then echo Uptime status will be changed for $NAME2 from $LLS2 CLS2=0 COUNT2=0 if [ $LLS2 -eq 1 ]; then LLS2=0 else LLS2=1 fi else CLS2=1 fi LPS2=$CPS2 if | $CLS2 -eq 0 ; then if $LLS1 -eq 1 && $LLS2 -eq 0 ; then echo Switching to $NAME2 ip route replace default scope global via $GW2 dev $EXTIF2 elif $LLS1 -eq 0 && $LLS2 -eq 1 ; then echo Switching to $NAME1 ip route replace default scope global via $GW1 dev $EXTIF1 elif $LLS1 -eq 0 && $LLS2 -eq 0 ; then echo Restoring default load balancing ip route replace default scope global nexthop via $GW1 dev $EXTIF1 weight $W1 nexthop via $GW2 dev $EXTIF2 weight $W2 fi fi sleep $SLEEPTIME done
Consulteu l'article sobre PFSENSE
#!/bin/bash # # Autor: Victor Carceler # # Licencia: GPLv3 o posterior # # Función: # # Monitorizar el estado de los diferentes enlaces y dar de baja/alta # routers en la ruta por defecto de forma dinámica PROGNAME=`basename $0` # # TESTIP: IP objectiu que s'utilitza per saber si han caigut els gateways # 213.176.161.13 es la IP associada a: www.xtec.cat # TESTIP=213.176.161.13 PINGTIMEOUT=20 # # IPs i portes de sortida del balancejador # IP1=192.168.0.33 IP2=192.168.1.33 GTW1=192.168.0.1 GTW2=192.168.1.1 GATEWAY_PING_SRC=($IP1 $IP2) GATEWAY_LIST=($GTW1 $GTW2) GATEWAY_WEIGHT_LIST=(1 1) SLEEP=60 N_GATEWAYS=`echo ${GATEWAY_LIST[@]} | wc -w` N_GATEWAYS=`expr $N_GATEWAYS - 1` last_hopes="" while true do hopes="" for x in `seq 0 1 $N_GATEWAYS` do ping -W $PINGTIMEOUT -I ${GATEWAY_PING_SRC[$x]} -c 1 $TESTIP >/dev/null 2>/dev/null RETVAL=$? if [ $RETVAL -eq 0 ] then hopes="$hopes nexthop via ${GATEWAY_LIST[$x]} weight ${GATEWAY_WEIGHT_LIST[$x]}" fi done if [ "$last_hopes" != "$hopes" ] then logger "$PROGNAME: Cambiamos la ruta por defecto a: $hopes" logger "$PROGNAME: Valor anterior: $last_hopes" logger "$PROGNAME: ip route replace default scope global $hopes" ip route replace default scope global $hopes last_hopes=$hopes fi sleep $SLEEP done
Consulteu els següents articles:
Vegeu també Xarxes_Linux#DNAT_i_multigateway i RouterOS_Policy_routing#Policy_routing.2C_combinant_Squid.2C_routerOS.2FROuterboard_i_varies_ADSLs_o_sortides_a_Internet._Projecte_OpenFPnet
Amb la directiva tcp_outgoing_address, podem canviar la IP d'origen del paquet que surt de l'Squid a l'hora de buscar una pàgina d'Internet. Combinant-ho amb Policy Routing (Source Policy Routing), podem encaminar per diferents gateways diferents peticions a un servidor Squid.
IMPORTANT: Si teniu un encaminador sense squid podeu utilitzar directament Policy Routing. Si utilitzeu Squid heu de recordar que qui es connecta a Internet no són mai els clients, sinó que és el proxy Squid qui fa de intermediari (d'aquí que Proxy es tradueixi habitualment com a servidor intermediari). Per tant el policy routing si és per Ip d'origen no s'aplica (totes les peticions de l'squid tenen la mateixa Ip d'origen: la de la targeta de xarxa del servidor squid que està connectada a Internet!)
A /etc/squid3/squid.conf cal posar quelcom similar a (cal adaptar l'exemple al vostre cas):
acl prova src 127.0.0.1 tcp_outgoing_address 80.32.57.101 prova acl in_192_168_11_2 myip 192.168.11.2 tcp_outgoing_address 192.168.11.2 in_192_168_11_2
acl in_80_32_57_101 myip 80.32.57.101 tcp_outgoing_address 80.32.57.101 in_80_32_57_101
acl in_192_168_1_5 myip 192.168.1.5 tcp_outgoing_address 192.168.1.5 in_192_168_1_5
On
acl prova src 127.0.0.1 tcp_outgoing_address 80.32.57.101 prova
Tots els clients que compleixen la ACL prova (és a dir tinguin la IP d'origen 127.0.0.1, en aquest cas localhost!) utilitzaran com IP d'origen 80.32.57.101 (aquesta IP ha de ser un IP correcta d'alguna de les targetes de xarxa de l'squid)
On:
Fa que les peticions demanades des de localhost, es posi com a IP d'origen la IP 80.32.57.101. Si ara no canviem res mes una petició des de 127.0.0.1 no funcionarà ja que la taula de rutes per defecte és:
$ sudo route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 80.32.57.64 0.0.0.0 255.255.255.192 U 0 0 0 internet2 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 intranet 192.168.11.0 0.0.0.0 255.255.255.0 U 0 0 0 internet1 0.0.0.0 192.168.11.1 0.0.0.0 UG 0 0 0 internet1
És a dir s'enviarà un paquet amb la IP d'origen 80.32.57.101 per la targeta de xarxa internet1! El que tocaria és que fos internet2. Cal tenir en compte però que les decisions de rutes es prenen segons la IP de destinació i no pas la d'origen. Com la IP de destinació és una màquina d'Internet aleshores surt per la targeta on esta el default gateway (internet1).
Podeu comprovar que surt per una targeta o que surt per un altre amb:
$ sudo tcpdump -n -i internet1 $ sudo tcpdump -n -i internet2
Ara cal configurar Policy routing:
A Policy routing cal dos taules de rutes extres (internet1 i internet2) i tenir les normes:
$ sudo ip rule list 0: from all lookup local 32758: from 80.32.57.101 lookup internet2 32759: from 192.168.11.2 lookup internet1 ...
Les podeu afegir amb:
$ sudo ip rule add from 80.32.57.101 table internet2 $ sudo ip rule add from 192.168.11.2 table internet1
Cal que també configureu les rutes de les taules internet1 i internet2 per tal de tenir:
$ sudo ip route list table internet1 192.168.1.0/24 dev intranet scope link src 192.168.1.5 default via 192.168.11.1 dev internet1 $ sudo ip route list table internet2 192.168.1.0/24 dev intranet scope link src 192.168.1.5 default via 80.32.57.65 dev internet2
Consulteu també Squid.