diff --git a/ChangeLog b/ChangeLog index 5c3e6a6..7622d62 100755 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2016-11-25 +- Add latency extension to nfpcapd +- Smaller bug fixes to nfpcapd + 2016-07-23 - Replace unreliable _ftok with more reliable string hash diff --git a/README.md b/README.md index 25ca9ba..7034b06 100755 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ NF_N_NAT_OUTSIDE_GLOBAL_IPV4 | 226 NF_N_POST_NAPT_SRC_PORT | 227 NF_N_POST_NAPT_DST_PORT | 228 | -__nprobe latency extensions__| +__latency extensions for nfpcapd and nprobe__| NF9_NPROBE_CLIENT_NW_DELAY_SEC | 57554 NF9_NPROBE_CLIENT_NW_DELAY_USEC | 57555 NF9_NPROBE_SERVER_NW_DELAY_SEC | 57556 diff --git a/bin/flowtree.c b/bin/flowtree.c index 434fb66..cd5f60d 100644 --- a/bin/flowtree.c +++ b/bin/flowtree.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * Copyright (c) 2011, Peter Haag * All rights reserved. @@ -27,12 +28,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Author$ - * - * $Id$ - * - * $LastChangedRevision$ - * */ #include "config.h" @@ -59,10 +54,12 @@ #include "flowtree.h" #include "netflow_pcap.h" -#ifndef DEVEL -# define dbg_printf(...) /* printf(__VA_ARGS__) */ -#else +#ifdef DEVEL # define dbg_printf(...) printf(__VA_ARGS__) +# define dbg_assert(a) assert(a) +#else +# define dbg_printf(...) /* printf(__VA_ARGS__) */ +# define dbg_assert(a) /* assert(a) */ #endif static int FlowNodeCMP(struct FlowNode *e1, struct FlowNode *e2); @@ -93,15 +90,6 @@ typedef struct FlowNode_list_s { uint32_t size; } Linked_list_t; -static Linked_list_t UDP_list; - -/* static prototypes */ -static void AppendFlowNode(Linked_list_t *LinkedList, struct FlowNode *node); - -static void DisconnectFlowNode(Linked_list_t *LinkedList, struct FlowNode *node); - -static void TouchFlowNode(Linked_list_t *LinkedList, struct FlowNode *node); - /* Free list handling functions */ // Get next free node from free list struct FlowNode *New_Node(void) { @@ -169,10 +157,8 @@ void Free_Node(struct FlowNode *node) { node->data = NULL; } -#ifdef DEVEL - assert(node->left == NULL); - assert(node->right == NULL); -#endif + dbg_assert(node->left == NULL); + dbg_assert(node->right == NULL); #ifdef USE_MALLOC dbg_printf("Free node: %llx\n", (unsigned long long)node); @@ -242,10 +228,6 @@ int i; Allocated = 0; NumFlows = 0; - UDP_list.list = NULL; - UDP_list.tail = NULL; - UDP_list.size = 0; - return 1; } // End of Init_FlowTree @@ -285,6 +267,9 @@ struct FlowNode *Lookup_Node(struct FlowNode *node) { struct FlowNode *Insert_Node(struct FlowNode *node) { struct FlowNode *n; +dbg_assert(node->left == NULL); +dbg_assert(node->right == NULL); + // return RB_INSERT(FlowTree, FlowTree, node); n = RB_INSERT(FlowTree, FlowTree, node); if ( n ) { // existing node @@ -293,9 +278,10 @@ struct FlowNode *n; NumFlows++; return NULL; } -} // End of Lookup_FlowTree +} // End of Insert_Node void Remove_Node(struct FlowNode *node) { +struct FlowNode *rev_node; #ifdef DEVEL assert(node->memflag == NODE_IN_USE); @@ -305,11 +291,53 @@ void Remove_Node(struct FlowNode *node) { } #endif + rev_node = node->rev_node; + if ( rev_node ) { + // unlink rev node on both nodes + dbg_assert(rev_node->rev_node == node); + rev_node->rev_node = NULL; + node->rev_node = NULL; + } RB_REMOVE(FlowTree, FlowTree, node); Free_Node(node); NumFlows--; -} // End of Lookup_FlowTree +} // End of Remove_Node + +int Link_RevNode(struct FlowNode *node) { +struct FlowNode lookup_node, *rev_node; + + dbg_printf("Link node: "); + dbg_assert(node->rev_node == NULL); + lookup_node.src_addr = node->dst_addr; + lookup_node.dst_addr = node->src_addr; + lookup_node.src_port = node->dst_port; + lookup_node.dst_port = node->src_port; + lookup_node.version = node->version; + lookup_node.proto = node->proto; + rev_node = Lookup_Node(&lookup_node); + if ( rev_node ) { + dbg_printf("Found revnode "); + // rev node must not be linked already - otherwise there is an inconsistency + dbg_assert(node->rev_node == NULL); + if (node->rev_node == NULL ) { + // link both nodes + node->rev_node = rev_node; + rev_node->rev_node = node; + dbg_printf(" - linked\n"); + } else { + dbg_printf("Rev-node != NULL skip linking - inconsitency\n"); + LogError("Rev-node != NULL skip linking - inconsitency\n"); + } + return 1; + } else { + dbg_printf("no revnode node\n"); + return 0; + } + + /* not reached */ + +} // End of Link_RevNode uint32_t Flush_FlowTree(FlowSource_t *fs) { struct FlowNode *node, *nxt; @@ -333,66 +361,10 @@ if ( node->left || node->right ) { LogError("### Flush_FlowTree() remaining flows: %u\n", NumFlows); #endif - UDP_list.list = NULL; - UDP_list.tail = NULL; - UDP_list.size = 0; - return n; } // End of Flush_FlowTree -void UDPexpire(FlowSource_t *fs, time_t t_expire) { -struct FlowNode *node; -uint32_t num = 0; - - node = UDP_list.list; - while ( node && (node->t_last.tv_sec < t_expire) ) { - struct FlowNode *n = node; - node = node->right; - DisconnectFlowNode(&UDP_list, n); - StorePcapFlow(fs, n); - Remove_Node(n); - num++; - } - dbg_printf("UDP expired %u flows - left %u\n", num, UDP_list.size); - -} // End of UDPexpire - -void AppendUDPNode(struct FlowNode *node) { - AppendFlowNode(&UDP_list, node); -} // End of AppendUDPNode - -static void AppendFlowNode(Linked_list_t *LinkedList, struct FlowNode *node) { - -#ifdef DEVEL - if ( LinkedList->tail ) - assert(LinkedList->tail->right == NULL); - assert(node->memflag == NODE_IN_USE); - assert(node->left == NULL); - assert(node->right == NULL); -#endif - if ( LinkedList->list == NULL ) { - dbg_printf("AppendFlowNode(): First node\n"); - node->left = NULL; - node->right = NULL; - LinkedList->list = node; - LinkedList->tail = node; - LinkedList->size++; - } else { - // new node - dbg_printf("AppendFlowNode(): next node: %u\n", LinkedList->size); - LinkedList->tail->right = node; - node->left = LinkedList->tail; - node->right = NULL; - LinkedList->tail = node; - LinkedList->size++; - } -#ifdef DEVEL - assert(LinkedList->tail->right == NULL); -#endif -} // End of AppendFlowNode - - static void DisconnectFlowNode(Linked_list_t *LinkedList, struct FlowNode *node) { if ( node == NULL ) @@ -421,44 +393,6 @@ static void DisconnectFlowNode(Linked_list_t *LinkedList, struct FlowNode *node) } // End of DisconnectFlowNode -void TouchUDPNode(struct FlowNode *node) { - TouchFlowNode(&UDP_list, node); -} // End of TouchUDPNode - -static void TouchFlowNode(Linked_list_t *LinkedList, struct FlowNode *node) { - - dbg_printf("In TochFlowNode()\n"); - if ( LinkedList->list == NULL ) { - // should never happen - LogError("TouchFlowNode() error in %s line %d: %s\n", __FILE__, __LINE__, "Tried to touch node in empty list" ); - return; - } - - if ( LinkedList->tail == node ) { - // nothing to do - dbg_printf("TochFlowNode() - last node - nothing to do\n"); - return; - } - - if ( node->left == NULL ) { - // first node - disconnect node - dbg_printf("TochFlowNode() - touch first node\n"); - LinkedList->list = node->right; - LinkedList->list->left = NULL; - } else { - dbg_printf("TochFlowNode() - touch middle node\n"); - (node->right)->left = node->left; - (node->left)->right = node->right; - } - - // append node - LinkedList->tail->right = node; - node->left = LinkedList->tail; - node->right = NULL; - LinkedList->tail = node; - -} // End of TouchFlowNode - int AddNodeData(struct FlowNode *node, uint32_t seq, void *payload, uint32_t size) { return 0; @@ -497,6 +431,7 @@ void DisposeNodeList(NodeList_t *NodeList) { } // End of DisposeNodeList #ifdef DEVEL +void ListCheck(NodeList_t *NodeList); void ListCheck(NodeList_t *NodeList) { uint32_t len = 0, mem = 0, proto; static uint32_t loops = 0; @@ -619,7 +554,7 @@ struct FlowNode *node; void DumpNodeStat(void) { LogInfo("Nodes in use: %u, Flows: %u CacheOverflow: %u", Allocated, NumFlows, CacheOverflow); -} // End of NodesAllocated +} // End of DumpNodeStat /* int main(int argc, char **argv) { diff --git a/bin/flowtree.h b/bin/flowtree.h index 7b1f130..3f5e41e 100644 --- a/bin/flowtree.h +++ b/bin/flowtree.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * Copyright (c) 2011, Peter Haag * All rights reserved. @@ -27,12 +28,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Author$ - * - * $Id$ - * - * $LastChangedRevision$ - * */ #include "rbtree.h" @@ -86,8 +81,15 @@ struct FlowNode { #define DATABLOCKSIZE 256 uint32_t DataSize; // max size of data buffer void *data; // start of data buffer -// uint32_t eodata; // offset last byte in buffer - + + struct FlowNode *rev_node; + struct latency_s { + uint64_t client; + uint64_t server; + uint64_t application; + uint32_t flag; + struct timeval t_request; + } latency; }; typedef struct NodeList_s { @@ -125,6 +127,8 @@ struct FlowNode *Insert_Node(struct FlowNode *node); void Remove_Node(struct FlowNode *node); +int Link_RevNode(struct FlowNode *node); + // Node list functions NodeList_t *NewNodeList(void); @@ -136,13 +140,6 @@ struct FlowNode *Pop_Node(NodeList_t *NodeList, int *done); void DumpList(NodeList_t *NodeList); -// Liked lists -void AppendUDPNode(struct FlowNode *node); - -void TouchUDPNode(struct FlowNode *node); - -void UDPexpire(FlowSource_t *fs, time_t t_expire); - // Stat functions void DumpNodeStat(void); diff --git a/bin/netflow_pcap.c b/bin/netflow_pcap.c index 5c81394..01f4a20 100644 --- a/bin/netflow_pcap.c +++ b/bin/netflow_pcap.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * Copyright (c) 2013, Peter Haag * All rights reserved. @@ -27,13 +28,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Author:$ - * - * $Id:$ - * - * $LastChangedRevision:$ - * - * */ #include "config.h" @@ -101,7 +95,7 @@ typedef struct pcap_v6_block_s { #define PCAP_V6_BLOCK_DATA_SIZE (sizeof(pcap_v6_block_t) - sizeof(uint32_t)) // All required extension to save full pcap records -static uint16_t pcap_full_map[] = { 0 }; +static uint16_t pcap_full_map[] = { EX_LATENCY, 0 }; #include "nffile_inline.c" @@ -251,6 +245,13 @@ void *data_ptr; tpl->output = 0; data_ptr = (void *)tpl->data; } break; + case EX_LATENCY: { // latecy extension + tpl_ext_latency_t *tpl = (tpl_ext_latency_t *)data_ptr; + tpl->client_nw_delay_usec = Node->latency.client; + tpl->server_nw_delay_usec = Node->latency.server; + tpl->appl_latency_usec = Node->latency.application; + data_ptr = (void *)tpl->data; + } break; default: // this should never happen, as pcap has no other extensions LogError("Process_pcap: Unexpected extension %i for pcap record. Skip extension", id); @@ -350,3 +351,58 @@ void *data_ptr; } /* End of StorePcapFlow */ +// Server latency = t(SYN Server) - t(SYN CLient) +void SetServer_latency(struct FlowNode *node) { +struct FlowNode *Client_node; +uint64_t latency; + + Client_node = node->rev_node; + latency = ((uint64_t)node->t_first.tv_sec * (uint64_t)1000000 + (uint64_t)node->t_first.tv_usec) - + ((uint64_t)Client_node->t_first.tv_sec * (uint64_t)1000000 + (uint64_t)Client_node->t_first.tv_usec); + + node->latency.server = latency; + Client_node->latency.server = latency; + // set flag, to calc client latency with nex packet from client + Client_node->latency.flag = 1; + dbg_printf("Server latency: %llu\n", (long long unsigned)latency); + +} // End of SetServerClient_latency + +// Client latency = t(ACK CLient) - t(SYN Server) +void SetClient_latency(struct FlowNode *node, struct timeval *t_packet) { +struct FlowNode *Server_node; +uint64_t latency; + + Server_node = node->rev_node; + latency = ((uint64_t)t_packet->tv_sec * (uint64_t)1000000 + (uint64_t)t_packet->tv_usec) - + ((uint64_t)Server_node->t_first.tv_sec * (uint64_t)1000000 + (uint64_t)Server_node->t_first.tv_usec); + + node->latency.client = latency; + Server_node->latency.client = latency; + // reset flag + node->latency.flag = 0; + // set flag, to calc application latency with nex packet from server + Server_node->latency.flag = 2; + Server_node->latency.t_request = *t_packet; + dbg_printf("Client latency: %llu\n", (long long unsigned)latency); + +} // End of SetClient_latency + +// Application latency = t(ACK Server) - t(ACK CLient) +void SetApplication_latency(struct FlowNode *node, struct timeval *t_packet) { +struct FlowNode *Client_node; +uint64_t latency; + + Client_node = node->rev_node; + latency = ((uint64_t)t_packet->tv_sec * (uint64_t)1000000 + (uint64_t)t_packet->tv_usec) - + ((uint64_t)node->latency.t_request.tv_sec * (uint64_t)1000000 + (uint64_t)node->latency.t_request.tv_usec); + + node->latency.application = latency; + Client_node->latency.application = latency; + // reset flag + node->latency.flag = 0; + dbg_printf("Application latency: %llu\n", (long long unsigned)latency); + +} // End of SetApplication_latency + + diff --git a/bin/netflow_pcap.h b/bin/netflow_pcap.h index b3e47b4..a52dd4a 100644 --- a/bin/netflow_pcap.h +++ b/bin/netflow_pcap.h @@ -40,3 +40,9 @@ int Init_pcap2nf(void); int StorePcapFlow(FlowSource_t *fs, struct FlowNode *Node); +void SetServer_latency(struct FlowNode *node); + +void SetClient_latency(struct FlowNode *node, struct timeval *t_packet); + +void SetApplication_latency(struct FlowNode *node, struct timeval *t_packet); + diff --git a/bin/nf_common.c b/bin/nf_common.c index bfe5113..4793f90 100644 --- a/bin/nf_common.c +++ b/bin/nf_common.c @@ -378,7 +378,7 @@ static struct format_token_list_s { { "%pbsize", 0, "Pb-Size", String_PortBlockSize}, // Port block size #endif - // nprobe latency + // latency extension for nfpcapd and nprobe { "%cl", 0, "C Latency", String_ClientLatency }, // client latency { "%sl", 0, "S latency", String_ServerLatency }, // server latency { "%al", 0, "A latency", String_AppLatency }, // app latency diff --git a/bin/nffile.h b/bin/nffile.h index 97cba7c..fd646cc 100644 --- a/bin/nffile.h +++ b/bin/nffile.h @@ -1112,7 +1112,7 @@ typedef struct tpl_ext_43_s { #define EX_NSEL_RESERVED 44 /* - * nprobe extensions + * latency extensions, used by nprobe and nfpcapd */ /* @@ -2046,7 +2046,6 @@ typedef struct master_record_s { #endif - // nprobe extensions // latency extension uint64_t client_nw_delay_usec; // index LATENCY_BASE_OFFSET 0xffff'ffff'ffff'ffff uint64_t server_nw_delay_usec; // index LATENCY_BASE_OFFSET + 1 0xffff'ffff'ffff'ffff diff --git a/bin/nfpcapd.c b/bin/nfpcapd.c index 070b40c..3d9e941 100644 --- a/bin/nfpcapd.c +++ b/bin/nfpcapd.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * Copyright (c) 2013, Peter Haag * All rights reserved. @@ -27,16 +28,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Author$ - * - * $Id$ - * - * $LastChangedRevision$ - * */ -/* $Id: pcapd.c 2778 2012-03-19 09:23:26Z roethlis $ */ - #include "config.h" #ifdef HAVE_FEATURES_H @@ -62,6 +55,7 @@ #include #include #include +#include #ifdef HAVE_STDINT_H #include @@ -618,7 +612,7 @@ int compress = args->compress; FlowSource_t *fs = args->fs; // locals -time_t t_start, t_clock, t_udp_flush; +time_t t_start, t_clock; int err, done; done = 0; @@ -659,7 +653,6 @@ int err, done; t_start = 0; t_clock = 0; - t_udp_flush = 0; while ( 1 ) { struct FlowNode *Node; @@ -673,7 +666,7 @@ int err, done; } if ( t_start == 0 ) { - t_udp_flush = t_start = t_clock - (t_clock % t_win); + t_start = t_clock - (t_clock % t_win); } if (((t_clock - t_start) >= t_win) || done) { /* rotate file */ @@ -796,11 +789,6 @@ int err, done; } } - if (((t_clock - t_udp_flush) >= 10) || !done) { /* flush inactive UDP list */ - UDPexpire(fs, t_clock - 10 ); - t_udp_flush = t_clock; - } - if ( Node->fin != SIGNAL_NODE ) // Process the Node ProcessFlowNode(fs, Node); diff --git a/bin/nfx.c b/bin/nfx.c index fa84afe..7069280 100755 --- a/bin/nfx.c +++ b/bin/nfx.c @@ -124,8 +124,8 @@ extension_descriptor_t extension_descriptor[] = { { EX_NSEL_RESERVED, 0, 0, 0, NULL}, - // nprobe extensions - { EX_LATENCY, 24, 64, 0, "nprobe latency"}, + // latency extension for nfpcapd and nprobe + { EX_LATENCY, 24, 64, 0, "nprobe/nfpcapd latency"}, // NAT - Network Event Logging { EX_NEL_COMMON, 12, 31, 0, "NEL Common block"}, diff --git a/bin/parse_csv.pl b/bin/parse_csv.pl index 48cb07c..f81039b 100755 --- a/bin/parse_csv.pl +++ b/bin/parse_csv.pl @@ -1,5 +1,6 @@ #!/usr/bin/perl # +# Copyright (c) 2016, Peter Haag # Copyright (c) 2009, Peter Haag # All rights reserved. # @@ -26,11 +27,6 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # -# $Author: haag $ -# -# $Id: parse_csv.pl 33 2009-09-30 08:30:37Z haag $ -# -# $LastChangedRevision: 33 $ # use strict; @@ -85,7 +81,7 @@ my @tags; # ismc,odmc input src, output dst MAC # idmc,osmc input dst, output src MAC # mpls1,mpls2,mpls3,mpls4,mpls5,mpls6,mpls7,mpls8,mpls9,mpls10 MPLS label 1-10 -# cl,sl,al client server application latency (nprobe) +# cl,sl,al client server application latency (nfpcapd,nprobe) # ra router IP # eng router engine type/id # exid exporter SysID diff --git a/bin/pcaproc.c b/bin/pcaproc.c index 7607896..d45a42c 100644 --- a/bin/pcaproc.c +++ b/bin/pcaproc.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016, Peter Haag * Copyright (c) 2014, Peter Haag * All rights reserved. * @@ -26,11 +27,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $Author$ - * - * $Id$ - * - * $LastChangedRevision$ * */ @@ -265,8 +261,9 @@ struct FlowNode *Node; assert(NewNode->memflag == NODE_IN_USE); Node = Insert_Node(NewNode); - // if insert fails, the existing node is returned -> flow exists already + // Return existing Node if flow exists already, otherwise insert es new if ( Node == NULL ) { + // Insert as new dbg_printf("New TCP flow: Packets: %u, Bytes: %u\n", NewNode->packets, NewNode->bytes); // in case it's a FIN/RST only packet - immediately flush it @@ -283,11 +280,23 @@ struct FlowNode *Node; NumFlows = Flush_FlowTree(fs); LogError("Flushed flows: %u", NumFlows); } + + if ( Link_RevNode(NewNode)) { + // if we could link this new node, it is the server answer + // -> calculate server latency + SetServer_latency(NewNode); + } return; } assert(Node->memflag == NODE_IN_USE); + // check for first client ACK for client latency + if ( Node->latency.flag == 1 ) { + SetClient_latency(Node, &(NewNode->t_first)); + } else if ( Node->latency.flag == 2 ) { + SetApplication_latency(Node, &(NewNode->t_first)); + } // update existing flow Node->flags |= NewNode->flags; Node->packets++; @@ -323,7 +332,6 @@ struct FlowNode *Node; Node = Insert_Node(NewNode); // if insert fails, the existing node is returned -> flow exists already if ( Node == NULL ) { - AppendUDPNode(NewNode); dbg_printf("New UDP flow: Packets: %u, Bytes: %u\n", NewNode->packets, NewNode->bytes); return; } @@ -333,7 +341,6 @@ struct FlowNode *Node; Node->packets++; Node->bytes += NewNode->bytes; Node->t_last = NewNode->t_last; - TouchUDPNode(Node); dbg_printf("Existing UDP flow: Packets: %u, Bytes: %u\n", Node->packets, Node->bytes); @@ -643,7 +650,6 @@ pkt->vlans[pkt->vlan_count].pcp = (p[0] >> 5) & 7; if ( Node->src_port == 53 || Node->dst_port == 53 ) content_decode_dns(Node, payload, payload_len); } - Push_Node(NodeList, Node); } break; case IPPROTO_TCP: { diff --git a/man/nfcapd.1 b/man/nfcapd.1 index 9850c7e..cd88c47 100755 --- a/man/nfcapd.1 +++ b/man/nfcapd.1 @@ -193,9 +193,9 @@ NEL/NAT extensions .P .P -nprobe extensions +latency extension .P -64 nprobe client/server/application latency"}, +64 nfpcapd/nprobe client/server/application latency"}, .B IMPORTANT: By default only extension 1 and 2 are selected