Carp + Pfsync no FreeBSD 10.1

E ai galera aqui vamos fazer a configuração do Carp e o Pfsync no FreeBSD 10.1 a ideia principal aqui é disponibilizar um ip compartilhado para os servidores trabalharem com alta disponibilidade o equivalente em Linux seria o Heartbeat e vamos também habilitar o pfsync que vai fazer a replicação da tabela de estados do PF (Packet Filter) no servidor slave.

A grande diferença que eu notei entre a implementação no Linux e no FreeBSD ou OpenBSD foi a replicação da tabela de estados, pois caso um cliente esteja acessando algo que contenha um token por exemplo de autenticação ele não vai precisar abrir uma nova conexão quando um servidor cair e o outro assumir pois a tabela de estados vai ser a mesma em ambos os servidores.

Se estiver trabalhando com VMS para testar não se esqueça de habilitar o modo promiscuous na interface, caso contrário o sistema pode não funcionar corretamente.

Informações sobre o Carp: Introduction to CARP (English)

Informações sobre o Pfsync: Introduction to pfsync (English)

Prepara o seu kernel com o seguinte how-to Preparação do Kernel

O que vamos utilizar:

  • FreeBSD 10.1 Master:
    • IP LAN: 192.168.1.70 Interface: em0 Função: pfsync
    • IP DMZ: 192.168.56.70 Interface: em1 Função: carp IP carp: 192.168.56.100
    • IP WAN: 192.168.57.70 Interface: em2 Função: carp IP carp: 192.168.57.100
  • FreeBSD 10.1 Slave:
    • IP LAN: 192.168.1.80 Interface: em0 Função: pfsync
    • IP DMZ: 192.168.56.80 Interface: em1 Função: carp IP carp: 192.168.56.100
    • IP WAN: 192.168.57.80 Interface: em2 Função: carp IP carp: 192.168.57.100

Como o carp trabalha enviando pacotes em multicast vamos precisar informar uma senha para que haja a troca de informações entre os nodos.

Caso esteja preocupado com a segurança da transferencia dos dados o carp utilizar a criptografia SHA-1 HMAC para a autenticação da senha.

Vamos gerar uma senha para a interface DMZ

cat /dev/random | hexdump -n 30| cut -d \  -f 2-| head -n 1 | tr -d " "
31adfd738f958d9f24e13a761ada65ba

Vamos gerar uma senha para a interface WAN

cat /dev/random | hexdump -n 30| cut -d \  -f 2-| head -n 1 | tr -d " "
64e537080834c4774f60d920dbc8d1f1

Agora vamos a configuração do carp no servidor Master a configuração é bem simples.

vim /etc/rc.conf
[...]
### GW
gateway_enable="YES"            #Habilita o servidor trabalhar como GW
### PF
pf_enable="YES"                 # Habilita o PF
pf_rules="/etc/pf.conf"         # Definição de regras para o pf
pf_flags=""                     # Flags adicionais para o pfctl inicializar caso necessário
pflog_enable="YES"              # Habilita o pflog
pflog_logfile="/var/log/pflog"  # Local de armazenamento dos logs
pflog_flags=""                  # Flags adicionais para o pflogd inicializar caso necessário

### CARP
ifconfig_em1_alias0="vhid 10 advskew 0 pass 31adfd738f958d9f24e13a761ada65ba alias 192.168.56.100/32"
ifconfig_em2_alias0="vhid 16 advskew 0 pass 64e537080834c4774f60d920dbc8d1f1 alias 192.168.57.100/32"

### PFSYNC
pfsync_enable="YES"
pfsync_syncdev="em0"

Explicando configuração do carp:

  • vhid: Identificação do host para o carp tem que ser a mesma identificação e mesma interface em ambos os hosts
  • advskew: Quando menor o número maior a prioridade para se tornar master
  • pass: Especifica uma senha para a comunicação do carp a senha tem que ser a mesma em ambos os servidores.
  • Alias: Especifica que estamos atribuindo um ip adicional a interface

Agora vamos configuração o carp no servidor Slave a configuração é bem parecida o que vai mudar vai ser o advskew que é quem vai determinar quem vai ser o servidor master, como este servidor vai ser um servidor slave ele vai ter um valor maior do que o servidor Master.

vim /etc/rc.conf
[...]
### GW
gateway_enable="YES"            #Habilita o servidor trabalhar como GW
### PF
pf_enable="YES"                 # Habilita o PF
pf_rules="/etc/pf.conf"         # Definição de regras para o pf
pf_flags=""                     # Flags adicionais para o pfctl inicializar caso necessário
pflog_enable="YES"              # Habilita o pflog
pflog_logfile="/var/log/pflog"  # Local de armazenamento dos logs
pflog_flags=""                  # Flags adicionais para o pflogd inicializar caso necessário

### CARP
ifconfig_em1_alias0="vhid 10 advskew 128 pass 31adfd738f958d9f24e13a761ada65ba alias 192.168.56.100/32"
ifconfig_em2_alias0="vhid 16 advskew 128 pass 64e537080834c4774f60d920dbc8d1f1 alias 192.168.57.100/32"

### PFSYNC
pfsync_enable="YES"
pfsync_syncdev="em0"

Explicando configuração do carp:

  • vhid: Identificação do host para o carp tem que ser a mesma identificação e mesma interface em ambos os hosts
  • advskew: Quando menor o número maior a prioridade para se tornar master
  • pass: Especifica uma senha para a comunicação do carp a senha tem que ser a mesma em ambos os servidores.
  • Alias: Especifica que estamos atribuindo um ip adicional a interface

Configuração mínima para o PF

Configuração mímina para o pf para que ele deixe os protocolos carp e pfsync trabalharem.

Precisamos desta configuração em ambos os servidores.

vim /etc/pf.conf
### Interfaces 
ext_if="em2"
int_if="em1"
pf_sync="em0"

### Filtering 
pass quick on $pf_sync proto pfsync keep state (no-sync)
pass quick on $ext_if  proto carp   keep state (no-sync)
pass quick on $int_if  proto carp   keep state (no-sync)

Agora precisamos so reiniciar os servidores para a alta diponibilidade estar ativa.

Testando

Vamos checar a configuração de rede do servidor Master

ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:52:b3:f2
  inet 192.168.1.70 netmask 0xffffff00 broadcast 192.168.1.255 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:01:c3:56
  inet 192.168.56.70 netmask 0xffffff00 broadcast 192.168.56.255 
  inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: MASTER vhid 10 advbase 1 advskew 0
em2: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:2c:ec:3f
  inet 192.168.57.70 netmask 0xffffff00 broadcast 192.168.57.255 
  inet 192.168.57.100 netmask 0xffffffff broadcast 192.168.57.100 vhid 16 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: MASTER vhid 16 advbase 1 advskew 0
pflog0: flags=141<UP,RUNNING,PROMISC> metric 0 mtu 33160
pfsync0: flags=41<UP,RUNNING> metric 0 mtu 1500
  pfsync: syncdev: em0 syncpeer: 224.0.0.240 maxupd: 128 defer: off
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
  options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
  inet6 ::1 prefixlen 128 
  inet6 fe80::1%lo0 prefixlen 64 scopeid 0x6 
  inet 127.0.0.1 netmask 0xff000000 
  nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>

Note que nas interaces em1 e em2 temos a ultima linha carp: MASTER informando que o nosso servidor é o master do pool.

Note também que na interface pfsync0 agora temos syncdev em0 que é a nossa interface de replicação do pfsync.

Podemos checar o status das interfaces com o seguinte comando.

systat -ifstat 1

                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average   || 

      Interface           Traffic               Peak                Total
        pfsync0  in      0.000 KB/s          0.000 KB/s            0.082 KB
                 out     0.000 KB/s          0.000 KB/s            0.199 KB

            em2  in      0.000 KB/s          0.484 KB/s            1.663 KB
                 out     0.065 KB/s          0.067 KB/s            6.682 KB

            em1  in      0.000 KB/s          0.484 KB/s            1.663 KB
                 out     0.065 KB/s          0.067 KB/s            6.682 KB

            em0  in      0.062 KB/s          0.374 KB/s           11.193 KB
                 out     0.186 KB/s          0.733 KB/s           11.887 KB

Podemos checar o trabalho da nossa interface pfsync com o pftop se não estiver instalado podemos instalar ele da seguinte forma

pkg install pftop

Agora podemos chamar ele comando o comando pftop

pftop
pfTop: Up State 1-2/2, View: default, Order: none, Cache: 10000                                                                                                                                                                      14:26:26

PR        DIR SRC                                           DEST                                                   STATE                AGE       EXP     PKTS    BYTES
carp      Out 192.168.56.70:0                               224.0.0.18:0                                       SINGLE:NO_TRAFFIC   00:07:37  00:00:30      410    22960
carp      Out 192.168.57.70:0                               224.0.0.18:0                                       SINGLE:NO_TRAFFIC   00:07:37  00:00:30      410    22960

Agora vamos testar os nossos ips do carp utilizado na DMZ

ping 192.168.56.100 -c 3
PING 192.168.56.100 (192.168.56.100) 56(84) bytes of data.
64 bytes from 192.168.56.100: icmp_seq=1 ttl=64 time=0.415 ms
64 bytes from 192.168.56.100: icmp_seq=2 ttl=64 time=0.429 ms
64 bytes from 192.168.56.100: icmp_seq=3 ttl=64 time=0.331 ms

--- 192.168.56.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.331/0.391/0.429/0.048 ms

Agora vamos testar os nossos ips do carp utilizado na WAN

ping 192.168.57.100 -c 3
PING 192.168.57.100 (192.168.57.100) 56(84) bytes of data.
64 bytes from 192.168.57.100: icmp_seq=1 ttl=64 time=0.438 ms
64 bytes from 192.168.57.100: icmp_seq=2 ttl=64 time=0.451 ms
64 bytes from 192.168.57.100: icmp_seq=3 ttl=64 time=0.516 ms

--- 192.168.57.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.438/0.468/0.516/0.038 ms

Como podemos notar está tudo ok.

Agora vamos checar a configuração do servidor Slave.

Vamos checar as interfaces de rede.

ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:93:50:a5
  inet 192.168.1.80 netmask 0xffffff00 broadcast 192.168.1.255 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:01:90:a4
  inet 192.168.56.80 netmask 0xffffff00 broadcast 192.168.56.255 
  inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: BACKUP vhid 10 advbase 1 advskew 128
em2: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:b1:6f:dc
  inet 192.168.57.80 netmask 0xffffff00 broadcast 192.168.57.255 
  inet 192.168.57.100 netmask 0xffffffff broadcast 192.168.57.100 vhid 16 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: BACKUP vhid 16 advbase 1 advskew 128
pflog0: flags=141<UP,RUNNING,PROMISC> metric 0 mtu 33160
pfsync0: flags=41<UP,RUNNING> metric 0 mtu 1500
  pfsync: syncdev: em0 syncpeer: 224.0.0.240 maxupd: 128 defer: off
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
  options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
  inet6 ::1 prefixlen 128 
  inet6 fe80::1%lo0 prefixlen 64 scopeid 0x6 
  inet 127.0.0.1 netmask 0xff000000 
  nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>

Assim como o servidor Master a configuração do carp esta ok porem ao invés de MASTER na linha carp temos BACKUP este servidor vai assumir a função de master quando o outros servidor ficar down.

Agora vamos testar o failover.

Vamos deixar pingando no servidor master o ip 192.168.56.100 e vamos baixar a interface em1 para testar

Agora vamos checar no servidor slave se ele agora está com a função de master

ifconfig em1
em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:01:90:a4
  inet 192.168.56.80 netmask 0xffffff00 broadcast 192.168.56.255 
  inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: MASTER vhid 10 advbase 1 advskew 128

Note que agora o nosso servido slave assumiu a função de master para o ip.

Agora vamos analisar os pings.

ping 192.168.56.100 
PING 192.168.56.100 (192.168.56.100) 56(84) bytes of data.
64 bytes from 192.168.56.100: icmp_seq=1 ttl=64 time=0.363 ms
64 bytes from 192.168.56.100: icmp_seq=2 ttl=64 time=0.399 ms
64 bytes from 192.168.56.100: icmp_seq=3 ttl=64 time=0.342 ms
64 bytes from 192.168.56.100: icmp_seq=4 ttl=64 time=0.358 ms
64 bytes from 192.168.56.100: icmp_seq=5 ttl=64 time=0.387 ms
64 bytes from 192.168.56.100: icmp_seq=8 ttl=64 time=0.701 ms
64 bytes from 192.168.56.100: icmp_seq=9 ttl=64 time=0.333 ms
64 bytes from 192.168.56.100: icmp_seq=10 ttl=64 time=0.444 ms
64 bytes from 192.168.56.100: icmp_seq=11 ttl=64 time=0.321 ms
64 bytes from 192.168.56.100: icmp_seq=12 ttl=64 time=0.341 ms
64 bytes from 192.168.56.100: icmp_seq=13 ttl=64 time=0.348 ms
64 bytes from 192.168.56.100: icmp_seq=14 ttl=64 time=0.321 ms
64 bytes from 192.168.56.100: icmp_seq=15 ttl=64 time=0.381 ms
64 bytes from 192.168.56.100: icmp_seq=16 ttl=64 time=0.413 ms
^C
--- 192.168.56.100 ping statistics ---
16 packets transmitted, 14 received, 12% packet loss, time 14997ms
rtt min/avg/max/mdev = 0.321/0.389/0.701/0.094 ms

Como podemos notar tivemos um atraso nos pacotes de 314ms na troca de função entre os servidores.

Agora se a interface em1 do servidor master subir novamente o slave não vai devolver a função para que isso aconteça precisamos habilitar uma flag no kernel.

Está flag deve ser habilitada somente o no servidor master.

vim /etc/sysctl.conf
[...]
#Habilitando a devolução da função
net.inet.carp.preempt=1

Podemos efetuar a mesma configuração temporariamente da seguinte forma

sysctl -w net.inet.carp.preempt=1

Agora já podemos checar o nosso servidor master que a função deve ter retornado para ele.

ifconfig em1
em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500
  options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
  ether 08:00:27:01:c3:56
  inet 192.168.56.70 netmask 0xffffff00 broadcast 192.168.56.255 
  inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 
  nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
  media: Ethernet autoselect (1000baseT <full-duplex>)
  status: active
  carp: MASTER vhid 10 advbase 1 advskew 0