Tc: vigilancia policial de entrada y duplicación ifb

20

Estoy tratando de configurar el modelado del tráfico en una puerta de enlace Linux como está escrito aquí . El script necesita ser personalizado porque tengo múltiples interfaces LAN. Entonces, para dar forma al lado de la LAN, estoy planeando crear un pseudo dispositivo ifb de esta manera:

     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

El guión del repositorio general mencionado anteriormente tiene estas líneas:

 /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

Este código y el código de creación de la interfaz ifb no se llevan bien juntos. El script personalizado se ejecuta, pero el dispositivo ifb0 no muestra ninguna estadística de tráfico. Si comento el código de ingreso de repositorio gist (citado anteriormente), entonces el dispositivo ifb0 muestra la cantidad de paquetes que se transfieren. Además, estas líneas no se pueden ejecutar juntas:

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

Me sale el archivo existe error. Entonces, ¿cómo puedo configurar la entrada en WAN_INTERFACE y al mismo tiempo también configurar el tráfico que va a LAN a través del dispositivo ifb0?

nixnotwin
fuente

Respuestas:

41

IFB es una alternativa a los filtros tc para manejar el tráfico de entrada, al redirigirlo a una interfaz virtual y tratarlo como tráfico de salida allí. Necesita una interfaz ifb por interfaz física, para redirigir el tráfico de entrada de eth0 a ifb0, eth1 a ifb1 y así en.

Al insertar el módulo ifb, dígale la cantidad de interfaces virtuales que necesita. El valor predeterminado es 2:

modprobe ifb numifbs=1

Ahora, habilite todas las interfaces ifb:

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

Y redirija el tráfico de entrada desde las interfaces físicas a la interfaz ifb correspondiente. 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

Nuevamente, repita para eth1 -> ifb1, eth2 -> ifb2 y así sucesivamente, hasta que se cubran todas las interfaces que desea dar forma.

Ahora, puede aplicar todas las reglas que desee. Las reglas de salida para eth0 van como de costumbre en eth0. Limitemos el ancho de banda, por ejemplo:

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

No hace falta decir que repita para eth1, eth2, ...

Reglas de entrada para eth0, ahora vaya como reglas de salida en ifb0 (todo lo que entra en ifb0 debe salir, y solo el tráfico de entrada eth0 entra en ifb0). De nuevo, un ejemplo de límite de ancho 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

La ventaja de este enfoque es que las reglas de salida son mucho más flexibles que los filtros de entrada. Los filtros solo le permiten descartar paquetes, no introducir tiempos de espera, por ejemplo. Al manejar el tráfico de entrada como salida, puede configurar disciplinas de cola, con clases de tráfico y, si es necesario, filtros. Obtiene acceso a todo el árbol tc, no solo a filtros simples.

Sergio Carvalho
fuente
Bien hecho. Siempre es bueno ver aparecer a un profesional con una primera respuesta de estrella de rock.
Magellan
Esta puede ser una pregunta ingenua, pero no encuentro información específica. Basado en esta respuesta (que es genial por cierto), ¿es posible obtener ifb0estadísticas específicamente para un cgroup classid? Es decir, puedo obtener con éxito las estadísticas de salida de un cgroup con un filtro classid. Pero, ¿el tráfico entrante también puede contabilizarse por grupo?
jdi
tenga en cuenta que si usa iptable para marcar su paquete y luego los filtra, no puede usar ifb ya que todo el tráfico de ingreso se enviará ANTES de cualquier marca. así que verás que tu clase permanezca en 0 y todo se reenviará al valor predeterminado IMQ parece la solución adecuada para los usuarios de iptables.
ornoone
@ SérgioCarvalho Parece que no puedo hacer que esto funcione en conjunto con el controlador net_cls de cgroups. Estoy un poco confundido, porque puedo limitar el tráfico de red saliente normal (salida) usando net_cls junto con tc. Mi mejor conjetura es que, por alguna razón, usando ifb de esta manera, ¿los paquetes de salida que salen de ifb no se etiquetan correctamente desde que comenzaron como ingreso? ¿Alguien puede confirmar esto o sugerir una forma en que yo pueda?
Gallo
3

Basado en la respuesta de Sérgio Carvalho, hice un pequeño script bash para limitar el ancho de banda:

Nombre de archivo: 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
fuente