Шейпинг скорости на iproute2 с использованием hash-таблиц(без использования Manad)
Материал из BiTel WiKi
Статья написана пока еще с не совсем сложившимся пониманием как это работает,поэтому могут быть неточности .
С ростом количества правил шейпинга в iptoute2 уже нельзя использовать линейную структуру фильтрации, когда все фильтры( по одному или несколько на каждого абонента) добавляются на одном уровне . В худшем случае при поиске правила шейпинга система будет проверять все фильтры каждый раз. Поэтому надо использовать hash-таблицы. Создадим 4 таблицы по 256 ячеек, каждая из таблиц будет оценивать один из октетов ип адреса. Вначале анализируется первый октет и на основе него выбирается какую таблицу дальше анализировать. Далее второй октет, третий и 4-ый. Количество переборов значительно уменьшается.
Данный пример сделан без использования Manad. Номера классов шейпинга делают на основе ip-адреса.
Пусть у нас есть внешние сети такого вида xxx.yyy.0.0/16. xxx и yyy обычные 10-ные числа обозначающие часть ip-адреса.
Пусть xxx - это XX в 16-ом системе, а yyy - это YY в 16-ой системе.
Тут делаем определенную структуру фильтров. Для 3-го октета мы назначаем handle равный этому 3-ме октету. И вы выстраиваем все правила до 3-го октета:
#ROOT tc qdisc del dev p514p1 handle ffff: ingress 2> /dev/null tc qdisc del dev p514p1 root tc qdisc del dev p514p1 root 2> /dev/null tc qdisc add dev p514p1 root handle 1: htb r2q 15000 tc class add dev p514p1 classid 1:0 htb rate 2460Mbit ### tc filter add dev p514p1 parent 1:0 prio 5 protocol ip u32 tc filter add dev p514p1 parent 1:0 prio 5 handle 10: protocol ip u32 divisor 256 tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht 800:: match ip dst 0.0.0.0/0 hashkey mask 0xff000000 at 16 link 10: ## entries for xxx.0.0.0/8 tc filter add dev p514p1 parent 1:0 prio 5 handle 11: protocol ip u32 divisor 256 tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht 10:XX: match ip dst 0.0.0.0/0 hashkey mask 0xff0000 at 16 link 11: ## entries for xxx.yyy.0.0/16 tc filter add dev p514p1 parent 1:0 prio 5 handle 12: protocol ip u32 divisor 256 tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht 11:YY: match ip dst 0.0.0.0/0 hashkey mask 0xff00 at 16 link 12: ## entries for xxx.yyy.161.0/24 tc filter add dev p514p1 parent 1:0 prio 5 handle a1: protocol ip u32 divisor 256 tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht 12:a1: match ip dst 0.0.0.0/0 hashkey mask 0xff at 16 link a1: ## entries for xxx.yyy.162.0/24 tc filter add dev p514p1 parent 1:0 prio 5 handle a2: protocol ip u32 divisor 256 tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht 12:a2: match ip dst 0.0.0.0/0 hashkey mask 0xff at 16 link a2: .......
Далее пусть у нас есть какой-то конкретный адрес для абонента xxx.yyy.161.155. Билинг( в данном случае access-сервер) должен добавить для него фильтры c handle равным последним октетам( 161.155). 161 - это A1 в 16-ной, 155 - 9B в 16-ной.
Команды добавления скорости от биллинга отправляется такие:
sudo tc filter add dev p514p1 protocol ip parent 1:0 prio 5 u32 ht A1:9B: match ip dst 0.0.0.0/0 flowid 1:A19B sudo tc class add dev p514p1 parent 1:0 classid 1:0xA19B htb rate 4096Kbit ceil 20480Kbit quantum 2000 mtu 3000 burst 15k
Команды удаления скорости от биллинга отправляется такие:
sudo tc filter delete dev p514p1 parent 1:0 protocol ip prio 5 handle $A1:9B:800 u32 sudo tc class delete dev p514p1 parent 1:0 classid 1:0xA19B htb rate 4096Kbit ceil 20480Kbit quantum 2000 mtu 3000 burst 15k
Для этого мы забиваем в конфигурации такие команды:
speedRate=$param($option,speedRate,100) speedCeil=$param($option,speedCeil,100) sa.endSequence=$ sa.command.common.ancestor.inetOption.11.enable.1=sudo tc filter add dev $param(iface) protocol ip parent 1:0 prio 5 u32 ht $ip3:$ip4: match ip dst 0.0.0.0/0 flowid 1:$ip34 sa.command.common.ancestor.inetOption.11.enable.2=sudo tc class add dev $param(iface) parent 1:0 classid 1:0x$ip34 htb rate {@speedRate} ceil {@speedCeil} quantum 2000 mtu 3000 burst 15k sa.command.common.ancestor.inetOption.11.disable.1=sudo tc filter delete dev $param(iface) parent 1:0 protocol ip prio 5 handle $ip3:$ip4:800 u32 sa.command.common.ancestor.inetOption.11.disable.2=sudo tc class delete dev $param(iface) parent 1:0 classid 1:0x$ip34 htb rate {@speedRate} ceil {@speedCeil} quantum 2000 mtu 3000 burst 15k
тут макросы $ip3 - 3-тий октет, $ip4 - 4-ый. $ip34 - 3 и 4 октеты слепленные вместе.
Данный команды рассчитаны на такое дерево в котором, все опции изменения скорости находятся в дереве опции ниже опции 11:
В конфигурации самих опции заносим значения скоростей:
speedRate=4096Kbit speedCeil=20480Kbit
Чтобы эти команды отправить на устройство мы используем SSHServiceActivator, но для поддержки специфичных макросов мы наследуется от него и немного добавляем код:
package ru.bitel.bgbilling.custom.inet.dyn.device.terminal; import ru.bitel.bgbilling.modules.inet.access.sa.ServiceActivator; import ru.bitel.bgbilling.modules.inet.access.sa.ServiceActivatorAdapter; import ru.bitel.bgbilling.modules.inet.access.sa.ServiceActivatorEvent; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDevice; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetDeviceType; import ru.bitel.bgbilling.server.util.Setup; import ru.bitel.common.ParameterMap; import ru.bitel.bgbilling.modules.inet.dyn.device.terminal.SSHServiceActivator; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetServ; import ru.bitel.bgbilling.modules.inet.api.common.bean.InetConnection; import java.util.Set; import ru.bitel.common.Utils; import org.apache.log4j.Logger; import java.util.concurrent.TimeUnit; import bitel.billing.server.util.ssh.SSHSession; public class SSHLinuxServiceActivator extends SSHServiceActivator implements ServiceActivator { private static final Logger logger = Logger.getLogger( SSHLinuxServiceActivator.class ); @Override protected Object getValue( ServiceActivatorEvent e, InetServ serv, InetConnection connection, Set<Integer> options, String macros, Object[] args, Object[] globalArgs ) throws Exception { if( "ip34".equals( macros ) ) { String hex = Utils.bytesToHexString( serv.getAddressFrom() ); return hex.substring(4,8); } if( "ip3".equals( macros ) ) { String hex = Utils.bytesToHexString( serv.getAddressFrom() ); return hex.substring(4,6); } if( "ip4".equals( macros ) ) { String hex = Utils.bytesToHexString( serv.getAddressFrom() ); return hex.substring(6,8); } else { return super.getValue( e, serv, connection, options, macros, args, globalArgs ); } } }