Tc: входная охрана и ifb зеркалирование

20

Я пытаюсь настроить формирование трафика на шлюзе Linux, как написано здесь . Сценарий должен быть настроен, потому что у меня есть несколько интерфейсов локальной сети. Поэтому для формирования стороны локальной сети я планирую создать псевдоустройство 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

Сценарий из упомянутого выше репозитория gist имеет следующие строки:

 /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

Этот код и код создания интерфейса ifb плохо ладят друг с другом. Настроенный скрипт выполняется, но устройство ifb0 не показывает статистику трафика. Если я закомментирую код входящего репо (приведенный выше), тогда устройство ifb0 показывает количество переданных пакетов. Также эти строки не могут быть выполнены вместе:

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

Я получаю файл существует ошибка. Итак, как я могу формировать вход на WAN_INTERFACE и в то же время также формировать трафик, который идет в LAN через устройство ifb0?

nixnotwin
источник

Ответы:

41

IFB является альтернативой tc-фильтрам для обработки входящего трафика, перенаправляя его на виртуальный интерфейс и обрабатывая его как выходной трафик. Вам нужен один интерфейс ifb для каждого физического интерфейса, чтобы перенаправить входящий трафик с eth0 на ifb0, eth1 на ifb1 и т. Д. на.

Вставляя модуль ifb, укажите количество нужных вам виртуальных интерфейсов. По умолчанию 2:

modprobe ifb numifbs=1

Теперь включите все интерфейсы ifb:

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

И перенаправить входящий трафик с физических интерфейсов на соответствующий интерфейс ifb. Для 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

Повторите еще раз для eth1 -> ifb1, eth2 -> ifb2 и так далее, пока все интерфейсы, которые вы хотите сформировать, не будут покрыты.

Теперь вы можете применять все правила, которые вы хотите. Правила выхода для eth0 идут как обычно в eth0. Давайте ограничим пропускную способность, например:

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

Излишне говорить, повторить для eth1, eth2, ...

Входящие правила для eth0, теперь идут как выходные правила для ifb0 (все, что входит в ifb0, должно выходить, и только входной трафик eth0 входит в ifb0). Опять же, пример ограничения пропускной способности:

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

Преимущество этого подхода состоит в том, что правила выхода намного более гибкие, чем фильтры входа. Фильтры позволяют только отбрасывать пакеты, а не вводить время ожидания, например. Обрабатывая входящий трафик как выходной, вы можете настроить порядок очереди с классами трафика и, если необходимо, фильтрами. Вы получаете доступ ко всему дереву tc, а не только к простым фильтрам.

Сержиу Карвалью
источник
Красиво сделано. Всегда приятно видеть, как профессионал выскакивает с первым ответом рок-звезды.
Магеллан
Это может быть наивный вопрос, но я не могу найти конкретную информацию. Основываясь на этом ответе (что здорово, между прочим), возможно ли получить ifb0статистику специально для classid cgroup? То есть я могу успешно получить исходящую статистику для cgroup с фильтром classid. Но можно ли учитывать входящий трафик для каждой группы?
JDI
Имейте в виду, что если вы используете iptable, чтобы пометить свой пакет, а затем отфильтровать их, вы не сможете использовать ifb, поскольку весь входящий трафик будет перенаправлен ДО любой маркировки. таким образом, ваш класс останется равным 0, и все будут перенаправлены по умолчанию. IMQ кажется самым подходящим решением для пользователей iptables.
ornoone
@ SérgioCarvalho Я не могу заставить это работать в тандеме с контроллером net_cls cgroups. Я немного запутался, потому что я могу ограничить нормальный исходящий сетевой трафик (выход), используя net_cls в тандеме с tc? Мое лучшее предположение состоит в том, что для некоторой причины, использующей ifb таким образом, что исходящие пакеты, выходящие из ifb, не маркируются должным образом, так как они начинались как входные? Кто-нибудь может подтвердить это или предложить способ, которым я могу?
Петух
3

Основываясь на ответе Сержиу Карвалью, я создал небольшой bash-скрипт для ограничения пропускной способности:

Имя файла: 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
источник