Microsoft Branch Cache Hash Offload

Problem this snippet solves:

BranchCache is a technology that Microsoft released with Windows 7 and Server 2008 R2 which can have a profound effect on where users actually source their content and the bandwidth consumed in retrieving that data. It’s fairly common in today’s work world to see global enterprises have multiple branch offices with users who regularly access data from a single centralized datacenter. If you take a look at the data that these users are commonly requesting, you’ll quickly realize that there is often a significant amount of data that is repetitive between the branch users. BranchCache is a technology designed to cut down on the unnecessary round trips from the branch office to the datacenter for data. By keeping an accessible copy of the content in the local branch, there will be a significant reduction in bandwidth used, and users will often be able to get the content faster than retrieving it from the distant datacenter.

This iRule will offload the hash calculation from the servers.

How to use this snippet:

Tested with oneconnect and tcp profiles applied, as well as this http profile:

profile http ms-pccrc-http {
   compress enable
   compress buffer size 131072
   compress http 1.0 disable
   compress gzip memory level 16k
   compress gzip window size 64k
}

Code :

when HTTP_REQUEST {
   set client "[IP::remote_addr]:[TCP::remote_port], URI: [HTTP::uri]"
   set key "[HTTP::host][HTTP::uri]"
   persist uie $key
   if { [HTTP::header "Accept-Encoding"] contains "peerdist" } {
      set will_peerdist 1
      log local0. "$client - peerdist requested"
      set pcc [session lookup uie $key]
      if { [llength $pcc] != 0 } {
         set pl [lrange [persist lookup uie $key] 0 2]
         if { $pl ne [lindex $pcc 0] } {
            log local0. "$client - cached peerdist hash does not match persistence: $pl != [lindex $pcc 0]"
         } else {
            log local0. "$client - responding with previously cached peerdist hash."
            HTTP::respond 200 content [binary format a* [lindex $pcc 1]] \
                  "Content-Encoding" "peerdist" \
                  "X-P2P-PeerDist" "Version=1.0, ContentLength=[lindex $pcc 2]" \
                  "ETag" [lindex $pcc 3] \
                  "Content-Type" [lindex $pcc 4]
         }
         unset pl
      }
      unset pcc
   } else {
      set will_peerdist 0
      log local0. "$client - peerdist not requested"
   }
}
when HTTP_RESPONSE {
   # Client can do peerdist, but server response is normal
   if { $will_peerdist && \
         [HTTP::status] == 200 && \
         ! [HTTP::header exists "Content-Encoding"] || \
         [HTTP::header "Content-Encoding"] eq "identity" } {
      set clen [HTTP::header "Content-Length"]
      if { $clen > 0 } {
         log local0. "[IP::remote_addr]:[TCP::remote_port]->$client - collecting content: $clen"
         set h_len $clen
         if { $h_len > 65536 } { set h_len 65536 }
         HTTP::collect $h_len
      } else {
         log local0. "[IP::remote_addr]:[TCP::remote_port]->$client - no content found: $clen, headers: [HTTP::header names]"
      }
   }
}
when HTTP_RESPONSE_DATA {
   if { ! [info exists offset] } {
      set offset 0
      set blkcnt 0
      set blkhashes {}
      set blkpci {}
      set segoff 0
      set segcnt 0
      set seglen 0
      set segpci {}
      set secret [virtual name]
      HTTP::header replace "Content-Encoding" "peerdist"
      HTTP::header insert "X-P2P-PeerDist" "Version=1.0, ContentLength=$clen"
      log local0. "$client - peerdist hashing started"
   }
   while { $offset < $clen } {
      #log local0. "$client - currently collected: [HTTP::payload length], hashing offset: $offset (of $clen)"
      set h_len [expr {$clen - $offset}]
      if { $h_len > 65536 } { set h_len 65536 }

      # if have more to do, but we haven't collected it yet
      if { [HTTP::payload length] < $h_len } {
         HTTP::collect $h_len
         #log local0. "$client - waiting for [expr {$h_len-[HTTP::payload length]}] more, have: [HTTP::payload length]"
         return
      }

      # Hash the current block
      set blkhashes [binary format a*a* $blkhashes [sha256 [HTTP::payload $h_len]]]

      # Delete the payload we just hashed since we don't need it anymore
      HTTP::payload replace 0 $h_len {}

      incr offset $h_len
      incr blkcnt

      # At the Segment boundary
      if { $blkcnt >= 512 } {

         # Add a segment hash of block hashes
         set seglen [expr {$offset - $segoff}]
         set seghash [sha256 $blkhashes]
         set seghods [sha256 [binary format a*a* $seghash $secret]]
         lappend segpci [binary format wiia*a* $segoff $seglen 65536 $seghash $seghods]

         # Add a block hash
         lappend blkpci [binary format ia* $blkcnt $blkhashes]

         set blkhashes {}
         set blkcnt 0
         set segoff $offset
         incr segcnt
         log local0. "$client - segment, len: $seglen, cnt: $segcnt, off: $segoff"
      }
   }

   # Didn't end on a block/segment boundary
   if { $blkcnt > 0 } {
      # Add a segment hash of block hashes for the last segment
      set seglen [expr {$offset - $segoff}]
      set seghash [sha256 $blkhashes]
      set seghods [sha256 [binary format a*a* $seghash $secret]]
      lappend segpci [binary format wiia*a* $segoff $seglen 65536 $seghash $seghods]

      # Add a block hash
      lappend blkpci [binary format ia* $blkcnt $blkhashes]

      set blkcnt 0
      incr segcnt
      log local0. "$client - last segment, cnt: $segcnt, len: $seglen"
   }

   # Now build the new payload with the hashes
   set pci [binary format siiii 0x0100 0x800C 0 0 $segcnt]
   HTTP::payload replace 0 0 $pci
   set offset [string length $pci]
   foreach pci $segpci {
      HTTP::payload replace $offset 0 $pci
      incr offset [string length $pci]
   }
   foreach pci $blkpci {
      HTTP::payload replace $offset 0 $pci
      incr offset [string length $pci]
   }
   session add uie $key [list [LB::server] [HTTP::payload] $clen [HTTP::header ETag] [HTTP::header Content-Type]]
   HTTP::release
   log local0. "$client - content hash completed!"

   unset -nocomplain pci offset blkcnt blkpci blkhashes secret key
   unset -nocomplain segoff segcnt seglen segpci seghash seghods
}
Published Mar 18, 2015
Version 1.0

Was this article helpful?