Tc: policiamento de ingresso e espelhamento ifb

20

Estou tentando configurar a modelagem de tráfego em um gateway Linux, como está escrito aqui . O script precisa ser personalizado porque tenho várias interfaces de rede local. Então, para moldar o lado da LAN, estou planejando criar um pseudo dispositivo ifb assim:

     modprobe ifb
     ip link set dev ifb0 up
    /sbin/tc qdisc add dev $WAN_INTERFACE ingress
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

O script do repositório principal mencionado acima tem as seguintes linhas:

 /sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip sport $INTERACTIVE_PORT 0xffff flowid :1
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 1 u32 match ip dport $INTERACTIVE_PORT 0xffff flowid :1
    /sbin/tc filter add dev $WAN_INTERFACE parent ffff: protocol ip prio 5 0 u32 match ip src 0.0.0.0/0 police rate $MAX_DOWNRATE_INGRESS burst 20k drop flowid :2

Esse código e o código de criação da interface ifb não se dão bem. O script personalizado é executado, mas o dispositivo ifb0 não mostra nenhuma estatística de tráfego. Se eu comentar o código de repo de entrada de lista de itens essenciais (citado acima), o dispositivo ifb0 mostra o número de pacotes que são transferidos. Além disso, essas linhas não podem ser executadas juntas:

/sbin/tc qdisc add dev $WAN_INTERFACE ingress
/sbin/tc qdisc add dev $WAN_INTERFACE handle ffff: ingress

Eu recebo o arquivo existe erro. Então, como posso moldar a entrada no WAN_INTERFACE e, ao mesmo tempo, também moldar o tráfego que vai para a LAN via dispositivo ifb0?

nixnotwin
fonte

Respostas:

41

O IFB é uma alternativa aos filtros tc para manipular o tráfego de entrada, redirecionando-o para uma interface virtual e o tratamento é como tráfego de saída. Você precisa de uma interface ifb por interface física para redirecionar o tráfego de entrada de eth0 para ifb0, eth1 para ifb1 e, portanto, em.

Ao inserir o módulo ifb, informe o número de interfaces virtuais necessárias. O padrão é 2:

modprobe ifb numifbs=1

Agora, ative todas as interfaces ifb:

ip link set dev ifb0 up # repeat for ifb1, ifb2, ...

E redirecione o tráfego de entrada das interfaces físicas para a interface ifb correspondente. Para eth0 -> ifb0:

tc qdisc add dev eth0 handle ffff: ingress
tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0

Repita novamente para eth1 -> ifb1, eth2 -> ifb2 e assim por diante, até que todas as interfaces que você deseja moldar sejam cobertas.

Agora, você pode aplicar todas as regras que desejar. As regras de saída para eth0 funcionam normalmente em eth0. Vamos limitar a largura de banda, por exemplo:

tc qdisc add dev eth0 root handle 1: htb default 10
tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 1mbit

Escusado será dizer que, repita para eth1, eth2, ...

Regras de entrada para eth0, agora como regras de saída no ifb0 (o que entra no ifb0 deve sair, e apenas o tráfego de entrada eth0 entra no ifb0). Novamente, um exemplo de limite de largura de banda:

tc qdisc add dev ifb0 root handle 1: htb default 10
tc class add dev ifb0 parent 1: classid 1:1 htb rate 1mbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 1mbit

A vantagem dessa abordagem é que as regras de saída são muito mais flexíveis que os filtros de entrada. Os filtros permitem apenas que você descarte pacotes, e não introduza tempos de espera, por exemplo. Ao manipular o tráfego de entrada como saída, você pode configurar as disciplinas da fila, com classes de tráfego e, se necessário, filtros. Você obtém acesso a toda a árvore tc, não apenas aos filtros simples.

Sérgio Carvalho
fonte
Bem feito. É sempre bom ver um profissional aparecendo com uma primeira resposta de estrela do rock.
Magellan
Esta pode ser uma pergunta ingênua, mas não consigo encontrar informações específicas. Com base nesta resposta (o que é ótimo), é possível obter ifb0estatísticas especificamente para um cgroup classid? Ou seja, posso obter com êxito as estatísticas de saída para um cgroup com um filtro classid. Mas o tráfego de entrada também pode ser contabilizado por grupo?
JDI
esteja ciente de que se você usar o iptable para marcar seu pacote e depois filtrá-lo, não poderá usar o ifb, pois todo o tráfego de ingresso será encaminhado ANTES de qualquer marcação. para que sua classe fique em 0 e todas encaminhadas para o padrão. O IMQ parece a solução certa para os usuários do iptables.
ornoone 25/09
@ SérgioCarvalho Não consigo fazer isso funcionar em conjunto com o controlador net_cls do cgroups. Estou um pouco confuso, porque posso limitar o tráfego de rede de saída normal (saída) usando net_cls em conjunto com tc? Meu melhor palpite é que, por alguma razão, usando ifb dessa maneira, os pacotes de saída que saem do ifb não estão sendo marcados corretamente desde que começaram como ingresso? Alguém pode confirmar isso ou sugerir uma maneira que eu possa?
Rooster
3

Baseado na resposta de Sérgio Carvalho, fiz um pequeno script bash para limitar a largura de banda:

Nome do arquivo: netspeed

#!/bin/bash 

#USAGE: sudo ./netspeed -l limit_in_kbit -s
usage="sudo $(basename "$0") -l speed_limit -s
  -l speed_limit - speed limit with units (eg. 1mbit, 100kbit, more on \`man tc\`)
  -s - remove all limits
"

# default values
LIMIT=0
STOP=0

# hardcoded constats
IFACE=ifb0 # fake interface name which will be used for shaping the traffic
NETFACE=wlan0 # interface which in connected to the internet

# shift all required and leave only optional

while getopts ':hl:s' option; do
  case "$option" in
   l) LIMIT=$OPTARG
      ;;
   s) STOP=1
      ;;
   h) echo "$usage"
      exit
      ;;
  esac
done

#
# functions used in script
#
function limitExists { # detected by ingress on $NETFACE qdisc
   # -n equals true if non-zero string length
  if [[ -n `tc qdisc show | grep "ingress .* $NETFACE"` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi

}
function ifaceExists {
  # -n equals true if non-zero string length
  if [[ -n `ifconfig -a | sed 's/[ \t].*//;/^\(lo\|\)$/d' | grep $IFACE` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi
}
function ifaceIsUp {
  # -n equals true if non-zero string length
  if [[ -n `ifconfig | sed 's/[ \t].*//;/^\(lo\|\)$/d' | grep $IFACE` ]]
  then
    return 0 # true
  else
    return 1 # false
  fi
}
function createLimit {
  #3. redirect ingress
  tc qdisc add dev $NETFACE handle ffff: ingress
  tc filter add dev $NETFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev $IFACE

  #4. apply egress rules to local inteface (like wlan0)
  tc qdisc add dev $NETFACE root handle 1: htb default 10
  tc class add dev $NETFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class add dev $NETFACE parent 1:1 classid 1:10 htb rate $LIMIT

  #5. and same for our relaying virtual interfaces (to simulate ingress)
  tc qdisc add dev $IFACE root handle 1: htb default 10
  tc class add dev $IFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class add dev $IFACE parent 1:1 classid 1:10 htb rate $LIMIT
}
function updateLimit {
  #3. redirect ingress
  tc filter replace dev $NETFACE parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev $IFACE

  #4. apply egress rules to local inteface (like wlan0)
  tc class replace dev $NETFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class replace dev $NETFACE parent 1:1 classid 1:10 htb rate $LIMIT

  #5. and same for our relaying virtual interfaces (to simulate ingress)
  tc class replace dev $IFACE parent 1: classid 1:1 htb rate $LIMIT
  tc class replace dev $IFACE parent 1:1 classid 1:10 htb rate $LIMIT
}
function removeLimit {
  if limitExists ; then
    tc qdisc del dev $NETFACE ingress
    tc qdisc del dev $NETFACE root
    tc qdisc del dev $IFACE root
  fi
  if ifaceIsUp ; then
    ip link set dev $IFACE down
  fi
}

#
# main script
#
if [[ `whoami` != "root" ]]; then
  echo "WARNING: script must be executed with root privileges!"
  echo $usage
  exit 1
fi
if [ $STOP -eq 1 ]; then
  echo "REMOVING limit"
  removeLimit
  echo "limit REMOVED"
elif [ "$LIMIT" != "0" ]; then
  # prepare interface
  if ! ifaceExists ; then
    echo "CREATING $IFACE by modprobe"
    modprobe ifb numifbs=1
    if ! ifaceExists ; then
      echo "creating $IFACE by modprobe FAILED"
      echo "exit with ERROR code 2"
      exit 2
    fi
  fi
  # set interface up
  if ifaceIsUp ; then
    echo "$IFACE is already up"
  else
    echo "set $IFACE up"
    ip link set dev $IFACE up # ( use ifconfig to see results)
    if ifaceIsUp ; then
      echo "$IFACE is up"
    else
      echo "enabling $IFACE by ip link FAILED"
      echo "exit with ERROR code 3"
      exit 3
    fi
  fi

  # create/update limits
  if limitExists ; then
    echo "update limit"
    updateLimit
  else
    echo "create limit"
    createLimit
  fi

  echo "limit CREATED"
  exit 0
else
  echo $usage
fi
jmarceli
fonte