Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #####
- # ProtocolProfileProcs.tcl
- # Author: Thomas Schockaert
- # Last Changed: 20141104
- # Contents: The procedures that perform the protocol mapping magic that translates bytes to meaningful logs.
- # Howto:
- # - You only need to call mapProtocol in your iRule in the CLIENT_DATA and SERVER_DATA event.
- # - It is possible that you need to pre-treat the payload, as is the case with SSL, where one payload can contain multiple instances of the protocol map.
- # You would need to run mapProtocol on each 'record', instead of on the entire payload, as the payload contains multiple records.
- # - Example:
- # when CLIENT_ACCEPTED {
- # array set pmap [call ProtocolProfilerInit::init_dns]
- # }
- # when CLIENT_DATA {
- # binary scan [UDP::payload] H* client_payload_in_hex
- # call ProtocolProfilerProcs::mapProtocol 0 $client_payload_in_hex 0 {} "" [array get pmap] $static::DEBUG
- # }
- # when SERVER_DATA {
- # binary scan [UDP::payload] H* server_payload_in_hex
- # call ProtocolProfilerProcs::mapProtocol 0 $server_payload_in_hex 0 {} "" [array get pmap] $static::DEBUG
- # }
- #####
- ### isIndex { index lst_pmap debug }
- # Description:
- # - This procedure checks if a given index exists in the 'pmap' array
- # Arguments:
- # - index => an index to verify for existence
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - 1 if found
- # - 0 if not found
- proc isIndex { index lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- # search the array for the existence of the index
- if { [info exists pmap($index)] } {
- return 1
- } else { if { $debug == 2 } { log local0. "DEBUG isIndex: pmap($index) could not be found" } }
- return 0
- }
- ### isParent { index lst_pmap debug }
- # Description:
- # - This procedure appends '_0' to a given text (index) and checks if it exists in the 'pmap' array
- # Arguments:
- # - index => a string that can be found in the pmap names list (a name is an array key)
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - 1 if found
- # - 0 if not found
- proc isParentIndex { index lst_pmap debug } {
- # verify that the given index exists
- if { [call ProtocolProfilerProcs::isIndex $index $lst_pmap $debug] } {
- # establish what its first child should look like
- set first_child_index "${index}_0"
- # verify if that first child exists
- if { [call ProtocolProfilerProcs::isIndex $first_child_index $lst_pmap $debug] } {
- return 1
- } else { if { $debug == 2 } { log local0. "DEBUG isParentIndex: isIndex said '$first_child_index' is not a valid index" } }
- } else { if { $debug == 2 } { log local0. "DEBUG isParentIndex: isIndex said '$index' is not a valid index" } }
- return 0
- }
- ### getParentIndex { index lst_pmap debug }
- # Description:
- # - This procedure splits an index string by the '_' character and returns all but the last part
- # Arguments:
- # - index => a string splittable by '_' characters (ex.: 0_1_2_3)
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - the parent index string if found
- # - -1 if there is no parent index to generate (i.e.: if the index passed to the proc does not contain '_' characters)
- proc getParentIndex { index lst_pmap debug } {
- # split the index string by the '_' character
- set lst_index [split $index "_"]
- # if one or more '_' characters are found, concatenate all but the last field and return it
- if { [llength $lst_index] > 1 } {
- set first_parent_index ""
- for { set x 0 } { $x < [expr [llength $lst_index]-1] } { incr x } {
- append first_parent_index "[lindex $lst_index $x]_"
- }
- set first_parent_index [substr $first_parent_index 0 [expr [string length $first_parent_index] -1]]
- return $first_parent_index
- } else { if { $debug == 2 } { log local0. "DEBUG getParentIndex: $index is a root parent (no '_' found)" } }
- return -1
- }
- ### getSiblingIndex { index lst_pmap debug }
- # Description:
- # - This procedure returns a list containing the siblings of an index. It finds them by finding the parent index and looping through possible child indexes.
- # - The returned list does not include the index itself, just its siblings.
- # Arguments:
- # - index => an index string
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - a list of sibling indexes (possibly empty)
- proc getSiblingIndexes { index lst_pmap debug } {
- set lst_sibling_indexes {}
- if { [call ProtocolProfilerProcs::isIndex $index $lst_pmap $debug] } {
- set parent_index [call ProtocolProfilerProcs::getParentIndex $index $lst_pmap $debug]
- if { [call ProtocolProfilerProcs::isParentIndex $parent_index $lst_pmap $debug] } {
- for { set x 0 } { $x < 255 } { incr x } {
- set new_index "${parent_index}_$x"
- if { [call ProtocolProfilerProcs::isIndex $new_index $lst_pmap $debug] } {
- #if { !($index == $new_index) } {
- lappend lst_sibling_indexes $new_index
- #}
- } else { break }
- }
- } else { if { $debug == 2 } { log local0. "DEBUG getSiblingIndexes: isParentIndex said '$parent_index' is not a valid parent index" } }
- } else { if { $debug == 2 } { log local0. "DEBUG getSiblingIndexes: isIndex said '$index' is not a valid index" } }
- return $lst_sibling_indexes
- }
- ### getChildrenIndexes { index lst_pmap debug }
- # Description:
- # - This procedure returns a list containing the children of an index. It finds them by checking if the index is a parent index and looping through possible child indexes.
- # Arguments:
- # - index => an index string
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - a list of child indexes (possibly empty)
- proc getChildrenIndexes { index lst_pmap debug } {
- set lst_child_indexes {}
- if { [call ProtocolProfilerProcs::isParentIndex $index $lst_pmap $debug] } {
- for { set x 0 } { $x < 255 } { incr x } {
- set new_child_index "${index}_$x"
- if { !([call ProtocolProfilerProcs::isIndex $new_child_index $lst_pmap 0]) } {
- break
- }
- else {
- lappend lst_child_indexes $new_child_index
- }
- }
- } else { if { $debug == 2 } { log local0. "DEBUG getChildrenIndexes: isParentIndex said '$parent_index' is not a valid parent index" } }
- return $lst_child_indexes
- }
- ### getMarkedIndexes { index lst_pmap debug }
- # Description:
- # - This procedure returns a list containing the protocol map levels that have a marker below the current index.
- # - A marker consist of a field definition of 2 fields: the 'fieldnamename' and 'markerfield=markervalue'
- # Arguments:
- # - index => an index string
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - a list of marker indexes (possibly empty)
- proc getMarkedIndexes { index lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- set lst_marker_indexes {}
- if { [call ProtocolProfilerProcs::isParentIndex $index $lst_pmap $debug] } {
- for { set x 0 } { $x < 255 } { incr x } {
- set new_marker_index "${index}_$x"
- if { [call ProtocolProfilerProcs::isIndex $new_marker_index $lst_pmap 0] } {
- if { [llength $pmap($new_marker_index)] == 2 } {
- lappend lst_marker_indexes $new_marker_index
- }
- }
- }
- } else { if { $debug == 2 } { log local0. "DEBUG getMarkerIndexes: isParentIndex said '$index' is not a valid parent index" } }
- return $lst_marker_indexes
- }
- ### getIndexByFieldname { index fieldname lst_pmap debug }
- # Description:
- # - This procedure returns the index string for a fieldname that is present in the protocol map.
- # Arguments:
- # - index => an index string
- # - fieldname => a fieldname to search for in the protocol map
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - the index string for the fieldname if found
- # - an empty string if not found
- proc getIndexByFieldname { index fieldname lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- set lst_children [call ProtocolProfilerProcs::getChildrenIndexes $index $lst_pmap $debug]
- set search_index 0
- if { [llength $lst_children] > 0 } {
- foreach { child_index } $lst_children {
- if { [lindex $pmap($child_index) 0] == "$fieldname" } {
- set search_index $child_index
- break
- }
- else {
- set search_index [call ProtocolProfilerProcs::getIndexByFieldname $child_index $fieldname $lst_pmap $debug]
- }
- }
- } else { if { $debug == 2 } { log -noname local0. "DEBUG getIndexByFieldname: getChildrenIndexes said '$index' has no children." } }
- return $search_index
- }
- ### getFieldDefinitionByIndex { index lst_pmap debug }
- # Description:
- # - This procedure returns the field definition string for an index that is present in the protocol map.
- # Arguments:
- # - index => an index string
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - the field definition for the index if found
- # - an empty string if not found
- proc getFieldDefinitionByIndex { index lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- set returnvalue ""
- if { [call ProtocolProfilerProcs::isIndex $index $lst_pmap $debug] } {
- if { [llength $pmap($index)] >= 3 } {
- set returnvalue $pmap($index)
- } else { if { $debug == 2 } { log -noname local0. "DEBUG getFieldDefinitionByIndex: '$index' doet not contain a valid field definition" } }
- } else { if { $debug == 2 } { log -noname local0. "DEBUG getFieldDefinitionByIndex: isIndex said '$index' is not a valid index" } }
- return $returnvalue
- }
- ### getFieldDefinitionByFieldname { fieldname index lst_pmap debug }
- # Description:
- # - This procedure returns the field definition string for an fieldname that is present in the protocol map.
- # Arguments:
- # - fieldname => a fieldname string
- # - index => an index string
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - the field definition for the fieldname if found
- # - an empty string if not found
- proc getFieldDefinitionByFieldname { fieldname index lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- set fielddefinition ""
- set potential_fielddefinition [call ProtocolProfilerProcs::getFieldDefinitionByIndex $index $lst_pmap $debug]
- if { [lindex $potential_fielddefinition 0] == $fieldname } {
- set fielddefinition $potential_fielddefinition
- }
- else {
- set lst_children [call ProtocolProfilerProcs::getChildrenIndexes $index $lst_pmap 0]
- if { [llength $lst_children] > 0 } {
- foreach { child_index } $lst_children {
- set fielddefinition [call ProtocolProfilerProcs::getFieldDefinitionByFieldname $fieldname $child_index $lst_pmap $debug]
- if { !($fielddefinition == "") } {
- break
- }
- }
- } else { if { $debug == 2 } { log -noname local0. "DEBUG getFieldDefinitionByFieldname: getChildrenIndexes said '$index' has no children." } }
- }
- return $fielddefinition
- }
- proc resolveMarker { index data offset lst_pmap logspacing debug } {
- array set pmap $lst_pmap
- set returnvalue {}
- set marker_fielddefinition [call ProtocolProfilerProcs::getFieldDefinitionByIndex $index $lst_pmap $debug]
- if { !($marker_fielddefinition == "") } {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: found a potential marker field definition at index '$index' ($marker_fielddefinition)" }
- set marker_fieldname [lindex $marker_fielddefinition 0]
- set marker_bytelength [lindex $marker_fielddefinition 1]
- if { [string is integer $marker_bytelength] } {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: confirmed field definition at index '$index' as a valid marker" }
- set marker_datalength [expr $marker_bytelength*2]
- set marker_format [lindex $marker_fielddefinition 0]
- set lst_markedindexes [call ProtocolProfilerProcs::getMarkedIndexes $index $lst_pmap $debug]
- set lst_markedindexes_length [llength $lst_markedindexes]
- if { $lst_markedindexes_length > 0 } {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: found $lst_markedindexes_length indexes that reference '$marker_fieldname' as their marker ([join $lst_markedindexes ", "])" }
- foreach { marked_index } $lst_markedindexes {
- set marked_index_name [lindex $pmap($marked_index) 0]
- set marked_index_markerdescription [lindex $pmap($marked_index) 1]
- set marked_index_marker_name [lindex [split $marked_index_markerdescription "="] 0]
- set marked_index_marker_value [lindex [split $marked_index_markerdescription "="] 1]
- set marker_data 0x[substr $data $offset $marker_datalength]
- if { $marked_index_marker_value == $marker_data } {
- if { $debug == 1 } { log -noname local0. "${logspacing}- resolveMarker: index '$marked_index' matches the value of the marker '$marker_fieldname'" }
- set offset [expr $offset+$marker_datalength]
- lappend returnvalue $offset
- lappend returnvalue $marked_index
- break
- }
- elseif { $marked_index_marker_value == "*" } {
- if { $debug == 1 } { log -noname local0. "${logspacing}- resolveMarker: index '$marked_index' matches the wildcard value of the marker '$marker_fieldname'" }
- set offset [expr $offset+$marker_datalength]
- lappend returnvalue $offset
- lappend returnvalue $marked_index
- break
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing}- resolveMarker: index '$marked_index' does not match the value of the marker '$marker_fieldname'" }
- }
- }
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: could not find indexes that reference '$marker_fieldname' as their marker" }
- }
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: could not find a valid marker field definition at index '$index' ($pmap($index)) (bytelength not an integer)" }
- }
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveMarker: could not find a valid marker field definition at index '$index' ($pmap($index))" }
- }
- return $returnvalue
- }
- proc resolveFields { index data offset lst_lengthfields lst_pmap logspacing debug } {
- array set pmap $lst_pmap
- set returnvalue {}
- set lst_indexestoresolve [call ProtocolProfilerProcs::getSiblingIndexes $index $lst_pmap $debug]
- set lst_indexestoresolve_length [llength $lst_indexestoresolve]
- if { $lst_indexestoresolve_length > 0 } {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveFields: found $lst_indexestoresolve_length potential index(es) on the level of index '$index'" }
- foreach { resolve_index } $lst_indexestoresolve {
- set resolve_index_fielddefinition [call ProtocolProfilerProcs::getFieldDefinitionByIndex $resolve_index $lst_pmap $debug]
- if { !($resolve_index_fielddefinition == "") } {
- if { $debug == 1 } { log -noname local0. "${logspacing}- resolveFields: index '$resolve_index' has a resolvable field definition" }
- set resolve_index_fieldname [lindex $resolve_index_fielddefinition 0]
- set resolve_index_bytelength [lindex $resolve_index_fielddefinition 1]
- set resolve_index_format [lindex $resolve_index_fielddefinition 2]
- if { [string is integer $resolve_index_bytelength] } {
- if { $debug == 1 } { log -noname local0. "${logspacing}-- resolveFields: index '$resolve_index' has a decimal bytelength field" }
- set resolve_index_datalength [expr $resolve_index_bytelength*2]
- }
- else {
- if { "$resolve_index_bytelength" contains "\[" } {
- if { $debug == 9 } { log -noname local0. "${logspacing}-- resolveFields: index '$resolve_index' has a calculated (formula) bytelength field" }
- eval "set tmp_resolve_index_bytelength $resolve_index_bytelength"
- set resolve_index_datalength [expr ($tmp_resolve_index_bytelength-($offset/2))*2]
- }
- else {
- if { $debug == 9 } { log -noname local0. "${logspacing}-- resolveFields: index '$resolve_index' references another field's value as its length" }
- set resolve_findlengthfield [lsearch $lst_lengthfields "${resolve_index_bytelength}_*"]
- if { !($resolve_findlengthfield == "") } {
- if { $debug == 9 } { log -noname local0. "${logspacing}-- resolveFields: the referenced length field '${resolve_index_bytelength}' for index '$resolve_index' was found" }
- set resolve_index_datalength [expr [lindex [split [lindex $lst_lengthfields [lsearch $lst_lengthfields "${resolve_index_bytelength}_*"]] "_"] 1]*2]
- }
- else {
- if { $debug == 9 } { log -noname local0. "${logspacing}-- resolveFields: the referenced length field '${resolve_index_bytelength}' for index '$resolve_index' could not be found" }
- }
- }
- }
- set resolve_index_data_rawvalue [substr $data $offset $resolve_index_datalength]
- set resolve_index_data_readablevalue "n/a"
- set nodecoder 0
- eval "if \{ \[catch \{ set resolve_index_data_readablevalue \[call ProtocolProfilerDecoders::decode_$resolve_index_format {$resolve_index_fielddefinition} \"$resolve_index_data_rawvalue\"\] \} errmsg\] \} \{ set nodecoder 1 \}"
- if { $nodecoder == 1 || $resolve_index_data_readablevalue == "Unknown" } {
- set resolve_index_data_readablevalue "(0x$resolve_index_data_rawvalue) Unknown"
- }
- set offset [expr $offset+$resolve_index_datalength]
- if { $debug == 1 } { log -noname local0. "$logspacing-- resolveFields: $resolve_index_fieldname = $resolve_index_data_readablevalue" }
- if { $resolve_index_fieldname ends_with "length" } {
- lappend lst_lengthfields "${resolve_index_fieldname}_$resolve_index_data_readablevalue"
- }
- lappend returnvalue "$resolve_index_fieldname = $resolve_index_data_readablevalue"
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing}- resolveFields: index '$resolve_index' doet not have a resolvable field definition" }
- }
- }
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing} resolveFields: could not find any potential indexes that are siblings to index '$index'" }
- }
- return "$offset {$lst_lengthfields} $returnvalue"
- }
- ### getFieldDefinitionByFieldname { index data offset logspacing lst_pmap debug }
- # Description (very short):
- # - mapProtocol is called for a specific index; this is its starting point
- # - 1) It verifies if this index has a valid field definition (minimum of 3 fields in the definition, checked by proc getFieldDefinitionByIndex).
- # - If the index has no valid field definition, it gets a list of its siblings and tries to resolve the fields that have a valid field definition.
- # - However, if the index does have a valid field definition, it checks if it's being used as a marker by children of this index.
- # - 2) If it's being used as a marker by one or more of its children, it will resolve the index to its value and advance the offset with its length
- # - If it's being used as a marker, after having resolved the field, it will it will try to resolve the siblings of the marker level further advancing the offset as required.
- # - Once finished, it will change the starting point to within the marker level by calling itself (mapProtocol) on the matching marker index.
- # Arguments:
- # - index => an index string
- # - data => the actual protocol data as seen on the wire
- # - offset => the offset at which to start reading the protocol data
- # - logspacing => usually just a space, used to trace the recursion of the function visually by enabling debug = 2
- # - lst_pmap => the protocol map
- # - debug => 0 = no debug logging, 1 = debug logging
- # Returns:
- # - nothing
- proc mapProtocol { index data offset lst_lengthfields logspacing lst_pmap debug } {
- # create the protocol map array from the protocol map list
- array set pmap $lst_pmap
- if { $debug == 1 } { log -noname local0. "${logspacing}- mapProtocol on index '$index' with offset '$offset'" }
- set lst_indexestoresolve {}
- set marker_found 0
- set marker_found_index ""
- set newoffset $offset
- # resolveMarkers
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: detecting markers" }
- set lst_markerindexestoresolve [call ProtocolProfilerProcs::getSiblingIndexes $index $lst_pmap $debug]
- set lst_markerindexestoresolve_length [llength $lst_markerindexestoresolve]
- set fields_resolved 0
- if { $lst_markerindexestoresolve_length > 0 } {
- foreach { resolve_markerindex } $lst_markerindexestoresolve {
- set lst_markerresults [call ProtocolProfilerProcs::resolveMarker $resolve_markerindex $data $newoffset $lst_pmap "${logspacing}---" $debug]
- if { [llength $lst_markerresults] == 2 } {
- #set offset [lindex $lst_markerresults 0]
- set marker_found_index [lindex $lst_markerresults 1]
- # resolveFields (only once for the current index level; this is to avoid an n+1'th marker from resolving the same fields when it has already been done by the first marker)
- if { $fields_resolved == 0 } {
- set fields_resolved 1
- if { $debug == 1 } { log -noname local0. "${logspacing}--- mapProtocol: resolving fields on the same level as the marker '$index'" }
- set lst_fields {}
- set lst_fields [call ProtocolProfilerProcs::resolveFields $index $data $newoffset $lst_lengthfields $lst_pmap "${logspacing}---" $debug]
- if { [llength $lst_fields] > 0 } {
- set lst_fields_cnt 0
- foreach { field } $lst_fields {
- if { $lst_fields_cnt > 1 } {
- log -noname local0. "$field"
- }
- incr lst_fields_cnt
- }
- }
- set newoffset [lindex $lst_fields 0]
- set lst_lengthfields [lindex $lst_fields 1]
- }
- # mapProtocol on the found marker
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: mapping detected marker '$marker_found_index'" }
- call ProtocolProfilerProcs::mapProtocol $marker_found_index $data $newoffset $lst_lengthfields "$logspacing " $lst_pmap $debug
- }
- }
- }
- # resolveFields when it hasn't been done yet through marker resolving (only happens when no markers were found on this level)
- if { $offset == $newoffset } {
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: no markers on this level so resolving fields on the same level as '$index'" }
- set lst_fields {}
- set lst_fields [call ProtocolProfilerProcs::resolveFields $index $data $offset $lst_lengthfields $lst_pmap "${logspacing}---" $debug]
- if { [llength $lst_fields] > 0 } {
- set lst_fields_cnt 0
- foreach { field } $lst_fields {
- if { $lst_fields_cnt > 1 } {
- log -noname local0. "$field"
- }
- incr lst_fields_cnt
- }
- }
- set offset [lindex $lst_fields 0]
- # mapProtocol on the next sublevel
- if { [call ProtocolProfilerProcs::isParentIndex $index $lst_pmap $debug] } {
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: nothing left on level '$index'; mapping one level down: '${index}_0'" }
- call ProtocolProfilerProcs::mapProtocol ${index}_0 $data $offset $lst_lengthfields "$logspacing " $lst_pmap $debug
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: nothing left on level '$index'; nothing on level '${index}_0' => END" }
- }
- }
- else {
- if { $debug == 1 } { log -noname local0. "${logspacing}-- mapProtocol: markers were found on this level, they have all been handled => END" }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement