From c9ca8f52cb677d576dfa6ef531c81c15b1d275cd Mon Sep 17 00:00:00 2001
From: Carl Byington <carl@five-ten-sg.com>
Date: Wed, 22 Apr 2015 15:07:07 -0700
Subject: add small packet matching

---
 lib/Vyatta/Qos/Match.pm                            |  120 +++++++++++++++++---
 .../node.tag/match/node.tag/ip/small/node.def      |    1 +
 .../node.tag/match/node.tag/ipv6/small/node.def    |    1 +
 3 files changed, 104 insertions(+), 18 deletions(-)
 create mode 100644 templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ip/small/node.def
 create mode 100644 templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ipv6/small/node.def

diff --git a/lib/Vyatta/Qos/Match.pm b/lib/Vyatta/Qos/Match.pm
index c8078b6..abdf226 100644
--- a/lib/Vyatta/Qos/Match.pm
+++ b/lib/Vyatta/Qos/Match.pm
@@ -26,7 +26,7 @@ sub getPort {
 
     if ( $str =~ /^([0-9]+)|(0x[0-9a-fA-F]+)$/ ) {
         die "$str is not a valid port number\n"
-          if ( $str <= 0 || $str > 65535 );
+            if ( $str <= 0 || $str > 65535 );
         return $str;
     }
 
@@ -47,8 +47,8 @@ sub new {
 
     # special case for match all
     unless ($config) {
-	$self->{'ether'} = { protocol => 'all' };
-	return $self;
+        $self->{'ether'} = { protocol => 'all' };
+        return $self;
     }
 
     foreach my $af (qw(ip ipv6 ether)) {
@@ -67,20 +67,23 @@ sub new {
             my $dst     = $config->returnValue("$af destination address");
             my $sport   = $config->returnValue("$af source port");
             my $dport   = $config->returnValue("$af destination port");
+            my $small   = $config->exists("$af small");
+
             my $maxlen  = $config->returnValue("$af max-length");
             my $tcpsyn  = $config->exists("$af tcp syn");
             my $tcpack  = $config->exists("$af tcp ack");
-        
+
             $fields{dsfield}  = getDsfield($dsfield) if $dsfield;
             $fields{protocol} = getProtocol($ipprot) if $ipprot;
             $fields{src}      = $src if $src;
             $fields{dst}      = $dst if $dst;
             $fields{sport}    = getPort( $sport, $ipprot ) if $sport;
             $fields{dport}    = getPort( $dport, $ipprot ) if $dport;
+            $fields{small}    = $small if $small;
             $fields{maxlen}   = $maxlen if $maxlen;
             $fields{tcpsyn}   = $tcpsyn;
             $fields{tcpack}   = $tcpack;
-            
+
             # Reject any protocol apart from tcp if tcp syn or tcp ack are set
             if($tcpsyn or $tcpack) {
                 die "Protocol cannot be different from tcp" if defined $fields{protocol} and $fields{protocol} ne getProtocol('tcp');
@@ -88,7 +91,7 @@ sub new {
             }
         }
 
-        # if the hash is empty then we didn't generate a match rule 
+        # if the hash is empty then we didn't generate a match rule
         # this usually means user left an uncompleted match in the config
         my @keys = keys(%fields);
         if ($#keys < 0) {
@@ -120,6 +123,82 @@ sub new {
     return $self;
 }
 
+sub small_ip_filter {
+    my ( $dev, $parent, $prio, $classid ) = @_;
+    my $protoip = "ip";
+    my $synack  = 2;    # hash table id, arbitrary number
+    $parent  = sprintf("%x", $parent);
+    $classid = sprintf("%x", $classid);
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32\n";
+    # make a linked hash table
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip handle $synack: u32 divisor 1\n";
+    # tcp syn bit
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32 ht $synack:";
+    print "    match u8 0x02 0x02 at 13";
+    print "    flowid $parent:$classid\n";
+    # tcp ack bit
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32 ht $synack:";
+    print "    match u8 0x10 0x10 at 13";
+    print "    flowid $parent:$classid\n";
+    # ipv4/icmp
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32";
+    print "    match ip protocol 1 0xff";
+    print "    flowid $parent:$classid\n";
+    # ipv4/tcp, total len<256, tos=0x10 == minimum delay
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32";
+    print "    match ip protocol 6 0xff";
+    print "    match u16 0x0000 0xff00 at 2";
+    print "    match ip tos 0x10 0xff";
+    print "    flowid $parent:$classid\n";
+    # ipv4/tcp, total len<128, not fragmented
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip u32";
+    print "    match ip protocol 6 0xff";
+    print "    match u16 0x0000 0xff80 at 2";
+    print "    match ip nofrag";
+    print "    offset at 0 mask 0x0f00 shift 6 eat";
+    print "    link $synack:\n";
+}
+
+sub small_ip6_filter {
+    my ( $dev, $parent, $prio, $classid ) = @_;
+    my $protoip6 = "ipv6";
+    my $synack6  = 3;    # hash table id, arbitrary number
+    $parent  = sprintf("%x", $parent);
+    $classid = sprintf("%x", $classid);
+    # setup base filter
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32\n";
+    # make a linked hash table
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 handle $synack6: u32 divisor 1\n";
+    # tcp syn bit
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32 ht $synack6: ";
+    print "    match u8 0x02 0x02 at 13";
+    print "    flowid $parent:$classid\n";
+    # tcp ack bit
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32 ht $synack6:";
+    print "    match u8 0x10 0x10 at 13";
+    print "    flowid $parent:$classid\n";
+    # ipv6/icmpv6
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32";
+    print "    match ip6 protocol 58 0xff";
+    print "    flowid $parent:$classid\n";
+    # ipv6/tcp, payload len<128, priority=0x10 == minimum delay
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32";
+    print "    match ip6 protocol 6 0xff";
+    print "    match u16 0x0000 0xff80 at 4";
+    print "    match ip6 priority 0x10 0xff";
+    print "    flowid $parent:$classid\n";
+    # ipv6/tcp, payload len<64, not fragmented since the next header is a tcp header
+    # this does not handle packets with other ipv6 extension headers that might be
+    # present between the ipv6 header and the tcp header
+    print "filter add dev $dev parent $parent: prior $prio protocol $protoip6 u32";
+    print "    match ip6 protocol 6 0xff";
+    print "    match u16 0x0000 0xffc0 at 4";
+    print "    offset plus 40 eat";
+    print "    link $synack6:\n";
+}
+
+
+
 sub filter {
     my ( $self, $dev, $parent, $classid, $prio, $dsmark, $police ) = @_;
 
@@ -134,9 +213,9 @@ sub filter {
             next unless $ip && $$ip{dsfield};
 
             printf "filter add dev %s parent %x: protocol %s prio %d",
-	      $dev, $parent, $ipver, $prio;
+                $dev, $parent, $ipver, $prio;
             printf " handle %s tcindex classid %x:%x\n",
-              $$ip{dsfield}, $parent, $classid;
+                $$ip{dsfield}, $parent, $classid;
 
             $prio += 1;
         }
@@ -148,12 +227,18 @@ sub filter {
         my $p = $self->{$proto};
         next unless $p;
 
+        if (defined($$p{small})) {
+            small_ip_filter($dev, $parent, $prio, $classid)  if ($proto eq 'ip');
+            small_ip6_filter($dev, $parent, $prio, $classid) if ($proto eq 'ipv6');
+            next;
+        }
+
         printf "filter add dev %s parent %x: prio %d", $dev, $parent, $prio;
         if ( $proto eq 'ether' ) {
             my $type = $$p{protocol};
             $type = 'all' unless $type;
 
-	        print " protocol $type u32";
+            print " protocol $type u32";
             if ( defined( $$p{src} ) || defined( $$p{dst} ) ) {
                 print " match ether src $$p{src}" if $$p{src};
                 print " match ether dst $$p{dst}" if $$p{dst};
@@ -211,8 +296,8 @@ sub filter {
             }
         }
 
-        print " match mark $fwmark 0xff" if $fwmark;
-        print " $police"                 if $police;
+        print  " match mark $fwmark 0xff" if $fwmark;
+        print  " $police"                 if $police;
         printf " flowid %x:%x\n", $parent, $classid;
         return;
     }
@@ -221,18 +306,17 @@ sub filter {
     my $vif   = $self->{_vif};
     if ( defined($vif) || defined($indev) ) {
         printf "filter add dev %s parent %x: prio %d", $dev, $parent, $prio;
-        print " protocol all basic";
-        print " match meta\(rt_iif eq $indev\)"        if $indev;
-        print " match meta\(vlan mask 0xfff eq $vif\)" if $vif;
-        print " match meta\(fwmark eq $fwmark\)"       if $fwmark;
-
-        print " $police" if $police;
+        print  " protocol all basic";
+        print  " match meta\(rt_iif eq $indev\)"        if $indev;
+        print  " match meta\(vlan mask 0xfff eq $vif\)" if $vif;
+        print  " match meta\(fwmark eq $fwmark\)"       if $fwmark;
+        print  " $police" if $police;
         printf " flowid %x:%x\n", $parent, $classid;
     }
     elsif ($fwmark) {
         printf "filter add dev %s parent %x: prio %d", $dev, $parent, $prio;
         printf " protocol all handle %d fw", $fwmark;
-        print " $police" if $police;
+        print  " $police" if $police;
         printf " flowid %x:%x\n", $parent, $classid;
     }
 }
diff --git a/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ip/small/node.def b/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ip/small/node.def
new file mode 100644
index 0000000..4321d9d
--- /dev/null
+++ b/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ip/small/node.def
@@ -0,0 +1 @@
+help: Match on small ip packets
diff --git a/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ipv6/small/node.def b/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ipv6/small/node.def
new file mode 100644
index 0000000..8afbb67
--- /dev/null
+++ b/templates/traffic-policy/shaper/node.tag/class/node.tag/match/node.tag/ipv6/small/node.def
@@ -0,0 +1 @@
+help: Match on small ipv6 packets
-- 
1.7.1

