iproute2 ile Linux’ta Trafik Şekillendirme ve Bant Genişliği Sınırlandırma (tc Komutu)

Ağ yönetiminde en çok ihmal edilen konulardan biri trafik şekillendirme. Çoğu sysadmin firewall kurallarına, routing tablolarına saatler harcar ama bant genişliği kontrolüne gelince “yeter ki çalışsın” moduna geçer. Oysa üretim ortamlarında bir backup işleminin tüm ağı yutması, bir müşterinin diğerlerinin bant genişliğini tüketmesi ya da kritik servislerin yoğun trafik dönemlerinde yavaşlaması gerçek sorunlar. Bu yazıda Linux’un trafik kontrolü alt sistemini, tc komutunu ve pratikte nasıl kullanacağınızı elimden geldiğince gerçekçi senaryolarla anlatmaya çalışacağım.

Linux Trafik Kontrolünün Temel Mantığı

Linux’ta trafik şekillendirme, kernel’in içinde yaşayan Traffic Control (TC) alt sistemi üzerinden çalışır. iproute2 paketinin bir parçası olan tc komutu bu sisteme kullanıcı alanından erişim sağlar.

İşin merkezinde qdisc (queueing discipline) kavramı var. Basitçe söylemek gerekirse: ağ arayüzünden çıkacak paketlerin nasıl sıraya alınacağını, hangisinin önce gönderileceğini ve hangisinin geciktirileceğini ya da atılacağını belirleyen kurallar bütünü. Her ağ arayüzünün hem çıkış (egress) hem de giriş (ingress) yönünde bir qdisc’i var.

Üç temel yapı taşı şunlar:

  • qdisc: Kuyruk disiplini. Paketlerin nasıl işleneceğini tanımlar.
  • class: qdisc içindeki alt bölümler. Farklı trafik türlerine farklı davranışlar atamak için kullanılır.
  • filter: Paketleri sınıflandırıp ilgili class’a yönlendiren kurallar.

Önemli bir nokta: tc varsayılan olarak yalnızca egress (çıkış) trafiğini şekillendirebilir. Giriş trafiğini sınırlandırmak için ingress qdisc ve IFB (Intermediate Functional Block) gibi numaralara başvurmak gerekir, bunu da yazının ilerleyen kısımlarında göreceğiz.

Ortam Hazırlığı

Başlamadan önce sistemde iproute2 paketinin kurulu olduğunu doğrulayın:

tc -Version
# ya da
ip -Version

Çoğu modern Linux dağıtımında bu paket varsayılan olarak geliyor. Gelmiyorsa:

# Debian/Ubuntu
apt install iproute2

# RHEL/CentOS/Rocky
dnf install iproute

Mevcut arayüzlerin qdisc durumunu görmek için:

tc qdisc show
# ya da belirli bir arayüz için
tc qdisc show dev eth0

Temiz bir sistemde genellikle pfifo_fast veya mq gibi varsayılan qdisc’leri görürsünüz.

Senaryo 1: Basit Bant Genişliği Sınırı (TBF)

Diyelim ki bir dosya sunucunuz var ve gece yarısı backup işlemleri tüm hat kapasitesini tüketiyor. Basit bir çözüm: tbf (Token Bucket Filter) qdisc kullanarak çıkış trafiğini sınırlandırmak.

# eth0 üzerinde çıkış trafiğini 50 Mbit/s ile sınırla
tc qdisc add dev eth0 root tbf 
    rate 50mbit 
    burst 32kbit 
    latency 400ms

Parametrelerin anlamları:

  • rate: Hedef bant genişliği. 50mbit, 100kbit, 1gbit gibi değerler alır.
  • burst: Token bucket’ın anlık taşıyabileceği maksimum veri miktarı. Çok düşük tutarsanız CPU yükü artar.
  • latency: Bir paketin kuyrukta bekleyebileceği maksimum süre. Bu süreyi aşan paketler atılır.

Kuralı kaldırmak için:

tc qdisc del dev eth0 root

Mevcut durumu kontrol etmek için:

tc qdisc show dev eth0
tc -s qdisc show dev eth0  # istatistiklerle birlikte

TBF güzel ve basit ama sınırlı. Tüm trafiği tek bir kova içine atıyor. Farklı servislere farklı öncelikler vermek istiyorsanız HTB’ye ihtiyacınız var.

Senaryo 2: HTB ile Hiyerarşik Bant Genişliği Yönetimi

HTB (Hierarchical Token Bucket), üretim ortamlarında en çok tercih edilen qdisc türü. Trafiği sınıflara ayırmanıza, her sınıfa garanti ve maksimum bant genişliği tanımlamanıza izin veriyor. Kullanılmayan bant genişliği de başka sınıflarla paylaşılabiliyor.

Şu senaryoyu ele alalım: 1 Gbit/s hat kapasiteli bir sunucuda şu trafikleri yönetmek istiyorsunuz:

  • HTTP/HTTPS trafiği: Minimum 600 Mbit/s garanti, maksimum 900 Mbit/s
  • Veritabanı replikasyon trafiği: Minimum 200 Mbit/s garanti, maksimum 400 Mbit/s
  • Yedekleme trafiği: Minimum 50 Mbit/s garanti, maksimum 150 Mbit/s
# Önce mevcut kuralları temizle
tc qdisc del dev eth0 root 2>/dev/null

# Root HTB qdisc ekle
tc qdisc add dev eth0 root handle 1: htb default 30

# Toplam bant genişliği için ana sınıf
tc class add dev eth0 parent 1: classid 1:1 htb rate 1gbit burst 15k

# HTTP/HTTPS sınıfı (classid 1:10)
tc class add dev eth0 parent 1:1 classid 1:10 htb 
    rate 600mbit 
    ceil 900mbit 
    burst 15k 
    prio 1

# Veritabanı replikasyon sınıfı (classid 1:20)
tc class add dev eth0 parent 1:1 classid 1:20 htb 
    rate 200mbit 
    ceil 400mbit 
    burst 15k 
    prio 2

# Yedekleme sınıfı (classid 1:30) - default olarak işaretli
tc class add dev eth0 parent 1:1 classid 1:30 htb 
    rate 50mbit 
    ceil 150mbit 
    burst 15k 
    prio 3

Parametrelerin anlamları:

  • handle: qdisc veya class’ın benzersiz tanımlayıcısı. major:minor formatında.
  • rate: Garanti edilen minimum bant genişliği.
  • ceil: Kullanılabilecek maksimum bant genişliği (burst kapasitesi dahil).
  • burst: Anlık maksimum paket boyutu. Genellikle rate/8000 değeri iyi bir başlangıç noktasıdır.
  • prio: Öncelik değeri. Düşük değer daha yüksek öncelik anlamına gelir.
  • default: Sınıflandırılamamış trafiklerin düşeceği class ID.

Şimdi her sınıfa bir yaprak qdisc ekleyelim (opsiyonel ama önerilen):

# Her sınıfa SFQ (Stochastic Fairness Queueing) ekle
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10

SFQ, aynı sınıf içindeki akışlar arasında adil dağılım sağlar. perturb 10 her 10 saniyede bir hash tablosunu yeniler, bu da belirli akışların diğerlerini domine etmesini engeller.

Filtreleme: Trafiği Doğru Sınıfa Yönlendirmek

HTB sınıflarını tanımladık ama hangi paketin hangi sınıfa gideceğini henüz söylemedik. Bu iş filterların görevi:

# HTTP trafiğini (port 80 ve 443) 1:10 sınıfına yönlendir
tc filter add dev eth0 parent 1: protocol ip prio 1 u32 
    match ip dport 80 0xffff 
    flowid 1:10

tc filter add dev eth0 parent 1: protocol ip prio 1 u32 
    match ip dport 443 0xffff 
    flowid 1:10

# Veritabanı replikasyon trafiğini (PostgreSQL 5432) 1:20 sınıfına yönlendir
tc filter add dev eth0 parent 1: protocol ip prio 2 u32 
    match ip dport 5432 0xffff 
    flowid 1:20

# Belirli bir IP adresinden gelen trafiği yedekleme sınıfına yönlendir
tc filter add dev eth0 parent 1: protocol ip prio 3 u32 
    match ip dst 10.0.0.50/32 
    flowid 1:30

Tüm yapıyı bir arada görmek için:

tc -s class show dev eth0
tc filter show dev eth0

Senaryo 3: Ingress Trafiğini Sınırlandırma

Daha önce söylediğim gibi tc doğrudan giriş trafiğini şekillendiremez. Ama bir numara var: IFB (Intermediate Functional Block) sanal arayüzü. Giriş trafiğini IFB’ye yönlendirip orada egress olarak işleyebilirsiniz.

# IFB modülünü yükle
modprobe ifb

# IFB arayüzünü aktive et
ip link set dev ifb0 up

# eth0'ın ingress trafiğini ifb0'a yönlendir
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

# ifb0 üzerinde HTB ile giriş trafiğini sınırla
tc qdisc add dev ifb0 root handle 1: htb default 10
tc class add dev ifb0 parent 1: classid 1:1 htb rate 500mbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb 
    rate 500mbit 
    ceil 500mbit

Bu yapıyla sunucuya gelen trafiği de kontrol altına alabilirsiniz. Özellikle ISP’den sabit hat alıp bant genişliğini çok kiracılı (multi-tenant) ortamlarda paylaştırmanız gereken durumlarda işe yarar.

Senaryo 4: DSCP ile QoS Markalaması

Kurumsal ağlarda genellikle paketler DSCP (Differentiated Services Code Point) değerleriyle işaretlenir. tc bu işaretlere göre filtreleme yapabilir:

# DSCP EF (Expedited Forwarding - VoIP trafiği) için öncelikli sınıf
tc filter add dev eth0 parent 1: protocol ip prio 1 u32 
    match ip tos 0xb8 0xfc 
    flowid 1:10

# DSCP AF31 (iş uygulamaları) için orta öncelikli sınıf
tc filter add dev eth0 parent 1: protocol ip prio 2 u32 
    match ip tos 0x68 0xfc 
    flowid 1:20

Trafiği ilerletmeden önce DSCP değeri atamak isterseniz tc ile birlikte iptables kullanabilirsiniz:

# SSH trafiğini DSCP AF21 ile işaretle
iptables -t mangle -A OUTPUT -p tcp --dport 22 
    -j DSCP --set-dscp-class AF21

İzleme ve Hata Ayıklama

Kurduğunuz kuralların gerçekten çalışıp çalışmadığını nasıl anlarsınız? İstatistiklere bakarak:

# Tüm sınıfların istatistiklerini göster
tc -s class show dev eth0

# Gerçek zamanlı izleme için watch ile kullan
watch -n 1 'tc -s class show dev eth0'

# qdisc istatistikleri (dropped paket sayısı önemli)
tc -s qdisc show dev eth0

tc -s çıktısında dikkat etmeniz gereken alanlar:

  • Sent: Gönderilen toplam byte ve paket sayısı
  • dropped: Atılan paket sayısı. Bu değer sürekli artıyorsa rate değeriniz çok düşük ya da burst değeriniz yetersiz.
  • overlimits: Sınır aşım sayısı. Sıfır olması iyidir ama bazı durumlarda beklenen bir değer de olabilir.
  • requeues: Yeniden kuyruğa alınan paketler.

Mevcut filtreler ve eşleşme sayıları için:

tc -s filter show dev eth0

Kalıcı Yapılandırma

tc komutlarıyla yaptığınız değişiklikler sistem yeniden başladığında kaybolur. Kalıcı hale getirmek için birkaç yöntem var.

Yöntem 1: Network interface script kullanımı

Debian/Ubuntu sistemlerde /etc/network/interfaces dosyasına post-up direktifi ekleyebilirsiniz:

# /etc/network/interfaces
auto eth0
iface eth0 inet static
    address 192.168.1.10/24
    gateway 192.168.1.1
    post-up tc qdisc add dev eth0 root handle 1: htb default 30
    post-up tc class add dev eth0 parent 1: classid 1:1 htb rate 1gbit
    pre-down tc qdisc del dev eth0 root

Yöntem 2: Systemd service

Daha temiz bir yöntem, tüm tc komutlarını bir bash script’e toplayıp systemd servisi olarak çalıştırmak:

#!/bin/bash
# /usr/local/sbin/tc-setup.sh

DEV="eth0"

# Temizlik
tc qdisc del dev $DEV root 2>/dev/null

# Root qdisc
tc qdisc add dev $DEV root handle 1: htb default 30

# Sınıflar
tc class add dev $DEV parent 1: classid 1:1 htb rate 1gbit burst 15k
tc class add dev $DEV parent 1:1 classid 1:10 htb rate 600mbit ceil 900mbit burst 15k prio 1
tc class add dev $DEV parent 1:1 classid 1:20 htb rate 200mbit ceil 400mbit burst 15k prio 2
tc class add dev $DEV parent 1:1 classid 1:30 htb rate 50mbit ceil 150mbit burst 15k prio 3

# Yaprak qdisc'ler
tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev $DEV parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev $DEV parent 1:30 handle 30: sfq perturb 10

# Filtreler
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 
    match ip dport 80 0xffff flowid 1:10
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 
    match ip dport 443 0xffff flowid 1:10
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 
    match ip dport 5432 0xffff flowid 1:20

echo "TC rules applied successfully"
chmod +x /usr/local/sbin/tc-setup.sh

# Systemd unit dosyası
cat > /etc/systemd/system/tc-rules.service << 'EOF'
[Unit]
Description=Traffic Control Rules
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/tc-setup.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now tc-rules.service

Sık Yapılan Hatalar

Yıllar içinde gördüğüm yaygın hatalardan bahsetmek istiyorum:

Burst değerini çok düşük tutmak: burst değeri rate / 8 / 1000 bayttan düşük olmamalı. Örneğin 100 Mbit/s için en az 12500 byte (12.5k) burst gerekir. Düşük tutarsanız CPU interrupt’ları artar ve beklenmedik paket kayıpları yaşarsınız.

Default sınıfı unutmak: HTB’de default parametresini belirtmezseniz ya da yanlış bir class ID verirseniz, filtreyle eşleşmeyen tüm trafik root’a düşer ve şekillendirme dışında kalır. Her zaman bir default class tanımlayın.

Filtrelerin sırasına dikkat etmemek: prio değerleri filtre önceliğini belirler. Daha spesifik kuralları daha düşük prio değeriyle (yani daha yüksek öncelikle) tanımlayın.

Ingress sınırlamasını göz ardı etmek: Sadece egress’i sınırlandırmak çoğu durumda yeterli değildir, özellikle simetrik bant genişliğiniz varsa. IFB yöntemini göz ardı etmeyin.

Sonuç

tc ve iproute2’nin trafik şekillendirme kabiliyetleri son derece güçlü ama öğrenme eğrisi dik. Burada anlattıklarım bu dünyanın yalnızca bir bölümü. HFSC, CAKE, FQ-CoDel gibi daha gelişmiş qdisc’ler var, eBPF ile entegrasyon var, tüm bunlar ayrı birer yazı konusu.

Pratik tavsiyem: Önce TBF ile basit sınırlandırmayı deneyin, sonra HTB’ye geçin. Üretim ortamına uygulamadan önce test ortamında netem ile gecikme ve paket kaybı simüle edin, filterlarınızın doğru sınıfı yakaladığını doğrulayın. tc -s istatistiklerini düzenli takip etmeyi alışkanlık haline getirin.

Trafik şekillendirme doğru yapıldığında kritik servislerin yoğun saatlerde bile stabil kalmasını sağlar, “gece backup’ı ağı öldürdü” gibi sabah mesajlarından kurtarır ve çok kiracılı ortamlarda adaletli kaynak dağılımı sunar. Zahmetine değer.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir