Add latency extension to nfpcapd. Streamline nfpcapd.

This commit is contained in:
Peter Haag 2016-11-26 12:17:16 +01:00
parent aaa5c69092
commit 4aa127265d
13 changed files with 172 additions and 185 deletions

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 <errno.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
@ -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);

View File

@ -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"},

View File

@ -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

View File

@ -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: {

View File

@ -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