diff --git a/opentrafficshaper/plugins/tc/tc.pm b/opentrafficshaper/plugins/tc/tc.pm index 44e128e8fb7a73ba3b014db10ae7d125d0909270..21de0ca07b366e33aac9bf82f304a70eed226dea 100644 --- a/opentrafficshaper/plugins/tc/tc.pm +++ b/opentrafficshaper/plugins/tc/tc.pm @@ -1,5 +1,5 @@ # OpenTrafficShaper Linux tc traffic shaping -# Copyright (C) 2007-2013, AllWorldIT +# Copyright (C) 2007-2014, AllWorldIT # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,15 +29,31 @@ use opentrafficshaper::logger; use opentrafficshaper::utils; use opentrafficshaper::plugins::configmanager qw( - getLimit getLimitAttribute setLimitAttribute removeLimitAttribute - getLimitTxInterface getLimitRxInterface getLimitMatchPriority - - getTrafficPriority - - getShaperState setShaperState - getInterfaces getInterfaceRate getInterfaceClasses getInterfaceDefaultPool - - isTrafficClassValid + getPool + getPoolAttribute + setPoolAttribute + removePoolAttribute + getPoolTxInterface + getPoolRxInterface + getPoolShaperState + setPoolShaperState + + getEffectivePool + + getPoolMember + setPoolMemberAttribute + getPoolMemberAttribute + removePoolMemberAttribute + getPoolMemberMatchPriority + setPoolMemberShaperState + getPoolMemberShaperState + + getTrafficClassPriority + + getInterfaces + getInterfaceRate + getInterfaceTrafficClasses + getInterfaceDefaultPool ); @@ -51,24 +67,17 @@ our (@ISA,@EXPORT,@EXPORT_OK); ); use constant { - VERSION => '0.0.2', + VERSION => '0.1.2', # 5% of a link can be used for very high priority traffic PROTO_RATE_LIMIT => 5, - PROTO_RATE_BURST_MIN => 16, # With a minimum burst of 8KiB - PROTO_RATE_BURST_MAXM => 1.5, # Multiplier for burst min to get to burst max + PROTO_RATE_BURST_MIN => 16, # With a minimum burst of 8KiB + PROTO_RATE_BURST_MAXM => 1.5, # Multiplier for burst min to get to burst max # High priority traffic gets the first 20% of the bandidth to itself PRIO_RATE_LIMIT => 20, - PRIO_RATE_BURST_MIN => 32, # With a minimum burst of 40KiB - PRIO_RATE_BURST_MAXM => 1.5, # Multiplier for burst min to get to burst max - - TC_CLASS_BASE => 10, - TC_CLASS_LIMIT_BASE => 100, - - TC_PRIO_BASE => 10, - - TC_FILTER_LIMIT_BASE => 100, + PRIO_RATE_BURST_MIN => 32, # With a minimum burst of 40KiB + PRIO_RATE_BURST_MAXM => 1.5, # Multiplier for burst min to get to burst max TC_ROOT_CLASS => 1, }; @@ -112,16 +121,16 @@ sub plugin_init # Setup our environment $logger = $globals->{'logger'}; - $logger->log(LOG_NOTICE,"[TC] OpenTrafficShaper tc Integration v".VERSION." - Copyright (c) 2013, AllWorldIT"); + $logger->log(LOG_NOTICE,"[TC] OpenTrafficShaper tc Integration v%s - Copyright (c) 2007-2014, AllWorldIT",VERSION); # Grab some of our config we need if (defined(my $proto = $globals->{'file.config'}->{'plugin.tc'}->{'protocol'})) { - $logger->log(LOG_INFO,"[TC] Set protocol to '$proto'"); + $logger->log(LOG_INFO,"[TC] Set protocol to '%s'",$proto); $config->{'ip_protocol'} = $proto; } if (defined(my $offset = $globals->{'file.config'}->{'plugin.tc'}->{'iphdr_offset'})) { - $logger->log(LOG_INFO,"[TC] Set IP header offset to '$offset'"); + $logger->log(LOG_INFO,"[TC] Set IP header offset to '%s'",$offset); $config->{'iphdr_offset'} = $offset; } @@ -131,7 +140,7 @@ sub plugin_init # Loop with the configured interfaces and initialize them foreach my $interface (@{getInterfaces()}) { # Initialize interface - $logger->log(LOG_INFO,"[TC] Queuing tasks to initialize '$interface'"); + $logger->log(LOG_INFO,"[TC] Queuing tasks to initialize '%s'",$interface); _tc_iface_init($changeSet,$interface); } _task_add_to_queue($changeSet); @@ -140,33 +149,37 @@ sub plugin_init # This session is our main session, its alias is "shaper" POE::Session->create( inline_states => { - _start => \&session_start, - _stop => \&session_stop, + _start => \&_session_start, + _stop => \&_session_stop, + + pool_add => \&_session_pool_add, + pool_remove => \&_session_pool_remove, + pool_change => \&_session_pool_change, - add => \&do_add, - change => \&do_change, - remove => \&do_remove, + poolmember_add => \&_session_poolmember_add, + poolmember_remove => \&_session_poolmember_remove, } ); # This is our session for communicating directly with tc, its alias is _tc POE::Session->create( inline_states => { - _start => \&task_session_start, + _start => \&_task_session_start, _stop => sub { }, + # Signals + _SIGCHLD => \&_task_SIGCHLD, + _SIGINT => \&_task_SIGINT, # Public'ish - queue => \&task_add, + queue => \&_task_queue, + # Internal - task_child_stdout => \&task_child_stdout, - task_child_stderr => \&task_child_stderr, - task_child_stdin => \&task_child_stdin, - task_child_close => \&task_child_close, - task_child_error => \&task_child_error, - task_run_next => \&task_run_next, - # Signals - handle_SIGCHLD => \&task_handle_SIGCHLD, - handle_SIGINT => \&task_handle_SIGINT, + _task_child_stdout => \&_task_child_stdout, + _task_child_stderr => \&_task_child_stderr, + _task_child_stdin => \&_task_child_stdin, + _task_child_close => \&_task_child_close, + _task_child_error => \&_task_child_error, + _task_run_next => \&_task_run_next, } ); @@ -182,7 +195,7 @@ sub plugin_start # Initialize this plugins main POE session -sub session_start +sub _session_start { my ($kernel,$heap) = @_[KERNEL,HEAP]; @@ -195,7 +208,7 @@ sub session_start # Initialize this plugins main POE session -sub session_stop +sub _session_stop { my ($kernel,$heap) = @_[KERNEL,HEAP]; @@ -215,288 +228,468 @@ sub session_stop } -# Add event for tc -sub do_add +# Event handler for adding a pool +sub _session_pool_add +{ + my ($kernel,$heap,$pid) = @_[KERNEL, HEAP, ARG0]; + + + # Grab pool + my $pool; + if (!defined($pool = getPool($pid))) { + $logger->log(LOG_ERR,"[TC] Shaper 'remove' event with non existing pool '%s'",$pid); + return; + } + + $logger->log(LOG_INFO,"[TC] Add pool '%s' to interface group '%s' [%s]", + $pool->{'Identifier'}, + $pool->{'InterfaceGroupID'}, + $pool->{'ID'} + ); + + # Grab our effective pool + my $effectivePool = getEffectivePool($pool->{'ID'}); + + my $changeSet = TC::ChangeSet->new(); + + # Grab some things we need from the main pool + my $txInterface = getPoolTxInterface($pool->{'ID'}); + my $rxInterface = getPoolRxInterface($pool->{'ID'}); + # Grab effective config + my $classID = $effectivePool->{'ClassID'}; + my $trafficLimitTx = $effectivePool->{'TrafficLimitTx'}; + my $trafficLimitTxBurst = $effectivePool->{'TrafficLimitTxBurst'}; + my $trafficLimitRx = $effectivePool->{'TrafficLimitRx'}; + my $trafficLimitRxBurst = $effectivePool->{'TrafficLimitRxBurst'}; + my $trafficPriority = getTrafficClassPriority($effectivePool->{'ClassID'}); + + # Get the Tx traffic classes TC class + my $tcClass_TxTrafficClass = _getTcClassFromTrafficClassID($txInterface,$classID); + # Generate our pools Tx TC class + my $tcClass_TxPool = _reserveTcClassByPoolID($txInterface,$pool->{'ID'}); + # Add the main Tx TC class for this pool + _tc_class_add($changeSet,$txInterface,TC_ROOT_CLASS,$tcClass_TxTrafficClass,$tcClass_TxPool,$trafficLimitTx, + $trafficLimitTxBurst,$trafficPriority + ); + # Add Tx TC optimizations + _tc_class_optimize($changeSet,$txInterface,$tcClass_TxPool,$trafficLimitTx); + # Set Tx TC class + setPoolAttribute($pool->{'ID'},'tc.txclass',$tcClass_TxPool); + + # Get the Rx traffic classes TC class + my $tcClass_RxTrafficClass = _getTcClassFromTrafficClassID($rxInterface,$classID); + # Generate our pools Rx TC class + my $tcClass_RxPool = _reserveTcClassByPoolID($rxInterface,$pool->{'ID'}); + # Add the main Rx TC class for this pool + _tc_class_add($changeSet,$rxInterface,TC_ROOT_CLASS,$tcClass_RxTrafficClass,$tcClass_RxPool,$trafficLimitRx, + $trafficLimitRxBurst,$trafficPriority + ); + # Add Rx TC optimizations + _tc_class_optimize($changeSet,$rxInterface,$tcClass_RxPool,$trafficLimitRx); + # Set Rx TC + setPoolAttribute($pool->{'ID'},'tc.rxclass',$tcClass_RxPool); + + # Post changeset + $kernel->post("_tc" => "queue" => $changeSet); + + # Set current live values + setPoolAttribute($pool->{'ID'},'shaper.live.ClassID',$classID); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTx',$trafficLimitTx); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTxBurst',$trafficLimitTxBurst); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRx',$trafficLimitRx); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRxBurst',$trafficLimitRxBurst); + + # Mark as live + setPoolShaperState($pool->{'ID'},SHAPER_LIVE); +} + + +# Event handler for removing a pool +sub _session_pool_remove +{ + my ($kernel, $pid) = @_[KERNEL, ARG0]; + + + my $changeSet = TC::ChangeSet->new(); + + # Pull in pool + my $pool; + if (!defined($pool = getPool($pid))) { + $logger->log(LOG_ERR,"[TC] Shaper 'remove' event with non existing pool '%s'",$pid); + return; + } + + # Make sure its not NOTLIVE + if (getPoolShaperState($pid) == SHAPER_NOTLIVE) { + $logger->log(LOG_WARN,"[TC] Ignoring remove for pool '%s' [%s]", + $pool->{'Identifier'}, + $pool->{'ID'} + ); + return; + } + + $logger->log(LOG_INFO,"[TC] Removing pool '%s' [%s]", + $pool->{'Identifier'}, + $pool->{'ID'} + ); + + # Grab our interfaces + my $txInterface = getPoolTxInterface($pool->{'ID'}); + my $rxInterface = getPoolRxInterface($pool->{'ID'}); + # Grab the traffic class from the pool + my $txPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); + my $rxPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); + + # Grab current class ID + my $classID = getPoolAttribute($pool->{'ID'},'shaper.live.ClassID'); + # Grab our minor classes + my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterface,$classID); + my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterface,$classID); + + # Clear up the class + $changeSet->add([ + '/sbin/tc','class','del', + 'dev',$txInterface, + 'parent',"1:$txTrafficClassTcClass", + 'classid',"1:$txPoolTcClass", + ]); + $changeSet->add([ + '/sbin/tc','class','del', + 'dev',$rxInterface, + 'parent',"1:$rxTrafficClassTcClass", + 'classid',"1:$rxPoolTcClass", + ]); + + # And recycle the classs + _disposePoolTcClass($txInterface,$txPoolTcClass); + _disposePoolTcClass($rxInterface,$rxPoolTcClass); + + _disposePrioTcClass($txInterface,$txPoolTcClass); + _disposePrioTcClass($rxInterface,$rxPoolTcClass); + + # Post changeset + $kernel->post("_tc" => "queue" => $changeSet); + + # Mark as not live + setPoolShaperState($pool->{'ID'},SHAPER_NOTLIVE); + + # Cleanup attributes + removePoolAttribute($pool->{'ID'},'tc.txclass'); + removePoolAttribute($pool->{'ID'},'tc.rxclass'); + + removePoolAttribute($pool->{'ID'},'shaper.live.ClassID'); + removePoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTx'); + removePoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTxBurst'); + removePoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRx'); + removePoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRxBurst'); +} + + +## Event handler for changing a pool +sub _session_pool_change { - my ($kernel,$heap,$lid,$changes) = @_[KERNEL, HEAP, ARG0, ARG1]; + my ($kernel, $pid) = @_[KERNEL, ARG0, ARG1]; + + # Grab pool + my $pool = getPool($pid); + + $logger->log(LOG_INFO,"[TC] Processing changes for '%s' [%s]",$pool->{'Identifier'},$pool->{'ID'}); + + # Grab our effective pool + my $effectivePool = getEffectivePool($pool->{'ID'}); + + # Grab our interfaces + my $txInterface = getPoolTxInterface($pool->{'ID'}); + my $rxInterface = getPoolRxInterface($pool->{'ID'}); + # Grab the traffic class from the pool + my $txPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); + my $rxPoolTcClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); + + # Grab effective config + my $classID = $effectivePool->{'ClassID'}; + my $trafficLimitTx = $effectivePool->{'TrafficLimitTx'}; + my $trafficLimitTxBurst = $effectivePool->{'TrafficLimitTxBurst'}; + my $trafficLimitRx = $effectivePool->{'TrafficLimitRx'}; + my $trafficLimitRxBurst = $effectivePool->{'TrafficLimitRxBurst'}; + my $trafficPriority = getTrafficClassPriority($classID); + + # Grab our minor classes + my $txTrafficClassTcClass = _getTcClassFromTrafficClassID($txInterface,$classID); + my $rxTrafficClassTcClass = _getTcClassFromTrafficClassID($rxInterface,$classID); + + # Generate changeset my $changeSet = TC::ChangeSet->new(); + _tc_class_change($changeSet,$txInterface,TC_ROOT_CLASS,$txTrafficClassTcClass,$txPoolTcClass,$trafficLimitTx, + $trafficLimitTxBurst,$trafficPriority); + _tc_class_change($changeSet,$rxInterface,TC_ROOT_CLASS,$rxTrafficClassTcClass,$rxPoolTcClass,$trafficLimitRx, + $trafficLimitRxBurst,$trafficPriority); + + # Post changeset + $kernel->post("_tc" => "queue" => $changeSet); - # Pull in limit - my $limit; - if (!defined($limit = getLimit($lid))) { - $logger->log(LOG_ERR,"[TC] Shaper 'add' event with non existing limit '$lid'"); + setPoolAttribute($pool->{'ID'},'shaper.live.ClassID',$classID); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTx',$trafficLimitTx); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitTxBurst',$trafficLimitTxBurst); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRx',$trafficLimitRx); + setPoolAttribute($pool->{'ID'},'shaper.live.TrafficLimitRxBurst',$trafficLimitRxBurst); + # Mark as live + setPoolShaperState($pool->{'ID'},SHAPER_LIVE); +} + + +# Event handler for adding a pool member +sub _session_poolmember_add +{ + my ($kernel,$heap,$pmid) = @_[KERNEL, HEAP, ARG0]; + + + # Grab pool + my $poolMember; + if (!defined($poolMember = getPoolMember($pmid))) { + $logger->log(LOG_ERR,"[TC] Shaper 'add' event with non existing pool member '%s'",$pmid); return; } - $logger->log(LOG_INFO,"[TC] Add '$limit->{'Username'}' [$lid]"); + + $logger->log(LOG_INFO,"[TC] Add pool member '%s' to pool '%s' [%s]", + $poolMember->{'IPAddress'}, + $poolMember->{'PoolID'}, + $poolMember->{'ID'} + ); + + my $changeSet = TC::ChangeSet->new(); # Filter levels for the IP components - my @components = split(/\./,$limit->{'IP'}); + my @components = split(/\./,$poolMember->{'IPAddress'}); my $ip1 = $components[0]; my $ip2 = $components[1]; my $ip3 = $components[2]; my $ip4 = $components[3]; + + my $pool; + if (!defined($pool = getPool($poolMember->{'PoolID'}))) { + $logger->log(LOG_ERR,"[TC] Shaper 'poolmember_add' event with invalid PoolID"); + return; + } + # Grab some variables we going to need below - my $txInterface = getLimitTxInterface($lid); - my $rxInterface = getLimitRxInterface($lid); - my $matchPriority = getLimitMatchPriority($lid); - my $trafficPriority = getTrafficPriority($limit->{'ClassID'}); + my $txInterface = getPoolTxInterface($pool->{'ID'}); + my $rxInterface = getPoolRxInterface($pool->{'ID'}); + my $trafficPriority = getTrafficClassPriority($pool->{'ClassID'}); + my $matchPriority = getPoolMemberMatchPriority($pool->{'ID'}); # Check if we have a entry for the /8, if not we must create our 2nd level hash table and link it if (!defined($tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1})) { # Grab filter ID's for 2nd level - my $txFilterID = _reserveTcFilter($txInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'} = $txFilterID; - $logger->log(LOG_DEBUG,"[TC] Linking 2nd level TX hash table to '$txFilterID' to '$ip1.0.0.0/8', priority '$matchPriority'"); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$txFilterID,$config->{'ip_protocol'},800,"","$ip1.0.0.0/8","00ff0000"); + $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; + $logger->log(LOG_DEBUG,"[TC] Linking 2nd level TX hash table to '%s' to '%s.0.0.0/8', priority '%s'", + $filterID, + $ip1, + $matchPriority + ); + _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", + "$ip1.0.0.0/8","00ff0000"); } if (!defined($tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1})) { # Grab filter ID's for 2nd level - my $rxFilterID = _reserveTcFilter($rxInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'} = $rxFilterID; - $logger->log(LOG_DEBUG,"[TC] Linking 2nd level RX hash table to '$rxFilterID' to '$ip1.0.0.0/8', priority '$matchPriority'"); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$rxFilterID,$config->{'ip_protocol'},800,"","$ip1.0.0.0/8","00ff0000"); + $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'} = $filterID; + $logger->log(LOG_DEBUG,"[TC] Linking 2nd level RX hash table to '%s' to '%s.0.0.0/8', priority '%s'", + $filterID, + $ip1, + $matchPriority + ); + _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},800,"", + "$ip1.0.0.0/8","00ff0000"); } # Check if we have our /16 hash entry, if not we must create the 3rd level hash table if (!defined($tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2})) { # Grab filter ID's for 3rd level - my $txFilterID = _reserveTcFilter($txInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $txFilterID; + $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; # Grab some hash table ID's we need - my $txIP1HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'}; + my $ip1HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{'id'}; # And hex our IP component my $ip2Hex = toHex($ip2); - $logger->log(LOG_DEBUG,"[TC] Linking 3rd level TX hash table to '$txFilterID' to '$ip1.$ip2.0.0/16', priority '$matchPriority'"); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$txFilterID,$config->{'ip_protocol'},$txIP1HtHex,$ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); + $logger->log(LOG_DEBUG,"[TC] Linking 3rd level TX hash table to '%s' to '%s.%s.0.0/16', priority '%s'", + $filterID, + $ip1, + $ip2, + $matchPriority + ); + _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, + $ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); } if (!defined($tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2})) { # Grab filter ID's for 3rd level - my $rxFilterID = _reserveTcFilter($rxInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $rxFilterID; + $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'} = $filterID; # Grab some hash table ID's we need - my $rxIP1HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'}; + my $ip1HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{'id'}; # And hex our IP component my $ip2Hex = toHex($ip2); - $logger->log(LOG_DEBUG,"[TC] Linking 3rd level RX hash table to '$rxFilterID' to '$ip1.$ip2.0.0/16', priority '$matchPriority'"); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$rxFilterID,$config->{'ip_protocol'},$rxIP1HtHex,$ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); + $logger->log(LOG_DEBUG,"[TC] Linking 3rd level RX hash table to '%s' to '%s.%s.0.0/16', priority '%s'", + $filterID, + $ip1, + $ip2, + $matchPriority + ); + _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip1HtHex, + $ip2Hex,"$ip1.$ip2.0.0/16","0000ff00"); } # Check if we have our /24 hash entry, if not we must create the 4th level hash table if (!defined($tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { # Grab filter ID's for 4th level - my $txFilterID = _reserveTcFilter($txInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($txInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $txFilterID; + $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; # Grab some hash table ID's we need - my $txIP2HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; + my $ip2HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; # And hex our IP component my $ip3Hex = toHex($ip3); - $logger->log(LOG_DEBUG,"[TC] Linking 4th level TX hash table to '$txFilterID' to '$ip1.$ip2.$ip3.0/24', priority '$matchPriority'"); - _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$txFilterID,$config->{'ip_protocol'},$txIP2HtHex,$ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); + $logger->log(LOG_DEBUG,"[TC] Linking 4th level TX hash table to '%s' to '%s.%s.%s.0/24', priority '%s'", + $filterID, + $ip1, + $ip2, + $ip3, + $matchPriority + ); + _tc_filter_add_dstlink($changeSet,$txInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, + $ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); } if (!defined($tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3})) { # Grab filter ID's for 4th level - my $rxFilterID = _reserveTcFilter($rxInterface,$matchPriority,$lid); + my $filterID = _reserveTcFilter($rxInterface,$matchPriority,$pool->{'ID'}); # Track our mapping - $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $rxFilterID; + $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'} = $filterID; # Grab some hash table ID's we need - my $rxIP2HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; + my $ip2HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{'id'}; # And hex our IP component my $ip3Hex = toHex($ip3); - $logger->log(LOG_DEBUG,"[TC] Linking 4th level RX hash table to '$rxFilterID' to '$ip1.$ip2.$ip3.0/24', priority '$matchPriority'"); - _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$rxFilterID,$config->{'ip_protocol'},$rxIP2HtHex,$ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); + $logger->log(LOG_DEBUG,"[TC] Linking 4th level RX hash table to '%s' to '%s.%s.%s.0/24', priority '%s'", + $filterID, + $ip1, + $ip2, + $ip3, + $matchPriority + ); + _tc_filter_add_srclink($changeSet,$rxInterface,TC_ROOT_CLASS,$matchPriority,$filterID,$config->{'ip_protocol'},$ip2HtHex, + $ip3Hex,"$ip1.$ip2.$ip3.0/24","000000ff"); } + # + # For sake of simplicity and so things loook all nice and similar, we going to do these 2 blocks in { } + # + # Only if we have TX limits setup process them - if (defined($changes->{'TrafficLimitTx'})) { - # Generate our limit TC class - my $txLimitTcClass = _reserveTcClassByLimitID($txInterface,$lid); - # Get traffic class TC class - my $classID = $changes->{'ClassID'}; - my $txTrafficClassTcClass = _getTcClassFromClassID($txInterface,$classID); + { + # Get the TX class + my $tcClass_trafficClass = getPoolAttribute($pool->{'ID'},'tc.txclass'); # Grab some hash table ID's we need - my $txIP3HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; + my $ip3HtHex = $tcFilterMappings->{$txInterface}->{'dst'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; # And hex our IP component my $ip4Hex = toHex($ip4); - $logger->log(LOG_DEBUG,"[TC] Linking TX IP '$limit->{'IP'}' to class '$txTrafficClassTcClass' at hash endpoint '$txIP3HtHex:$ip4Hex'"); - # Add shaping classes - _tc_class_add($changeSet,$txInterface,TC_ROOT_CLASS,$txTrafficClassTcClass,$txLimitTcClass,$changes->{'TrafficLimitTx'},$changes->{'TrafficLimitTxBurst'},$trafficPriority); + $logger->log(LOG_DEBUG,"[TC] Linking pool member IP '%s' to class '%s' at hash endpoint '%s:%s'", + $poolMember->{'IPAddress'}, + $tcClass_trafficClass, + $ip3HtHex, + $ip4Hex + ); + # Link filter to traffic flow (class) - _tc_filter_add_flowlink($changeSet,$txInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$txIP3HtHex,$ip4Hex,"dst",16,$limit->{'IP'},$txLimitTcClass); - # Add optimizations - _tc_class_optimize($changeSet,$txInterface,$txLimitTcClass,$changes->{'TrafficLimitTx'}); - # Save limit tc class ID - setLimitAttribute($lid,'tc.txclass',$txLimitTcClass); - setLimitAttribute($lid,'tc.txfilter',"${txIP3HtHex}:${ip4Hex}:1"); - # Set current live values - setLimitAttribute($lid,'tc.live.TrafficLimitTx',$changes->{'TrafficLimitTx'}); - setLimitAttribute($lid,'tc.live.TrafficLimitTxBurst',$changes->{'TrafficLimitTxBurst'}); - } + _tc_filter_add_flowlink($changeSet,$txInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex,$ip4Hex, + "dst",16,$poolMember->{'IPAddress'},$tcClass_trafficClass); + # Save pool member filter ID + setPoolMemberAttribute($poolMember->{'ID'},'tc.txfilter',"${ip3HtHex}:${ip4Hex}:1"); + } # Only if we have RX limits setup process them - if (defined($changes->{'TrafficLimitRx'})) { + { # Generate our limit TC class - my $rxLimitTcClass = _reserveTcClassByLimitID($rxInterface,$lid); - # Get traffic class TC class - my $classID = $changes->{'ClassID'}; - my $rxTrafficClassTcClass = _getTcClassFromClassID($rxInterface,$classID); + my $tcClass_trafficClass = getPoolAttribute($pool->{'ID'},'tc.rxclass'); # Grab some hash table ID's we need - my $rxIP3HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; + my $ip3HtHex = $tcFilterMappings->{$rxInterface}->{'src'}->{$matchPriority}->{$ip1}->{$ip2}->{$ip3}->{'id'}; # And hex our IP component my $ip4Hex = toHex($ip4); - $logger->log(LOG_DEBUG,"[TC] Linking RX IP '$limit->{'IP'}' to class '$rxTrafficClassTcClass' at hash endpoint '$rxIP3HtHex:$ip4Hex'"); - # Add shaping classes - _tc_class_add($changeSet,$rxInterface,TC_ROOT_CLASS,$rxTrafficClassTcClass,$rxLimitTcClass,$changes->{'TrafficLimitRx'},$changes->{'TrafficLimitRxBurst'},$trafficPriority); + $logger->log(LOG_DEBUG,"[TC] Linking RX IP '%s' to class '%s' at hash endpoint '%s:%s'", + $poolMember->{'IPAddress'}, + $tcClass_trafficClass, + $ip3HtHex, + $ip4Hex + ); + # Link filter to traffic flow (class) - _tc_filter_add_flowlink($changeSet,$rxInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$rxIP3HtHex,$ip4Hex,"src",12,$limit->{'IP'},$rxLimitTcClass); - # Add optimizations - _tc_class_optimize($changeSet,$rxInterface,$rxLimitTcClass,$changes->{'TrafficLimitRx'}); - # Save limit tc class ID - setLimitAttribute($lid,'tc.rxclass',$rxLimitTcClass); - setLimitAttribute($lid,'tc.rxfilter',"${rxIP3HtHex}:${ip4Hex}:1"); - # Set current live values - setLimitAttribute($lid,'tc.live.TrafficLimitRx',$changes->{'TrafficLimitRx'}); - setLimitAttribute($lid,'tc.live.TrafficLimitRxBurst',$changes->{'TrafficLimitRxBurst'}); - } + _tc_filter_add_flowlink($changeSet,$rxInterface,TC_ROOT_CLASS,$trafficPriority,$config->{'ip_protocol'},$ip3HtHex,$ip4Hex, + "src",12,$poolMember->{'IPAddress'},$tcClass_trafficClass); - setLimitAttribute($lid,'tc.live.ClassID',$changes->{'ClassID'}); + # Save pool member filter ID + setPoolMemberAttribute($poolMember->{'ID'},'tc.rxfilter',"${ip3HtHex}:${ip4Hex}:1"); + } # Post changeset $kernel->post("_tc" => "queue" => $changeSet); - # Mark as live - setShaperState($lid,SHAPER_LIVE); + # Mark pool member as live + setPoolMemberShaperState($poolMember->{'ID'},SHAPER_LIVE); } -# Change event for tc -sub do_change +# Event handler for removing a pool member +sub _session_poolmember_remove { - my ($kernel, $lid, $changes) = @_[KERNEL, ARG0, ARG1]; - + my ($kernel, $pmid) = @_[KERNEL, ARG0]; - # Pull in limit - my $limit; - if (!defined($limit = getLimit($lid))) { - $logger->log(LOG_ERR,"[TC] Shaper 'change' event with non existing limit '$lid'"); + # Pull in pool member + my $poolMember; + if (!defined($poolMember = getPoolMember($pmid))) { + $logger->log(LOG_ERR,"[TC] Shaper 'remove' event with non existing pool member '%s'",$pmid); return; } - # Check if we don't have a changeset - if (!defined($changes)) { - $logger->log(LOG_WARN,"[TC] Shaper got a undefined changeset to process for '$lid'"); + # Grab the pool members associated pool + my $pool = getPool($poolMember->{'PoolID'}); + + # Make sure its not NOTLIVE + if (getPoolMemberShaperState($pmid) == SHAPER_NOTLIVE) { + $logger->log(LOG_WARN,"[TC] Ignoring remove for pool member '%s' with IP '%s' [%s] from pool '%s'", + $poolMember->{'Username'}, + $poolMember->{'IPAddress'}, + $poolMember->{'ID'}, + $pool->{'Identifier'} + ); return; } - $logger->log(LOG_INFO,"[TC] Processing changes for '$limit->{'Username'}' [$lid]"); - - # Pull in values we need - my $classID = getLimitAttribute($lid,'tc.live.ClassID'); - if (defined($changes->{'ClassID'}) && $changes->{'ClassID'} ne $classID) { - $classID = $changes->{'ClassID'}; - setLimitAttribute($lid,'tc.live.ClassID',$classID); - } - - my $trafficLimitTx; - my $trafficLimitTxBurst; - if (defined($changes->{'TrafficLimitTx'})) { - $trafficLimitTx = $changes->{'TrafficLimitTx'}; - setLimitAttribute($lid,'tc.live.TrafficLimitTx',$trafficLimitTx); - } else { - $trafficLimitTx = getLimitAttribute($lid,'tc.live.TrafficLimitTx'); - } - if (defined($changes->{'TrafficLimitTxBurst'})) { - $trafficLimitTxBurst = $changes->{'TrafficLimitTxBurst'}; - setLimitAttribute($lid,'tc.live.TrafficLimitTxBurst',$trafficLimitTxBurst); - } else { - $trafficLimitTxBurst = getLimitAttribute($lid,'tc.live.TrafficLimitTxBurst'); - } - - my $trafficLimitRx; - my $trafficLimitRxBurst; - if (defined($changes->{'TrafficLimitRx'})) { - $trafficLimitRx = $changes->{'TrafficLimitRx'}; - setLimitAttribute($lid,'tc.live.TrafficLimitRx',$trafficLimitRx); - } else { - $trafficLimitRx = getLimitAttribute($lid,'tc.live.TrafficLimitRx'); - } - if (defined($changes->{'TrafficLimitRxBurst'})) { - $trafficLimitRxBurst = $changes->{'TrafficLimitRxBurst'}; - setLimitAttribute($lid,'tc.live.TrafficLimitRxBurst',$trafficLimitRxBurst); - } else { - $trafficLimitRxBurst = getLimitAttribute($lid,'tc.live.TrafficLimitRxBurst'); - } + $logger->log(LOG_INFO,"[TC] Removing pool member '%s' with IP '%s' [%s] from pool '%s'", + $poolMember->{'Username'}, + $poolMember->{'IPAddress'}, + $poolMember->{'ID'}, + $pool->{'Identifier'} + ); # Grab our interfaces - my $txInterface = getLimitTxInterface($lid); - my $rxInterface = getLimitRxInterface($lid); - # Grab our classes - my $txLimitTcClass = getLimitAttribute($lid,'tc.txclass'); - my $rxLimitTcClass = getLimitAttribute($lid,'tc.rxclass'); - # Grab our minor classes - my $txTrafficClassTcClass = _getTcClassFromClassID($txInterface,$classID); - my $rxTrafficClassTcClass = _getTcClassFromClassID($rxInterface,$classID); - # Grab traffic priority - my $trafficPriority = getTrafficPriority($classID); - - # Generate changeset - my $changeSet = TC::ChangeSet->new(); - _tc_class_change($changeSet,$txInterface,TC_ROOT_CLASS,$txTrafficClassTcClass,$txLimitTcClass,$trafficLimitTx,$trafficLimitTxBurst,$trafficPriority); - _tc_class_change($changeSet,$rxInterface,TC_ROOT_CLASS,$rxTrafficClassTcClass,$rxLimitTcClass,$trafficLimitRx,$trafficLimitRxBurst,$trafficPriority); - - # Post changeset - $kernel->post("_tc" => "queue" => $changeSet); -} + my $txInterface = getPoolTxInterface($pool->{'ID'}); + my $rxInterface = getPoolRxInterface($pool->{'ID'}); + # Grab the filter ID's from the pool member which is linked to the traffic class + my $txFilter = getPoolMemberAttribute($poolMember->{'ID'},'tc.txfilter'); + my $rxFilter = getPoolMemberAttribute($poolMember->{'ID'},'tc.rxfilter'); + # Grab current class ID + my $classID = getPoolAttribute($pool->{'ID'},'shaper.live.ClassID'); + my $trafficPriority = getTrafficClassPriority($classID); -# Remove event for tc -sub do_remove -{ - my ($kernel, $lid) = @_[KERNEL, ARG0]; my $changeSet = TC::ChangeSet->new(); - - # Pull in limit - my $limit; - if (!defined($limit = getLimit($lid))) { - $logger->log(LOG_ERR,"[TC] Shaper 'change' event with non existing limit '$lid'"); - return; - } - - # Make sure its being shaped at present, it could be we have multiple removes queued? - if (getShaperState($lid) == SHAPER_NOTLIVE) { - $logger->log(LOG_INFO,"[TC] Ignoring duplicate remove for '$limit->{'Username'}' [$lid]"); - return; - } - - $logger->log(LOG_INFO,"[TC] Remove '$limit->{'Username'}' [$lid]"); - - # Grab our interfaces - my $txInterface = getLimitTxInterface($lid); - my $rxInterface = getLimitRxInterface($lid); - # Grab varaibles we need to make this happen - my $txLimitTcClass = getLimitAttribute($lid,'tc.txclass'); - my $rxLimitTcClass = getLimitAttribute($lid,'tc.rxclass'); - # Grab our filters - my $txFilter = getLimitAttribute($lid,'tc.txfilter'); - my $rxFilter = getLimitAttribute($lid,'tc.rxfilter'); - - # Grab current class ID - my $classID = getLimitAttribute($lid,'tc.live.ClassID'); - my $trafficPriority = getTrafficPriority($classID); - # Grab our minor classes - my $txTrafficClassTcClass = _getTcClassFromClassID($txInterface,$classID); - my $rxTrafficClassTcClass = _getTcClassFromClassID($rxInterface,$classID); - - # Clear up the filter $changeSet->add([ '/sbin/tc','filter','del', @@ -516,100 +709,69 @@ sub do_remove 'protocol',$config->{'ip_protocol'}, 'u32', ]); - # Clear up the class - $changeSet->add([ - '/sbin/tc','class','del', - 'dev',$txInterface, - 'parent',"1:$txTrafficClassTcClass", - 'classid',"1:$txLimitTcClass", - ]); - $changeSet->add([ - '/sbin/tc','class','del', - 'dev',$rxInterface, - 'parent',"1:$rxTrafficClassTcClass", - 'classid',"1:$rxLimitTcClass", - ]); - - # And recycle the classs - _disposeLimitTcClass($txInterface,$txLimitTcClass); - _disposeLimitTcClass($rxInterface,$rxLimitTcClass); - - _disposePrioTcClass($txInterface,$txLimitTcClass); - _disposePrioTcClass($rxInterface,$rxLimitTcClass); # Post changeset $kernel->post("_tc" => "queue" => $changeSet); # Mark as not live - setShaperState($lid,SHAPER_NOTLIVE); + setPoolMemberShaperState($poolMember->{'ID'},SHAPER_NOTLIVE); # Cleanup attributes - removeLimitAttribute($lid,'tc.txclass'); - removeLimitAttribute($lid,'tc.rxclass'); - removeLimitAttribute($lid,'tc.txfilter'); - removeLimitAttribute($lid,'tc.rxfilter'); -} - - -# Grab limit ID from TC class -sub getLIDFromTcLimitClass -{ - my ($interface,$tcLimitClass) = @_; - - return __getRefByMinorTcClass($interface,TC_ROOT_CLASS,$tcLimitClass); + removePoolMemberAttribute($poolMember->{'ID'},'tc.txfilter'); + removePoolMemberAttribute($poolMember->{'ID'},'tc.rxfilter'); } -# Function to return if this is linked to a system class or limit class -sub isTcLimitClass +# Grab pool ID from TC class +sub getPIDFromTcClass { my ($interface,$majorTcClass,$minorTcClass) = @_; - # Return the class ID if found - if (my $ref = __getRefByMinorTcClass($interface,$majorTcClass,$minorTcClass)) { - if (!($ref =~ /^_class_/)) { - return $minorTcClass; - } + # Return the pool ID if found + my $ref = __getRefByMinorTcClass($interface,$majorTcClass,$minorTcClass); + if (!defined($ref) || substr($ref,0,13) ne "_pool_class_:") { + return undef; } - return undef; + return substr($ref,13); } -# Function to return the traffic class ID if its valid -sub isTcTrafficClassValid +# Function to return if this is linked to a pool's class +sub isPoolTcClass { my ($interface,$majorTcClass,$minorTcClass) = @_; - # Return the class ID if found - if (__getRefByMinorTcClass($interface,$majorTcClass,$minorTcClass)) { - return $minorTcClass; + my $pid = getPIDFromTcClass($interface,$majorTcClass,$minorTcClass); + if (!defined($pid)) { + return undef; } - return undef; + return $minorTcClass; } -# Return the ClassID from a TC limit class + +# Return the ClassID from a TC class # This is similar to isTcTrafficClassValid() but returns the ref, not the minor class -sub getCIDFromTcLimitClass +sub getCIDFromTcClass { my ($interface,$majorTcClass,$minorTcClass) = @_; # Grab ref my $ref = __getRefByMinorTcClass($interface,$majorTcClass,$minorTcClass); - # Chop off _class: and return if we did - if (defined($ref) && $ref =~ s/^_class_://) { - return $ref; + # If we're not a traffic class, just return + if (substr($ref,0,16) ne "_traffic_class_:") { + return undef; } - return undef; + # Else return the part after the above tag + return substr($ref,16); } - # # Internal functions # @@ -624,7 +786,7 @@ sub _tc_iface_init # Grab our interface rate my $rate = getInterfaceRate($interface); # Grab interface class configuration - my $classes = getInterfaceClasses($interface); + my $trafficClasses = getInterfaceTrafficClasses($interface); # Clear the qdisc from the interface @@ -634,20 +796,20 @@ sub _tc_iface_init 'root', ]); - # Create our parent classes - foreach my $classID (sort {$a <=> $b} keys %{$classes}) { + # Reserve our parent TC classes + foreach my $classID (sort {$a <=> $b} keys %{$trafficClasses}) { # We don't really need the result, we just need the class created - _reserveTcClassByClassID($interface,$classID); + _reserveTcClassByTrafficClassID($interface,$classID); } # Do we have a default pool? if so we must direct traffic there my @qdiscOpts = ( ); my $defaultPool = getInterfaceDefaultPool($interface); - my $defaultPoolClass; + my $defaultPoolTcClass; if (defined($defaultPool)) { # Push unclassified traffic to this class - $defaultPoolClass = _getTcClassFromClassID($interface,$defaultPool); - push(@qdiscOpts,'default',$defaultPoolClass); + $defaultPoolTcClass = _getTcClassFromTrafficClassID($interface,$defaultPool); + push(@qdiscOpts,'default',$defaultPoolTcClass); } # Add root qdisc @@ -672,17 +834,17 @@ sub _tc_iface_init ]); # Setup the classes - while ((my $classID, my $class) = each(%{$classes})) { - my $trafficClassTcClass = _getTcClassFromClassID($interface,$classID); + while ((my $classID, my $class) = each(%{$trafficClasses})) { + my $tcClass = _getTcClassFromTrafficClassID($interface,$classID); - my $trafficPriority = getTrafficPriority($classID); + my $trafficPriority = getTrafficClassPriority($classID); # Add class $changeSet->add([ '/sbin/tc','class','add', 'dev',$interface, 'parent','1:1', - 'classid',"1:$trafficClassTcClass", + 'classid',"1:$tcClass", 'htb', 'rate',"$class->{'cir'}kbit", 'ceil',"$class->{'limit'}kbit", @@ -694,7 +856,7 @@ sub _tc_iface_init # Process our default pool traffic optimizations if (defined($defaultPool)) { # If we have a rate for this iface, then use it - _tc_class_optimize($changeSet,$interface,$defaultPoolClass,$classes->{$defaultPool}->{'limit'}); + _tc_class_optimize($changeSet,$interface,$defaultPoolTcClass,$trafficClasses->{$defaultPool}->{'limit'}); # Make the queue size big enough my $queueSize = ($rate * 1024) / 8; @@ -706,7 +868,7 @@ sub _tc_iface_init my $redBurst = int( ($redMin+$redMax) / (2*$redAvPkt)); my $redLimit = $queueSize; - my $prioTcClass = _getPrioTcClass($interface,$defaultPoolClass); + my $prioTcClass = _getPrioTcClass($interface,$defaultPoolTcClass); # Priority band my $prioBand = 1; @@ -714,7 +876,7 @@ sub _tc_iface_init '/sbin/tc','qdisc','add', 'dev',$interface, 'parent',"$prioTcClass:".toHex($prioBand), - 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolClass=>$prioBand").":", + 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolTcClass=>$prioBand").":", 'bfifo', 'limit',$queueSize, ]); @@ -724,7 +886,7 @@ sub _tc_iface_init '/sbin/tc','qdisc','add', 'dev',$interface, 'parent',"$prioTcClass:".toHex($prioBand), - 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolClass=>$prioBand").":", + 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolTcClass=>$prioBand").":", # TODO: NK - try enable the below # 'estimator','1sec','4sec', # Quick monitoring, every 1s with 4s constraint 'red', @@ -748,7 +910,7 @@ sub _tc_iface_init '/sbin/tc','qdisc','add', 'dev',$interface, 'parent',"$prioTcClass:".toHex($prioBand), - 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolClass=>$prioBand").":", + 'handle',_reserveMajorTcClass($interface,"_default_pool_:$defaultPoolTcClass=>$prioBand").":", 'red', 'min',$redMin, 'max',$redMax, @@ -767,7 +929,7 @@ sub _tc_iface_init # XXX: This probably needs working on sub _tc_class_optimize { - my ($changeSet,$interface,$limitTcClass,$rate) = @_; + my ($changeSet,$interface,$poolTcClass,$rate) = @_; # Rate for things like ICMP , ACK, SYN ... etc @@ -779,7 +941,7 @@ sub _tc_class_optimize $rateBand2 = PRIO_RATE_BURST_MIN if ($rateBand2 < PRIO_RATE_BURST_MIN); my $rateBand2Burst = ($rateBand2 / 8) * PRIO_RATE_BURST_MAXM; - my $prioTcClass = _reserveMajorTcClassByPrioClass($interface,$limitTcClass); + my $prioTcClass = _reserveMajorTcClassByPrioClass($interface,$poolTcClass); # # DEFINE 3 PRIO BANDS @@ -789,7 +951,7 @@ sub _tc_class_optimize $changeSet->add([ '/sbin/tc','qdisc','add', 'dev',$interface, - 'parent',"1:$limitTcClass", + 'parent',"1:$poolTcClass", 'handle',"$prioTcClass:", 'prio', 'bands','3', @@ -1130,6 +1292,7 @@ sub _tc_filter_add_dstlink { my ($changeSet,$interface,$parentID,$priority,$filterID,$protocol,$htHex,$ipHex,$cidr,$mask) = @_; + # Add hash table _tc_filter_hash_add($changeSet,$interface,$parentID,$priority,$filterID,$config->{'ip_protocol'}); # Add filter to it @@ -1142,6 +1305,7 @@ sub _tc_filter_add_srclink { my ($changeSet,$interface,$parentID,$priority,$filterID,$protocol,$htHex,$ipHex,$cidr,$mask) = @_; + # Add hash table _tc_filter_hash_add($changeSet,$interface,$parentID,$priority,$filterID,$config->{'ip_protocol'}); # Add filter to it @@ -1152,7 +1316,7 @@ sub _tc_filter_add_srclink # Function to easily add a hash table sub _tc_filter_add_flowlink { - my ($changeSet,$interface,$parentID,$priority,$protocol,$htHex,$ipHex,$type,$offset,$ip,$limitTcClass) = @_; + my ($changeSet,$interface,$parentID,$priority,$protocol,$htHex,$ipHex,$type,$offset,$ip,$poolTcClass) = @_; # Link hash table @@ -1169,7 +1333,7 @@ sub _tc_filter_add_flowlink 'match','ip',$type,$ip, 'at',$offset+$config->{'iphdr_offset'}, # Link to our flow - 'flowid',"1:$limitTcClass", + 'flowid',"1:$poolTcClass", ]); } @@ -1179,6 +1343,7 @@ sub _tc_filter_hash_add { my ($changeSet,$interface,$parentID,$priority,$filterID,$protocol) = @_; + # Create second level hash table for $ip1 $changeSet->add([ '/sbin/tc','filter','add', @@ -1198,6 +1363,7 @@ sub _tc_filter_add { my ($changeSet,$interface,$parentID,$priority,$filterID,$protocol,$htHex,$ipHex,$type,$offset,$cidr,$mask) = @_; + # Link hash table $changeSet->add([ '/sbin/tc','filter','add', @@ -1221,7 +1387,8 @@ sub _tc_filter_add # Function to add a TC class sub _tc_class_add { - my ($changeSet,$interface,$majorTcClass,$trafficClassTcClass,$limitTcClass,$rate,$ceil,$trafficPriority) = @_; + my ($changeSet,$interface,$majorTcClass,$trafficClassTcClass,$poolTcClass,$rate,$ceil,$trafficPriority) = @_; + # Set burst to a sane value my $burst = int($ceil / 8 / 5); @@ -1231,7 +1398,7 @@ sub _tc_class_add '/sbin/tc','class','add', 'dev',$interface, 'parent',"$majorTcClass:$trafficClassTcClass", - 'classid',"$majorTcClass:$limitTcClass", + 'classid',"$majorTcClass:$poolTcClass", 'htb', 'rate', "${rate}kbit", 'ceil', "${ceil}kbit", @@ -1244,7 +1411,7 @@ sub _tc_class_add # Function to change a TC class sub _tc_class_change { - my ($changeSet,$interface,$majorTcClass,$trafficClassTcClass,$limitTcClass,$rate,$ceil,$trafficPriority) = @_; + my ($changeSet,$interface,$majorTcClass,$trafficClassTcClass,$poolTcClass,$rate,$ceil,$trafficPriority) = @_; # Set burst to a sane value @@ -1255,7 +1422,7 @@ sub _tc_class_change '/sbin/tc','class','change', 'dev',$interface, 'parent',"$majorTcClass:$trafficClassTcClass", - 'classid',"$majorTcClass:$limitTcClass", + 'classid',"$majorTcClass:$poolTcClass", 'htb', 'rate', "${rate}kbit", 'ceil', "${ceil}kbit", @@ -1265,21 +1432,21 @@ sub _tc_class_change } -# Get a limit class TC class -sub _reserveTcClassByLimitID +# Get a pool TC class from pool ID +sub _reserveTcClassByPoolID { - my ($interface,$lid) = @_; + my ($interface,$pid) = @_; - return __reserveMinorTcClass($interface,TC_ROOT_CLASS,$lid); + return __reserveMinorTcClass($interface,TC_ROOT_CLASS,"_pool_class_:$pid"); } # Get a traffic class TC class -sub _reserveTcClassByClassID +sub _reserveTcClassByTrafficClassID { my ($interface,$classID) = @_; - return __reserveMinorTcClass($interface,TC_ROOT_CLASS,"_class_:$classID"); + return __reserveMinorTcClass($interface,TC_ROOT_CLASS,"_traffic_class_:$classID"); } @@ -1289,16 +1456,16 @@ sub _reserveMajorTcClassByPrioClass { my ($interface,$classID) = @_; - return _reserveMajorTcClass($interface,"_prioclass_:$classID"); + return _reserveMajorTcClass($interface,"_priority_class_:$classID"); } -# Return TC class using class -sub _getTcClassFromClassID +# Return TC class from a traffic class ID +sub _getTcClassFromTrafficClassID { my ($interface,$classID) = @_; - return __getMinorTcClassByRef($interface,TC_ROOT_CLASS,"_class_:$classID"); + return __getMinorTcClassByRef($interface,TC_ROOT_CLASS,"_traffic_class_:$classID"); } @@ -1308,16 +1475,16 @@ sub _getPrioTcClass { my ($interface,$tcClass) = @_; - return __getMajorTcClassByRef($interface,"_prioclass_:$tcClass"); + return __getMajorTcClassByRef($interface,"_priority_class_:$tcClass"); } # Function to dispose of a TC class -sub _disposeLimitTcClass +sub _disposePoolTcClass { my ($interface,$tcClass) = @_; - return __disposeMinorTcClass($interface,TC_ROOT_CLASS,$tcClass); + return __disposeMinorTcClass($interface,TC_ROOT_CLASS,"_pool_class_:$tcClass"); } @@ -1329,15 +1496,15 @@ sub _disposePrioTcClass # If we can grab the major class dipose of it - if (my $majorTcClass = _getPrioTcClass($interface,$tcClass)) { - return __disposeMajorTcClass($interface,$majorTcClass); + my $majorTcClass = _getPrioTcClass($interface,$tcClass); + if (!defined($majorTcClass)) { + return undef; } - return undef; + return __disposeMajorTcClass($interface,$majorTcClass); } - # Function to get next available TC class sub __reserveMinorTcClass { @@ -1410,11 +1577,11 @@ sub __getMinorTcClassByRef my ($interface,$majorTcClass,$ref) = @_; - if (defined($tcClasses->{$interface}) && defined($tcClasses->{$interface}->{$majorTcClass})) { - return $tcClasses->{$interface}->{$majorTcClass}->{'reverse'}->{$ref}; + if (!defined($tcClasses->{$interface}) || !defined($tcClasses->{$interface}->{$majorTcClass})) { + return undef; } - return undef; + return $tcClasses->{$interface}->{$majorTcClass}->{'reverse'}->{$ref}; } @@ -1424,11 +1591,11 @@ sub __getMajorTcClassByRef my ($interface,$ref) = @_; - if (defined($tcClasses->{$interface})) { - return $tcClasses->{$interface}->{'reverse'}->{$ref}; + if (!defined($tcClasses->{$interface})) { + return undef; } - return undef; + return $tcClasses->{$interface}->{'reverse'}->{$ref}; } @@ -1438,11 +1605,11 @@ sub __getRefByMinorTcClass my ($interface,$majorTcClass,$minorTcClass) = @_; - if (defined($tcClasses->{$interface}) && defined($tcClasses->{$interface}->{$majorTcClass})) { - return $tcClasses->{$interface}->{$majorTcClass}->{'track'}->{$minorTcClass}; + if (!defined($tcClasses->{$interface}) || !defined($tcClasses->{$interface}->{$majorTcClass})) { + return undef; } - return undef; + return $tcClasses->{$interface}->{$majorTcClass}->{'track'}->{$minorTcClass}; } @@ -1496,8 +1663,8 @@ sub _reserveTcFilter # Generate new number if (!$filterID) { $filterID = keys %{$tcFilters->{$interface}->{'track'}}; - # Bump ID up - $filterID += TC_FILTER_LIMIT_BASE; + # Bump ID + $filterID += 2; # Skip 0 and 1 # We cannot use ID 800, its internal $filterID = 801 if ($filterID == 800); # Hex it @@ -1527,7 +1694,7 @@ sub _disposeTcFilter # # Initialize our tc session -sub task_session_start +sub _task_session_start { my $kernel = $_[KERNEL]; @@ -1535,10 +1702,10 @@ sub task_session_start $kernel->alias_set("_tc"); # Setup handing of console INT - $kernel->sig('INT', 'handle_SIGINT'); + $kernel->sig("INT", "_SIGINT"); # Fire things up, we trigger this to process the task queue generated during init - $kernel->yield("task_run_next"); + $kernel->yield("_task_run_next"); } @@ -1559,7 +1726,7 @@ sub _task_add_to_queue $numChanges++; } - $logger->log(LOG_DEBUG,"[TC] TASK: Queued $numChanges changes"); + $logger->log(LOG_DEBUG,"[TC] TASK: Queued %s changes",$numChanges); } @@ -1575,7 +1742,7 @@ sub _task_put_next delete($heap->{'idle_tasks'}->{$task->ID}); $task->put($cmdStr); - $logger->log(LOG_DEBUG,"[TC] TASK/".$task->ID.": Starting '$cmdStr' as ".$task->ID." with PID ".$task->PID); + $logger->log(LOG_DEBUG,"[TC] TASK/%s: Starting '%s' as %s with PID %s",$task->ID,$cmdStr,$task->ID,$task->PID); # If there is no commands in the queue, set it to idle } else { @@ -1585,8 +1752,8 @@ sub _task_put_next } -# Run a task -sub task_add +# Queue a task +sub _task_queue { my ($kernel,$heap,$changeSet) = @_[KERNEL,HEAP,ARG0]; @@ -1596,13 +1763,13 @@ sub task_add # Trigger a run if list is not empty if (@taskQueue) { - $kernel->yield("task_run_next"); + $kernel->yield("_task_run_next"); } } # Run next task -sub task_run_next +sub _task_run_next { my ($kernel,$heap) = @_[KERNEL,HEAP]; @@ -1626,14 +1793,13 @@ sub task_run_next my $task = POE::Wheel::Run->new( Program => [ '/sbin/tc', '-force', '-batch' ], Conduit => 'pipe', -# Program => [ '/root/tc.sh' ], StdioFilter => POE::Filter::Line->new( Literal => "\n" ), StderrFilter => POE::Filter::Line->new( Literal => "\n" ), - StdoutEvent => 'task_child_stdout', - StderrEvent => 'task_child_stderr', - CloseEvent => 'task_child_close', - StdinEvent => 'task_child_stdin', - ErrorEvent => 'task_child_error', + StdoutEvent => '_task_child_stdout', + StderrEvent => '_task_child_stderr', + CloseEvent => '_task_child_close', + StdinEvent => '_task_child_stdin', + ErrorEvent => '_task_child_error', ) or $logger->log(LOG_ERR,"[TC] TASK: Unable to start task"); # Set task ID @@ -1641,7 +1807,7 @@ sub task_run_next # Intercept SIGCHLD - $kernel->sig_child($task->PID, "handle_SIGCHLD"); + $kernel->sig_child($task->PID, "_SIGCHLD"); # Wheel events include the wheel's ID. $heap->{'task_by_wid'}->{$task_id} = $task; @@ -1654,32 +1820,38 @@ sub task_run_next # Child writes to STDOUT -sub task_child_stdout +sub _task_child_stdout { my ($kernel,$heap,$stdout,$task_id) = @_[KERNEL,HEAP,ARG0,ARG1]; + + my $task = $heap->{'task_by_wid'}->{$task_id}; - $logger->log(LOG_INFO,"[TC] TASK/$task_id: STDOUT => ".$stdout); + $logger->log(LOG_INFO,"[TC] TASK/%s: STDOUT => %s",$task_id,$stdout); } # Child writes to STDERR -sub task_child_stderr +sub _task_child_stderr { my ($kernel,$heap,$stdout,$task_id) = @_[KERNEL,HEAP,ARG0,ARG1]; + + my $task = $heap->{'task_by_wid'}->{$task_id}; - $logger->log(LOG_WARN,"[TC] TASK/$task_id: STDERR => ".$stdout); + $logger->log(LOG_WARN,"[TC] TASK/%s: STDOUT => %s",$task_id,$stdout); } # Child flushed to STDIN -sub task_child_stdin +sub _task_child_stdin { my ($kernel,$heap,$task_id) = @_[KERNEL,HEAP,ARG0]; + + my $task = $heap->{'task_by_wid'}->{$task_id}; - $logger->log(LOG_DEBUG,"[TC] TASK/$task_id is READY"); + $logger->log(LOG_DEBUG,"[TC] TASK/%s is READY",$task_id); # And shove another queued command its direction _task_put_next($heap,$task); } @@ -1687,18 +1859,20 @@ sub task_child_stdin # Child closed its handles, it won't communicate with us, so remove it -sub task_child_close +sub _task_child_close { my ($kernel,$heap,$task_id) = @_[KERNEL,HEAP,ARG0]; + + my $task = $heap->{'task_by_wid'}->{$task_id}; # May have been reaped by task_sigchld() if (!defined($task)) { - $logger->log(LOG_DEBUG,"[TC] TASK/$task_id: Closed dead child"); + $logger->log(LOG_DEBUG,"[TC] TASK/%s: Closed dead child",$task_id); return; } - $logger->log(LOG_DEBUG,"[TC] TASK/$task_id: Closed PID ".$task->PID); + $logger->log(LOG_DEBUG,"[TC] TASK/%s: Closed PID %s",$task_id,$task->PID); # Remove other references delete($heap->{'task_by_wid'}->{$task_id}); @@ -1707,22 +1881,24 @@ sub task_child_close # Start next one, if there is a next one if (@taskQueue) { - $kernel->yield("task_run_next"); + $kernel->yield("_task_run_next"); } } # Child got an error event, lets remove it too -sub task_child_error +sub _task_child_error { my ($kernel,$heap,$operation,$errnum,$errstr,$task_id) = @_[KERNEL,HEAP,ARG0..ARG3]; + + my $task = $heap->{'task_by_wid'}->{$task_id}; if ($operation eq "read" && !$errnum) { $errstr = "Remote end closed" } - $logger->log(LOG_ERR,"[TC] Task $task_id generated $operation error $errnum: '$errstr'"); + $logger->log(LOG_ERR,"[TC] Task %s generated %s error %s: '%s'",$task_id,$operation,$errnum,$errstr); # If there is no task, return return if (!defined($task)); @@ -1734,19 +1910,20 @@ sub task_child_error # Start next one, if there is a next one if (@taskQueue) { - $kernel->yield("task_run_next"); + $kernel->yield("_task_run_next"); } } # Reap the dead child -sub task_handle_SIGCHLD +sub _task_SIGCHLD { my ($kernel,$heap,$pid,$status) = @_[KERNEL,HEAP,ARG1,ARG2]; - my $task = $heap->{'task_by_pid'}->{$pid}; - $logger->log(LOG_DEBUG,"[TC] TASK: Task with PID $pid exited with status $status"); + my $task = $heap->{'task_by_pid'}->{$pid}; + + $logger->log(LOG_DEBUG,"[TC] TASK: Task with PID %s exited with status %s",$pid,$status); # May have been reaped by task_child_close() return if (!defined($task)); @@ -1759,10 +1936,11 @@ sub task_handle_SIGCHLD # Handle SIGINT -sub task_handle_SIGINT +sub _task_SIGINT { my ($kernel,$heap,$signal_name) = @_[KERNEL,HEAP,ARG0]; + # Shutdown stdin on all children, this will terminate /sbin/tc foreach my $task_id (keys %{$heap->{'task_by_wid'}}) { my $task = $heap->{'task_by_wid'}{$task_id}; @@ -1776,9 +1954,6 @@ sub task_handle_SIGINT - - - # TC changeset item package TC::ChangeSet;