Tc: ingress policing dan ifb mirroring

20

Saya mencoba mengatur pembentukan traffic pada gateway Linux seperti yang tertulis di sini . Skrip harus disesuaikan karena saya memiliki beberapa antarmuka LAN. Jadi untuk membentuk sisi LAN saya berencana untuk membuat perangkat pseudo ifb seperti:

     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

Script dari repo inti yang disebutkan di atas memiliki baris berikut:

 /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

Kode ini dan kode pembuatan antarmuka ifb tidak cocok bersama. Skrip khusus dijalankan, tetapi perangkat ifb0 tidak menampilkan statistik lalu lintas apa pun. Jika saya berkomentar keluar kode intisari intress (dikutip di atas), maka perangkat ifb0 menunjukkan jumlah paket yang ditransfer. Baris-baris ini juga tidak dapat dieksekusi bersama:

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

Saya mendapatkan file ada kesalahan. Jadi, bagaimana cara membentuk ingress di WAN_INTERFACE dan pada saat yang sama juga membentuk lalu lintas yang menuju ke LAN melalui perangkat ifb0?

nixnotwin
sumber

Jawaban:

41

IFB adalah alternatif untuk filter tc untuk menangani lalu lintas masuk, dengan mengarahkan ulang ke antarmuka virtual dan memperlakukan adalah sebagai lalu lintas keluar di sana. di.

Saat memasukkan modul ifb, beri tahu jumlah antarmuka virtual yang Anda butuhkan. Standarnya adalah 2:

modprobe ifb numifbs=1

Sekarang, aktifkan semua antarmuka ifb:

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

Dan mengarahkan lalu lintas masuknya dari antarmuka fisik ke antarmuka ifb yang sesuai. Untuk 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

Sekali lagi, ulangi untuk eth1 -> ifb1, eth2 -> ifb2 dan seterusnya, sampai semua antarmuka yang ingin Anda bentuk tertutup.

Sekarang, Anda dapat menerapkan semua aturan yang Anda inginkan. Aturan jalan keluar untuk eth0 berjalan seperti biasa di eth0. Mari kita batasi bandwidth, misalnya:

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

Tak perlu dikatakan, ulangi untuk eth1, eth2, ...

Aturan jalan masuk untuk eth0, sekarang pergi sebagai aturan jalan keluar di ifb0 (apa pun yang masuk ke ifb0 harus keluar, dan hanya lalu lintas masuk eth0 masuk ke ifb0). Sekali lagi, contoh batas bandwidth:

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

Keuntungan dari pendekatan ini adalah bahwa aturan jalan keluar jauh lebih fleksibel daripada filter masuknya. Filter hanya memungkinkan Anda untuk menjatuhkan paket, bukan memperkenalkan waktu tunggu, misalnya. Dengan menangani lalu lintas masuk sebagai jalan keluar, Anda dapat mengatur disiplin antrian, dengan kelas lalu lintas dan, jika perlu, filter. Anda mendapatkan akses ke seluruh pohon tc, tidak hanya filter sederhana.

Sérgio Carvalho
sumber
Bagus sekali. Selalu baik untuk melihat seorang profesional muncul dengan jawaban pertama bintang rock.
Magellan
Ini mungkin pertanyaan yang naif, tetapi saya gagal menemukan informasi spesifik. Berdasarkan jawaban ini (yang merupakan btw hebat), apakah mungkin untuk mendapatkan ifb0statistik khusus untuk cgroup classid? Artinya, saya bisa berhasil mendapatkan statistik jalan keluar untuk cgroup dengan filter classid. Tetapi bisakah lalu lintas masuk juga diperhitungkan berdasarkan per kelompok?
jdi
perlu diketahui bahwa jika Anda menggunakan iptable untuk menandai paket Anda dan kemudian memfilternya, Anda tidak dapat menggunakan ifb karena semua trafik masuk akan diteruskan sebelum penandaan apa pun. sehingga Anda akan melihat kelas Anda tetap pada 0 dan semua diteruskan ke default. IMQ tampaknya solusi yang tepat untuk pengguna iptables.
ornoone
@ SérgioCarvalho Sepertinya saya tidak bisa menjalankannya bersamaan dengan pengontrol cgroup net_cls. Saya agak bingung, karena saya dapat membatasi lalu lintas jaringan keluar normal (keluar) menggunakan net_cls bersama-sama dengan tc? Tebakan terbaik saya adalah bahwa untuk suatu alasan menggunakan ifb dengan cara ini bahwa paket-paket keluar yang keluar dari ifb tidak ditandai dengan benar sejak mereka mulai masuk sebagai ingress? Adakah yang bisa mengkonfirmasi ini atau menyarankan cara yang saya bisa?
Ayam jantan
3

Berdasarkan jawaban Sérgio Carvalho saya membuat skrip bash kecil untuk membatasi bandwidth:

Nama file: 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
sumber