Tc: ingressing policying i ifb mirroring

20

Próbuję skonfigurować kształtowanie ruchu w bramie systemu Linux, jak napisano tutaj . Skrypt należy dostosować, ponieważ mam wiele interfejsów LAN. Aby ukształtować stronę LAN, planuję stworzyć pseudo urządzenie ifb:

     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

Skrypt z wyżej wymienionego repozytorium gist ma następujące wiersze:

 /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

Ten kod i kod tworzenia interfejsu ifb nie układają się dobrze. Spersonalizowany skrypt zostanie wykonany, ale urządzenie ifb0 nie wyświetla żadnych statystyk ruchu. Jeśli skomentuję wejściowy kod Gist Repo (cytowany powyżej), to urządzenie ifb0 pokazuje liczbę przesyłanych pakietów. Również tych wierszy nie można wykonać razem:

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

Otrzymuję plik istnieje błąd. Jak więc mogę kształtować wejście na WAN_INTERFACE, a jednocześnie kształtować ruch do sieci LAN za pośrednictwem urządzenia ifb0?

nixnotwin
źródło

Odpowiedzi:

41

IFB jest alternatywą dla filtrów tc do obsługi ruchu wejściowego, przekierowując go do interfejsu wirtualnego i traktując go jako ruch wyjściowy. Potrzebujesz jednego interfejsu ifb na interfejs fizyczny, aby przekierować ruch wejściowy z eth0 na ifb0, eth1 na ifb1 i tak dalej na.

Podczas wkładania modułu ifb podaj liczbę potrzebnych interfejsów wirtualnych. Domyślnie jest to 2:

modprobe ifb numifbs=1

Teraz włącz wszystkie interfejsy ifb:

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

I przekierowuj ruch przychodzący z fizycznych interfejsów do odpowiedniego interfejsu ifb. Dla 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

Ponownie, powtórz dla eth1 -> ifb1, eth2 -> ifb2 i tak dalej, aż wszystkie interfejsy, które chcesz kształtować, zostaną pokryte.

Teraz możesz zastosować wszystkie potrzebne reguły. Zasady wyjścia dla eth0 idą jak zwykle w eth0. Ograniczmy przepustowość, na przykład:

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

Nie trzeba dodawać, powtórz dla eth1, eth2, ...

Reguły wejścia dla eth0, teraz działają jako reguły wyjścia dla ifb0 (cokolwiek wchodzi w ifb0 musi wyjść, a tylko ruch przychodzący eth0 przechodzi do ifb0). Ponownie przykład ograniczenia przepustowości:

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

Zaletą tego podejścia jest to, że reguły wychodzenia są znacznie bardziej elastyczne niż filtry wejściowe. Filtry pozwalają tylko upuszczać pakiety, a nie wprowadzać na przykład czasów oczekiwania. Obsługując ruch przychodzący jako wyjściowy, możesz ustawić dyscypliny kolejki, z klasami ruchu i, w razie potrzeby, filtrami. Otrzymujesz dostęp do całego drzewa TC, nie tylko prostych filtrów.

Sérgio Carvalho
źródło
Ładnie wykonane. Zawsze dobrze jest widzieć profesjonalistę wyskakującego z pierwszą gwiazdą rocka.
Magellan,
To może być naiwne pytanie, ale nie mogę znaleźć konkretnych informacji. Czy na podstawie tej odpowiedzi (która jest świetna przy okazji) można uzyskać ifb0statystyki specjalnie dla klasy cgroup classid? Oznacza to, że mogę z powodzeniem uzyskać statystyki wyjścia dla grupy cgroup z filtrem classid. Ale czy ruch przychodzący może być również rozliczany indywidualnie dla każdej grupy?
jdi
pamiętaj, że jeśli użyjesz iptable, aby oznaczyć swój pakiet, a następnie je odfiltrować, nie możesz użyć ifb, ponieważ cały ruch przychodzący zostanie przekazany PRZED jakimkolwiek oznaczeniem. więc zobaczysz, że Twoja klasa pozostanie na poziomie 0 i wszystkie zostaną przesłane do ustawień domyślnych. IMQ wydaje się sztywnym rozwiązaniem dla użytkowników iptables.
ornoone
@ SérgioCarvalho Nie wydaje mi się, żeby działało to w tandemie z kontrolerem net_cls cgroups. Jestem trochę zdezorientowany, ponieważ mogę ograniczyć normalny wychodzący ruch sieciowy (wyjście) za pomocą net_cls w połączeniu z tc? Wydaje mi się, że od jakiegoś czasu używa się ifb w ten sposób, że pakiety wychodzące wychodzące z ifb nie są odpowiednio oznakowane, odkąd zaczęły jako ingres? Czy ktoś może to potwierdzić lub zasugerować sposób, w jaki mogę?
Kogut
3

Na podstawie odpowiedzi Sérgio Carvalho napisałem mały skrypt bash, aby ograniczyć przepustowość:

Nazwa pliku: prędkość sieci

#!/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
źródło