Tc: police d'entrée et mise en miroir ifb

20

J'essaie de configurer la mise en forme du trafic sur une passerelle Linux comme écrit ici . Le script doit être personnalisé car j'ai plusieurs interfaces LAN. Donc, pour façonner le côté LAN, je prévois de créer un pseudo périphérique ifb comme ceci:

     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

Le script du dépôt gist mentionné ci-dessus a ces lignes:

 /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

Ce code et le code de création d'interface ifb ne s'entendent pas bien. Le script personnalisé est exécuté, mais le périphérique ifb0 n'affiche aucune statistique de trafic. Si je commente le code repo de l'entrée gist (cité ci-dessus), alors le périphérique ifb0 affiche le nombre de paquets qui sont transférés. De plus, ces lignes ne peuvent pas être exécutées ensemble:

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

J'obtiens le fichier existe une erreur. Alors, comment puis-je façonner l'entrée sur WAN_INTERFACE et en même temps façonner également le trafic qui va au LAN via le périphérique ifb0?

nixnotwin
la source

Réponses:

41

IFB est une alternative aux filtres tc pour gérer le trafic entrant, en le redirigeant vers une interface virtuelle et le traiter comme un trafic de sortie là-bas. Vous avez besoin d'une interface ifb par interface physique, pour rediriger le trafic entrant de eth0 vers ifb0, eth1 vers ifb1, etc. sur.

Lors de l'insertion du module ifb, indiquez-lui le nombre d'interfaces virtuelles dont vous avez besoin. La valeur par défaut est 2:

modprobe ifb numifbs=1

Maintenant, activez toutes les interfaces ifb:

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

Et redirigez le trafic entrant des interfaces physiques vers l'interface ifb correspondante. Pour 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

Encore une fois, répétez pour eth1 -> ifb1, eth2 -> ifb2 et ainsi de suite, jusqu'à ce que toutes les interfaces que vous souhaitez façonner soient couvertes.

Maintenant, vous pouvez appliquer toutes les règles que vous souhaitez. Les règles de sortie pour eth0 vont comme d'habitude dans eth0. Limitons la bande passante, par exemple:

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

Inutile de dire que répéter pour eth1, eth2, ...

Les règles d'entrée pour eth0 vont maintenant comme règles de sortie sur ifb0 (tout ce qui entre dans ifb0 doit sortir, et seul le trafic d'entrée eth0 entre dans ifb0). Encore une fois, un exemple de limite de bande passante:

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

L'avantage de cette approche est que les règles de sortie sont beaucoup plus flexibles que les filtres d'entrée. Les filtres vous permettent uniquement de supprimer des paquets, et non d'introduire des temps d'attente, par exemple. En traitant le trafic entrant comme sortie, vous pouvez configurer des disciplines de file d'attente, avec des classes de trafic et, si nécessaire, des filtres. Vous avez accès à l'ensemble de l'arborescence tc, pas seulement aux filtres simples.

Sérgio Carvalho
la source
Bien fait. C'est toujours bon de voir un professionnel apparaître avec une première réponse de rock star.
Magellan
C'est peut-être une question naïve, mais je n'arrive pas à trouver des informations spécifiques. Sur la base de cette réponse (ce qui est génial), est-il possible d'obtenir des ifb0statistiques spécifiques pour un classid cgroup? Autrement dit, je peux obtenir avec succès les statistiques de sortie pour un groupe de contrôle avec un filtre classid. Mais le trafic entrant peut-il également être pris en compte par groupe de contrôle?
jdi
sachez que si vous utilisez iptable pour marquer votre paquet puis les filtre, vous ne pouvez pas utiliser ifb car tout le trafic entrant sera transmis AVANT tout marquage. vous verrez donc votre séjour de classe à 0 et tous transférés par défaut. IMQ semble être la meilleure solution pour les utilisateurs d'iptables.
ornoone
@ SérgioCarvalho Je n'arrive pas à faire fonctionner cela en tandem avec le contrôleur net_cls de cgroups. Je suis un peu confus, car je peux limiter le trafic réseau sortant normal (sortie) en utilisant net_cls en tandem avec tc? Ma meilleure supposition est que, pour une raison quelconque, en utilisant ifb de cette manière, les paquets de sortie sortant de ifb ne sont pas correctement étiquetés depuis qu'ils ont commencé comme entrée? Quelqu'un peut-il confirmer cela ou suggérer un moyen que je peux?
Coq
3

Sur la base de la réponse de Sérgio Carvalho, j'ai fait un petit script bash pour limiter la bande passante:

Nom du fichier: 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
la source