Tc: เข้าการรักษาและการสะท้อน ifb


20

ฉันพยายามที่จะติดตั้งการปรับการจราจรบนเกตเวย์ Linux เป็นลายลักษณ์อักษรที่นี่ สคริปต์ต้องได้รับการปรับแต่งเพราะฉันมีอินเตอร์เฟส LAN หลายอัน ดังนั้นเพื่อรูปร่างด้าน LAN ฉันวางแผนที่จะสร้างอุปกรณ์หลอก 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

สคริปต์จาก repo ส่วนสำคัญดังกล่าวข้างต้นมีบรรทัดเหล่านี้:

 /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 ไม่แสดงสถิติการเข้าชมใด ๆ ถ้าฉันใส่เครื่องหมายเอาความในส่วนของรหัส repo (ที่ยกมาข้างต้น) อุปกรณ์ ifb0 จะแสดงจำนวนของแพ็คเก็ตที่ถูกถ่ายโอน ด้วยบรรทัดเหล่านี้ไม่สามารถดำเนินการร่วมกัน:

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

ฉันได้รับไฟล์ข้อผิดพลาด ดังนั้นฉันจะกำหนดรูปทรงให้เข้ากับ WAN_INTERFACE ได้อย่างไรและในขณะเดียวกันก็กำหนดรูปแบบการรับส่งข้อมูลที่ไปยัง LAN ผ่านอุปกรณ์ ifb0

คำตอบ:


41

IFB เป็นทางเลือกแทนตัวกรอง tc สำหรับจัดการการรับส่งข้อมูลโดยเปลี่ยนเส้นทางไปยังอินเทอร์เฟซเสมือนและถือว่าเป็นปริมาณการส่งออกที่นั่นคุณต้องการอินเทอร์เฟซ ifb หนึ่งตัวต่อหนึ่งอินเทอร์เฟซทางกายภาพ บน.

เมื่อแทรกโมดูล 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 และต่อไปเรื่อย ๆ จนกระทั่งอินเตอร์เฟสทั้งหมดที่คุณต้องการสร้างรูปร่างนั้นครอบคลุม

ตอนนี้คุณสามารถใช้กฎทั้งหมดที่คุณต้องการ กฎ Egress สำหรับ 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, ...

กฎของ Ingress สำหรับ eth0 ตอนนี้ใช้เป็น egress rule บน ifb0 (อะไรก็ตามที่เข้าสู่ 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

ข้อดีของวิธีนี้คือกฏของ egress นั้นมีความยืดหยุ่นมากกว่าตัวกรองทางเข้า ตัวกรองอนุญาตให้คุณวางแพ็กเก็ตเท่านั้นไม่แนะนำเวลารอเป็นต้น ด้วยการจัดการทราฟฟิกขาเข้าเป็น egress คุณสามารถตั้งค่าวินัยของคิวด้วยคลาสทราฟฟิกและหากจำเป็นต้องมีตัวกรอง คุณสามารถเข้าถึงแผนผัง tc ทั้งหมดไม่เพียง แต่ตัวกรองอย่างง่าย


ทำได้ดีมาก ดีเสมอที่เห็นมืออาชีพโผล่ขึ้นมาพร้อมกับคำตอบแรกของร็อคสตาร์
Magellan

นี่อาจเป็นคำถามที่ไร้เดียงสา แต่ฉันไม่สามารถหาข้อมูลที่เฉพาะเจาะจงได้ ตามคำตอบนี้ (ซึ่งเป็น btw ที่ดี) เป็นไปได้ที่จะได้รับifb0สถิติเฉพาะสำหรับ cgroup classid หรือไม่ นั่นคือฉันสามารถรับสถิติ egress สำหรับ cgroup ด้วยตัวกรอง classid ได้สำเร็จ แต่ปริมาณการรับข้อมูลที่เข้ามาสามารถถูกคิดตามจำนวนกลุ่มต่อกลุ่มได้หรือไม่?
jdi

โปรดระวังว่าถ้าคุณใช้ iptable เพื่อทำเครื่องหมายแพ็คเก็ตของคุณแล้วกรองคุณจะไม่สามารถใช้ ifb ได้เนื่องจาก trafic ทางเข้าทั้งหมดจะถูกส่งต่อก่อนการทำเครื่องหมายใด ๆ ดังนั้นคุณจะเห็นชั้นเรียนของคุณอยู่ที่ 0 และส่งต่อไปยังค่าเริ่มต้น IMQ ดูเหมือนจะเป็นทางออกที่เข้มงวดสำหรับผู้ใช้ iptables
ornoone

@ SérgioCarvalhoฉันไม่สามารถทำให้มันทำงานร่วมกับตัวควบคุม net_cls ของกลุ่ม cg ได้ ฉันสับสนเล็กน้อยเพราะฉันสามารถ จำกัด ทราฟฟิกเครือข่ายขาออกปกติ (egress) โดยใช้ net_cls ควบคู่กับ tc? การเดาที่ดีที่สุดของฉันคือสำหรับบางครั้งที่ใช้ ifb ในลักษณะนี้ว่าแพ็กเก็ต egress ที่ออกมาจาก ifb นั้นไม่ได้รับการติดแท็กอย่างถูกต้องตั้งแต่พวกเขาเริ่มต้นจากการเป็นทางเข้า? ใครสามารถยืนยันสิ่งนี้หรือแนะนำวิธีที่ฉันสามารถทำได้?
ไก่

3

จากคำตอบSérgio Carvalho ฉันสร้างสคริปต์ทุบตีเล็ก ๆ เพื่อ จำกัด แบนด์วิดท์:

ชื่อไฟล์: 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.