You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-unittest-helper.c

1176 lines
32 KiB
C

/* Copyright (C) 2007-2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Pablo Rincon Crespo <pablo.rincon.crespo@gmail.com>
*
* This file provide a set of helper functions for reducing the complexity
* when constructing unittests
*/
#include "suricata-common.h"
#include "decode.h"
#include "flow-private.h"
#include "flow-util.h"
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
#include "flow-spare-pool.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-alert.h"
#include "detect-engine-sigorder.h"
#include "detect-engine-build.h"
#include "stream-tcp.h"
#include "stream-tcp-private.h"
#include "util-debug.h"
#include "util-time.h"
#include "util-error.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#if defined(UNITTESTS) || defined(FUZZ)
Flow *TestHelperBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)
{
struct in_addr in;
Flow *f = SCMalloc(sizeof(Flow));
if (unlikely(f == NULL)) {
printf("FlowAlloc failed\n");
;
return NULL;
}
memset(f, 0x00, sizeof(Flow));
FLOW_INITIALIZE(f);
if (family == AF_INET) {
f->flags |= FLOW_IPV4;
} else if (family == AF_INET6) {
f->flags |= FLOW_IPV6;
}
if (src != NULL) {
if (family == AF_INET) {
if (inet_pton(AF_INET, src, &in) != 1) {
printf("invalid address %s\n", src);
SCFree(f);
return NULL;
}
f->src.addr_data32[0] = in.s_addr;
} else {
BUG_ON(1);
}
}
if (dst != NULL) {
if (family == AF_INET) {
if (inet_pton(AF_INET, dst, &in) != 1) {
printf("invalid address %s\n", dst);
SCFree(f);
return NULL;
}
f->dst.addr_data32[0] = in.s_addr;
} else {
BUG_ON(1);
}
}
f->sp = sp;
f->dp = dp;
return f;
}
/** \brief writes the contents of a buffer into a file */
int TestHelperBufferToFile(const char *name, const uint8_t *data, size_t size)
{
if (remove(name) != 0) {
if (errno != ENOENT) {
printf("failed remove, errno=%d\n", errno);
return -1;
}
}
FILE *fd = fopen(name, "wb");
if (fd == NULL) {
printf("failed open, errno=%d\n", errno);
return -2;
}
if (fwrite (data, 1, size, fd) != size) {
fclose(fd);
return -3;
}
fclose(fd);
return 0;
}
#endif
#ifdef UNITTESTS
/**
* \brief return the uint32_t for a ipv4 address string
*
* \param str Valid ipaddress in string form (e.g. 1.2.3.4)
*
* \retval uint the uin32_t representation
*/
uint32_t UTHSetIPv4Address(const char *str)
{
struct in_addr in;
if (inet_pton(AF_INET, str, &in) != 1) {
printf("invalid IPv6 address %s\n", str);
exit(EXIT_FAILURE);
}
return (uint32_t)in.s_addr;
}
/**
* \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
* specifying ip and port sources and destinations (IPV6)
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
* \param src pointer to a string containing the ip source
* \param dst pointer to a string containing the ip destination
* \param sport pointer to a string containing the port source
* \param dport pointer to a string containing the port destination
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacketIPV6Real(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto, const char *src, const char *dst,
uint16_t sport, uint16_t dport)
{
uint32_t in[4];
Packet *p = PacketGetFromAlloc();
if (unlikely(p == NULL))
return NULL;
p->ts = TimeGet();
p->src.family = AF_INET6;
p->dst.family = AF_INET6;
p->payload = payload;
p->payload_len = payload_len;
p->proto = ipproto;
p->ip6h = SCMalloc(sizeof(IPV6Hdr));
if (p->ip6h == NULL)
goto error;
memset(p->ip6h, 0, sizeof(IPV6Hdr));
p->ip6h->s_ip6_nxt = ipproto;
p->ip6h->s_ip6_plen = htons(payload_len + sizeof(TCPHdr));
if (inet_pton(AF_INET6, src, &in) != 1)
goto error;
p->src.addr_data32[0] = in[0];
p->src.addr_data32[1] = in[1];
p->src.addr_data32[2] = in[2];
p->src.addr_data32[3] = in[3];
p->sp = sport;
p->ip6h->s_ip6_src[0] = in[0];
p->ip6h->s_ip6_src[1] = in[1];
p->ip6h->s_ip6_src[2] = in[2];
p->ip6h->s_ip6_src[3] = in[3];
if (inet_pton(AF_INET6, dst, &in) != 1)
goto error;
p->dst.addr_data32[0] = in[0];
p->dst.addr_data32[1] = in[1];
p->dst.addr_data32[2] = in[2];
p->dst.addr_data32[3] = in[3];
p->dp = dport;
p->ip6h->s_ip6_dst[0] = in[0];
p->ip6h->s_ip6_dst[1] = in[1];
p->ip6h->s_ip6_dst[2] = in[2];
p->ip6h->s_ip6_dst[3] = in[3];
p->tcph = SCMalloc(sizeof(TCPHdr));
if (p->tcph == NULL)
goto error;
memset(p->tcph, 0, sizeof(TCPHdr));
p->tcph->th_sport = htons(sport);
p->tcph->th_dport = htons(dport);
SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(TCPHdr) + payload_len);
return p;
error:
if (p != NULL) {
if (p->ip6h != NULL) {
SCFree(p->ip6h);
}
if (p->tcph != NULL) {
SCFree(p->tcph);
}
SCFree(p);
}
return NULL;
}
/**
* \brief UTHBuildPacketReal is a function that create tcp/udp packets for unittests
* specifying ip and port sources and destinations
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
* \param src pointer to a string containing the ip source
* \param dst pointer to a string containing the ip destination
* \param sport pointer to a string containing the port source
* \param dport pointer to a string containing the port destination
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto, const char *src, const char *dst,
uint16_t sport, uint16_t dport)
{
struct in_addr in;
Packet *p = PacketGetFromAlloc();
if (unlikely(p == NULL))
return NULL;
p->ts = TimeGet();
p->src.family = AF_INET;
p->dst.family = AF_INET;
p->payload = payload;
p->payload_len = payload_len;
p->proto = ipproto;
if (inet_pton(AF_INET, src, &in) != 1)
goto error;
p->src.addr_data32[0] = in.s_addr;
p->sp = sport;
if (inet_pton(AF_INET, dst, &in) != 1)
goto error;
p->dst.addr_data32[0] = in.s_addr;
p->dp = dport;
p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
if (p->ip4h == NULL)
goto error;
p->ip4h->s_ip_src.s_addr = p->src.addr_data32[0];
p->ip4h->s_ip_dst.s_addr = p->dst.addr_data32[0];
p->ip4h->ip_proto = ipproto;
p->ip4h->ip_verhl = sizeof(IPV4Hdr);
p->proto = ipproto;
int hdr_offset = sizeof(IPV4Hdr);
switch (ipproto) {
case IPPROTO_UDP:
p->udph = (UDPHdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
if (p->udph == NULL)
goto error;
p->udph->uh_sport = sport;
p->udph->uh_dport = dport;
hdr_offset += sizeof(UDPHdr);
break;
case IPPROTO_TCP:
p->tcph = (TCPHdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
if (p->tcph == NULL)
goto error;
p->tcph->th_sport = htons(sport);
p->tcph->th_dport = htons(dport);
hdr_offset += sizeof(TCPHdr);
break;
case IPPROTO_ICMP:
p->icmpv4h = (ICMPV4Hdr *)(GET_PKT_DATA(p) + sizeof(IPV4Hdr));
if (p->icmpv4h == NULL)
goto error;
hdr_offset += sizeof(ICMPV4Hdr);
break;
default:
break;
/* TODO: Add more protocols */
}
if (payload && payload_len) {
PacketCopyDataOffset(p, hdr_offset, payload, payload_len);
}
SET_PKT_LEN(p, hdr_offset + payload_len);
p->payload = GET_PKT_DATA(p)+hdr_offset;
return p;
error:
SCFree(p);
return NULL;
}
/**
* \brief UTHBuildPacket is a wrapper that build packets with default ip
* and port fields
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacket(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto)
{
return UTHBuildPacketReal(payload, payload_len, ipproto,
"192.168.1.5", "192.168.1.1",
41424, 80);
}
/**
* \brief UTHBuildPacketArrayFromEth is a wrapper that build a packets from an array of
* packets in ethernet rawbytes. Hint: It also share the flows.
*
* \param raw_eth pointer to the array of ethernet packets in rawbytes
* \param pktsize pointer to the array of sizes corresponding to each buffer pointed
* from pktsize.
* \param numpkts number of packets in the array
*
* \retval Packet pointer to the array of built in packets; NULL if something fail
*/
Packet **UTHBuildPacketArrayFromEth(uint8_t *raw_eth[], int *pktsize, int numpkts)
{
DecodeThreadVars dtv;
ThreadVars th_v;
if (raw_eth == NULL || pktsize == NULL || numpkts <= 0) {
SCLogError("The arrays cant be null, and the number"
" of packets should be grater thatn zero");
return NULL;
}
Packet **p = NULL;
p = SCMalloc(sizeof(Packet *) * numpkts);
if (unlikely(p == NULL))
return NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
int i = 0;
for (; i < numpkts; i++) {
p[i] = PacketGetFromAlloc();
if (p[i] == NULL) {
SCFree(p);
return NULL;
}
DecodeEthernet(&th_v, &dtv, p[i], raw_eth[i], pktsize[i]);
}
return p;
}
/**
* \brief UTHBuildPacketFromEth is a wrapper that build a packet for the rawbytes
*
* \param raw_eth pointer to the rawbytes containing an ethernet packet
* (and any other headers inside)
* \param pktsize pointer to the length of the payload
*
* \retval Packet pointer to the built in packet; NULL if something fail
*/
Packet *UTHBuildPacketFromEth(uint8_t *raw_eth, uint16_t pktsize)
{
DecodeThreadVars dtv;
ThreadVars th_v;
Packet *p = PacketGetFromAlloc();
if (unlikely(p == NULL))
return NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
DecodeEthernet(&th_v, &dtv, p, raw_eth, pktsize);
return p;
}
/**
* \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
* and defaulting ports
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacketSrcDst(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto, const char *src, const char *dst)
{
return UTHBuildPacketReal(payload, payload_len, ipproto,
src, dst,
41424, 80);
}
/**
* \brief UTHBuildPacketSrcDst is a wrapper that build packets specifying IPs
* and defaulting ports (IPV6)
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacketIPV6SrcDst(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto, const char *src, const char *dst)
{
return UTHBuildPacketIPV6Real(payload, payload_len, ipproto,
src, dst,
41424, 80);
}
/**
* \brief UTHBuildPacketSrcDstPorts is a wrapper that build packets specifying
* src and dst ports and defaulting IPs
*
* \param payload pointer to the payload buffer
* \param payload_len pointer to the length of the payload
* \param ipproto Protocols allowed atm are IPPROTO_TCP and IPPROTO_UDP
*
* \retval Packet pointer to the built in packet
*/
Packet *UTHBuildPacketSrcDstPorts(uint8_t *payload, uint16_t payload_len,
uint8_t ipproto, uint16_t sport, uint16_t dport)
{
return UTHBuildPacketReal(payload, payload_len, ipproto,
"192.168.1.5", "192.168.1.1",
sport, dport);
}
/**
* \brief UTHFreePackets: function to release the allocated data
* from UTHBuildPacket and the packet itself
*
* \param p pointer to the Packet
*/
void UTHFreePackets(Packet **p, int numpkts)
{
if (p == NULL)
return;
int i = 0;
for (; i < numpkts; i++) {
UTHFreePacket(p[i]);
}
}
/**
* \brief UTHFreePacket: function to release the allocated data
* from UTHBuildPacket and the packet itself
*
* \param p pointer to the Packet
*/
void UTHFreePacket(Packet *p)
{
if (p == NULL)
return;
#if 0 // VJ we now use one buffer
switch (p->proto) {
case IPPROTO_UDP:
if (p->udph != NULL)
SCFree(p->udph);
if (p->ip4h != NULL)
SCFree(p->ip4h);
break;
case IPPROTO_TCP:
if (p->tcph != NULL)
SCFree(p->tcph);
if (p->ip4h != NULL)
SCFree(p->ip4h);
break;
case IPPROTO_ICMP:
if (p->ip4h != NULL)
SCFree(p->ip4h);
break;
/* TODO: Add more protocols */
}
#endif
SCFree(p);
}
void UTHAssignFlow(Packet *p, Flow *f)
{
if (p && f) {
p->flow = f;
p->flags |= PKT_HAS_FLOW;
}
}
Flow *UTHBuildFlow(int family, const char *src, const char *dst, Port sp, Port dp)
{
return TestHelperBuildFlow(family, src, dst, sp, dp);
}
void UTHFreeFlow(Flow *flow)
{
if (flow != NULL) {
SCFree(flow);//FlowFree(flow);
}
}
int UTHAddStreamToFlow(Flow *f, int direction,
uint8_t *data, uint32_t data_len)
{
FAIL_IF_NULL(f);
FAIL_IF_NOT(f->proto == IPPROTO_TCP);
FAIL_IF_NULL(f->protoctx);
TcpSession *ssn = f->protoctx;
StreamingBufferSegment seg;
TcpStream *stream = direction == 0 ? &ssn->client : &ssn->server;
int r = StreamingBufferAppend(&stream->sb, &stream_config.sbcnf, &seg, data, data_len);
FAIL_IF_NOT(r == 0);
stream->last_ack += data_len;
return 1;
}
int UTHAddSessionToFlow(Flow *f,
uint32_t ts_isn,
uint32_t tc_isn)
{
FAIL_IF_NULL(f);
TcpSession *ssn = SCCalloc(1, sizeof(*ssn));
FAIL_IF_NULL(ssn);
StreamingBuffer x = STREAMING_BUFFER_INITIALIZER;
ssn->client.sb = x;
ssn->server.sb = x;
ssn->client.isn = ts_isn;
ssn->server.isn = tc_isn;
f->protoctx = ssn;
return 1;
}
int UTHRemoveSessionFromFlow(Flow *f)
{
FAIL_IF_NULL(f);
FAIL_IF_NOT(f->proto == IPPROTO_TCP);
TcpSession *ssn = f->protoctx;
FAIL_IF_NULL(ssn);
StreamTcpSessionCleanup(ssn);
SCFree(ssn);
f->protoctx = NULL;
return 1;
}
/**
* \brief UTHGenericTest: function that perform a generic check taking care of
* as maximum common unittest elements as possible.
* It will create a detection engine, append an array
* of signatures an check the expected results for each
* of them, it check matches for an array of packets
*
* \param pkt pointer to the array of packets
* \param numpkts number of packets to match
* \param sigs array of char* pointing to signatures to load
* \param numsigs number of signatures to load and check
* \param results pointer to arrays of numbers, each of them foreach packet
* to check if sids matches that packet as expected with
* that number of times or not. The size of results should be
* numpkts * numsigs * sizeof(uint16_t *)
*
* Example:
* result[1][3] would mean the number of times the pkt[1]
* match the sid[3]
*
* \retval int 1 if the match of all the sids is the specified has the
* specified results; 0 if not
*/
int UTHGenericTest(Packet **pkt, int numpkts, const char *sigs[], uint32_t sids[], uint32_t *results, int numsigs)
{
int result = 0;
if (pkt == NULL || sigs == NULL || numpkts == 0
|| sids == NULL || results == NULL || numsigs == 0) {
SCLogError("Arguments invalid, that the pointer/arrays are not NULL, and the number of "
"signatures and packets is > 0");
goto end;
}
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
if (de_ctx == NULL) {
goto end;
}
de_ctx->flags |= DE_QUIET;
if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0)
goto cleanup;
result = UTHMatchPacketsWithResults(de_ctx, pkt, numpkts, sids, results, numsigs);
cleanup:
DetectEngineCtxFree(de_ctx);
end:
return result;
}
/**
* \brief UTHCheckPacketMatches: function to check if a packet match some sids
*
*
* \param p pointer to the Packet
* \param sigs array of char* pointing to signatures to load
* \param numsigs number of signatures to load from the array
* \param results pointer to an array of numbers to check if sids matches
* that number of times or not.
*
* \retval int 1 if the match of all the sids is the specified has the
* specified results; 0 if not
*/
int UTHCheckPacketMatchResults(Packet *p, uint32_t sids[], uint32_t results[], int numsigs)
{
if (p == NULL || sids == NULL) {
SCLogError("Arguments invalid, check if the "
"packet is NULL, and if the array contain sids is set");
return 0;
}
int i = 0;
int res = 1;
for (; i < numsigs; i++) {
uint32_t r = PacketAlertCheck(p, sids[i]);
if (r != results[i]) {
SCLogInfo("Sid %" PRIu32 " matched %" PRIu32 " times, and not %" PRIu32 " as expected",
sids[i], r, results[i]);
res = 0;
} else {
SCLogInfo("Sid %" PRIu32 " matched %" PRIu32 " times, as expected", sids[i], r);
}
}
return res;
}
/**
* \brief UTHAppendSigs: Add sigs to the detection_engine checking for errors
*
* \param de_ctx pointer to the DetectEngineCtx used
* \param sigs array of char* pointing to signatures to load
* \param numsigs number of signatures to load from the array
* (size of the array)
*
* \retval int 0 if we have errors; 1 if all the signatures loaded successfully
*/
int UTHAppendSigs(DetectEngineCtx *de_ctx, const char *sigs[], int numsigs)
{
BUG_ON(de_ctx == NULL);
BUG_ON(numsigs <= 0);
BUG_ON(sigs == NULL);
for (int i = 0; i < numsigs; i++) {
if (sigs[i] == NULL) {
SCLogError("Check the signature"
" at position %d",
i);
return 0;
}
Signature *s = DetectEngineAppendSig(de_ctx, sigs[i]);
if (s == NULL) {
SCLogError("Check the signature at"
" position %d (%s)",
i, sigs[i]);
return 0;
}
}
return 1;
}
/**
* \test UTHMatchPacketsWithResults Match a packet or a array of packets against sigs
* of a de_ctx, checking that each signature matches X times for certain packets
*
* \param de_ctx pointer with the signatures loaded
* \param p pointer to the array of packets
* \param num_packets number of packets in the array
*
* \retval return 1 if all goes well
* \retval return 0 if something fail
*/
int UTHMatchPacketsWithResults(DetectEngineCtx *de_ctx, Packet **p, int num_packets, uint32_t sids[], uint32_t *results, int numsigs)
{
BUG_ON(de_ctx == NULL);
BUG_ON(p == NULL);
int result = 0;
DecodeThreadVars dtv;
ThreadVars th_v;
DetectEngineThreadCtx *det_ctx = NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
for (int i = 0; i < num_packets; i++) {
SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
if (UTHCheckPacketMatchResults(p[i], sids, &results[(i * numsigs)], numsigs) == 0)
goto cleanup;
}
result = 1;
cleanup:
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
return result;
}
/**
* \test UTHMatchPackets Match a packet or a array of packets against sigs
* of a de_ctx, but note that the return value doesn't mean that we have a
* match, we have to check it later with PacketAlertCheck()
*
* \param de_ctx pointer with the signatures loaded
* \param p pointer to the array of packets
* \param num_packets number of packets in the array
*
* \retval return 1 if all goes well
* \retval return 0 if something fail
*/
int UTHMatchPackets(DetectEngineCtx *de_ctx, Packet **p, int num_packets)
{
BUG_ON(de_ctx == NULL);
BUG_ON(p == NULL);
int result = 1;
DecodeThreadVars dtv;
ThreadVars th_v;
DetectEngineThreadCtx *det_ctx = NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
SCSigRegisterSignatureOrderingFuncs(de_ctx);
SCSigOrderSignatures(de_ctx);
SCSigSignatureOrderingModuleCleanup(de_ctx);
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
for (int i = 0; i < num_packets; i++)
SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]);
/* Here we don't check if the packet matched or not, because
* the de_ctx can have multiple signatures, and some of them may match
* and others may not. That check will be outside
*/
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
if (de_ctx != NULL) SigGroupCleanup(de_ctx);
return result;
}
/**
* \test Test if a packet match a signature given as string and a mpm_type
* Hint: Useful for unittests with only one packet and one signature
*
* \param sig pointer to the string signature to test
* \param sid sid number of the signature
*
* \retval return 1 if match
* \retval return 0 if not
*/
int UTHPacketMatchSigMpm(Packet *p, char *sig, uint16_t mpm_type)
{
SCEnter();
int result = 0;
DecodeThreadVars dtv;
ThreadVars th_v;
DetectEngineThreadCtx *det_ctx = NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
if (de_ctx == NULL) {
printf("de_ctx == NULL: ");
goto end;
}
de_ctx->flags |= DE_QUIET;
de_ctx->mpm_matcher = mpm_type;
de_ctx->sig_list = SigInit(de_ctx, sig);
if (de_ctx->sig_list == NULL) {
printf("signature == NULL: ");
goto end;
}
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
printf("signature didn't alert: ");
goto end;
}
result = 1;
end:
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
DetectEngineCtxFree(de_ctx);
SCReturnInt(result);
}
/**
* \test Test if a packet match a signature given as string
* Hint: Useful for unittests with only one packet and one signature
*
* \param sig pointer to the string signature to test
* \param sid sid number of the signature
*
* \retval return 1 if match
* \retval return 0 if not
*/
int UTHPacketMatchSig(Packet *p, const char *sig)
{
int result = 1;
DecodeThreadVars dtv;
ThreadVars th_v;
DetectEngineThreadCtx *det_ctx = NULL;
memset(&dtv, 0, sizeof(DecodeThreadVars));
memset(&th_v, 0, sizeof(th_v));
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
if (de_ctx == NULL) {
result=0;
goto end;
}
de_ctx->flags |= DE_QUIET;
de_ctx->sig_list = SigInit(de_ctx, sig);
if (de_ctx->sig_list == NULL) {
result = 0;
goto end;
}
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
if (PacketAlertCheck(p, de_ctx->sig_list->id) != 1) {
result = 0;
goto end;
}
end:
if (de_ctx) {
SigGroupCleanup(de_ctx);
SigCleanSignatures(de_ctx);
}
if (det_ctx != NULL)
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
if (de_ctx != NULL)
DetectEngineCtxFree(de_ctx);
return result;
}
uint32_t UTHBuildPacketOfFlows(uint32_t start, uint32_t end, uint8_t dir)
{
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
FlowLookupStruct fls;
memset(&fls, 0, sizeof(fls));
uint32_t i = start;
uint8_t payload[] = "Payload";
for (; i < end; i++) {
Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
if (dir == 0) {
p->src.addr_data32[0] = i;
p->dst.addr_data32[0] = i + 1;
} else {
p->src.addr_data32[0] = i + 1;
p->dst.addr_data32[0] = i;
}
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
FlowHandlePacket(NULL, &fls, p);
if (p->flow != NULL) {
FLOWLOCK_UNLOCK(p->flow);
}
/* Now the queues should be updated */
UTHFreePacket(p);
}
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
Flow *f;
while ((f = FlowQueuePrivateGetFromTop(&fls.spare_queue))) {
FlowFree(f);
}
while ((f = FlowQueuePrivateGetFromTop(&fls.work_queue))) {
FlowFree(f);
}
return i;
}
/** \brief parser a sig and see if the expected result is correct */
int UTHParseSignature(const char *str, bool expect)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
de_ctx->flags |= DE_QUIET;
Signature *s = DetectEngineAppendSig(de_ctx, str);
if (expect)
FAIL_IF_NULL(s);
else
FAIL_IF_NOT_NULL(s);
DetectEngineCtxFree(de_ctx);
PASS;
}
/*
* unittests for the unittest helpers
*/
/**
* \brief CheckUTHTestPacket wrapper to check packets for unittests
*/
static int CheckUTHTestPacket(Packet *p, uint8_t ipproto)
{
uint16_t sport = 41424;
uint16_t dport = 80;
uint8_t payload[] = "Payload";
uint8_t len = sizeof(payload);
if (p == NULL)
return 0;
if (p->payload_len != len)
return 0;
if (strncmp((char *)payload, (char *)p->payload, len) != 0)
return 0;
if (p->src.family != AF_INET)
return 0;
if (p->dst.family != AF_INET)
return 0;
if (p->proto != ipproto)
return 0;
switch(ipproto) {
case IPPROTO_UDP:
if (p->udph == NULL)
return 0;
if (p->udph->uh_sport != sport)
return 0;
if (p->udph->uh_dport != dport)
return 0;
break;
case IPPROTO_TCP:
if (p->tcph == NULL)
return 0;
if (SCNtohs(p->tcph->th_sport) != sport)
return 0;
if (SCNtohs(p->tcph->th_dport) != dport)
return 0;
break;
}
return 1;
}
#ifdef HAVE_MEMMEM
#include <string.h>
void * UTHmemsearch(const void *big, size_t big_len, const void *little, size_t little_len) {
return memmem(big, big_len, little, little_len);
}
#else
#include "util-spm-bs.h"
void * UTHmemsearch(const void *big, size_t big_len, const void *little, size_t little_len) {
return BasicSearch(big, big_len, little, little_len);
}
#endif //HAVE_MEMMEM
/**
* \brief UTHBuildPacketRealTest01 wrapper to check packets for unittests
*/
static int UTHBuildPacketRealTest01(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_TCP,
"192.168.1.5", "192.168.1.1", 41424, 80);
int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketRealTest02 wrapper to check packets for unittests
*/
static int UTHBuildPacketRealTest02(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketReal(payload, sizeof(payload), IPPROTO_UDP,
"192.168.1.5", "192.168.1.1", 41424, 80);
int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketTest01 wrapper to check packets for unittests
*/
static int UTHBuildPacketTest01(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketTest02 wrapper to check packets for unittests
*/
static int UTHBuildPacketTest02(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_UDP);
int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketOfFlowsTest01 wrapper to check packets for unittests
*/
static int UTHBuildPacketOfFlowsTest01(void)
{
int result = 0;
FlowInitConfig(FLOW_QUIET);
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
uint32_t flow_spare_q_len = FlowSpareGetPoolSize();
UTHBuildPacketOfFlows(0, 100, 0);
flow: redesign of flow timeout handling Goals: - reduce locking - take advantage of 'hot' caches - better locality Locking reduction New flow spare pool. The global pool is implmented as a list of blocks, where each block has a 100 spare flows. Worker threads fetch a block at a time, storing the block in the local thread storage. Flow Recycler now returns flows to the pool is blocks as well. Flow Recycler fetches all flows to be processed in one step instead of one at a time. Cache 'hot'ness Worker threads now check the timeout of flows they evaluate during lookup. The worker will have to read the flow into cache anyway, so the added overhead of checking the timeout value is minimal. When a flow is considered timed out, one of 2 things happens: - if the flow is 'owned' by the thread it is handled locally. Handling means checking if the flow needs 'timeout' work. - otherwise, the flow is added to a special 'evicted' list in the flow bucket where it will be picked up by the flow manager. Flow Manager timing By default the flow manager now tries to do passes of the flow hash in smaller steps, where the goal is to do full pass in 8 x the lowest timeout value it has to enforce. So if the lowest timeout value is 30s, a full pass will take 4 minutes. The goal here is to reduce locking overhead and not get in the way of the workers. In emergency mode each pass is full, and lower timeouts are used. Timing of the flow manager is also no longer relying on pthread condition variables, as these generally cause waking up much quicker than the desired timout. Instead a simple (u)sleep loop is used. Both changes reduce the number of hash passes a lot. Emergency behavior In emergency mode there a number of changes to the workers. In this scenario the flow memcap is fully used up and it is unavoidable that some flows won't be tracked. 1. flow spare pool fetches are reduced to once a second. This avoids locking overhead, while the chance of success was very low. 2. getting an active flow directly from the hash skips flows that had very recent activity to avoid the scenario where all flows get only into the NEW state before getting reused. Rather allow some to have a chance of completing. 3. TCP packets that are not SYN packets will not get a used flow, unless stream.midstream is enabled. The goal here is again to avoid evicting active flows unnecessarily. Better Localily Flow Manager injects flows into the worker threads now, instead of one or two packets. Advantage of this is that the worker threads can get packets from their local packet pools, avoiding constant overhead of packets returning to 'foreign' pools. Counters A lot of flow counters have been added and some have been renamed. Overall the worker threads increment 'flow.wrk.*' counters, while the flow manager increments 'flow.mgr.*'. Additionally, none of the counters are snapshots anymore, they all increment over time. The flow.memuse and flow.spare counters are exceptions. Misc FlowQueue has been split into a FlowQueuePrivate (unlocked) and FlowQueue. Flow no longer has 'prev' pointers and used a unified 'next' pointer for both hash and queue use.
7 years ago
if (FlowSpareGetPoolSize() != flow_spare_q_len - 100)
result = 0;
else
result = 1;
FlowShutdown();
return result;
}
/**
* \brief UTHBuildPacketSrcDstTest01 wrapper to check packets for unittests
*/
static int UTHBuildPacketSrcDstTest01(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_TCP,
"192.168.1.5", "192.168.1.1");
int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketSrcDstTest02 wrapper to check packets for unittests
*/
static int UTHBuildPacketSrcDstTest02(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketSrcDst(payload, sizeof(payload), IPPROTO_UDP,
"192.168.1.5", "192.168.1.1");
int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketSrcDstPortsTest01 wrapper to check packets for unittests
*/
static int UTHBuildPacketSrcDstPortsTest01(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_TCP,
41424, 80);
int ret = CheckUTHTestPacket(p, IPPROTO_TCP);
UTHFreePacket(p);
return ret;
}
/**
* \brief UTHBuildPacketSrcDstPortsTest02 wrapper to check packets for unittests
*/
static int UTHBuildPacketSrcDstPortsTest02(void)
{
uint8_t payload[] = "Payload";
Packet *p = UTHBuildPacketSrcDstPorts(payload, sizeof(payload), IPPROTO_UDP,
41424, 80);
int ret = CheckUTHTestPacket(p, IPPROTO_UDP);
UTHFreePacket(p);
return ret;
}
#endif /* UNITTESTS */
void UTHRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("UTHBuildPacketRealTest01", UTHBuildPacketRealTest01);
UtRegisterTest("UTHBuildPacketRealTest02", UTHBuildPacketRealTest02);
UtRegisterTest("UTHBuildPacketTest01", UTHBuildPacketTest01);
UtRegisterTest("UTHBuildPacketTest02", UTHBuildPacketTest02);
UtRegisterTest("UTHBuildPacketSrcDstTest01", UTHBuildPacketSrcDstTest01);
UtRegisterTest("UTHBuildPacketSrcDstTest02", UTHBuildPacketSrcDstTest02);
UtRegisterTest("UTHBuildPacketSrcDstPortsTest01",
UTHBuildPacketSrcDstPortsTest01);
UtRegisterTest("UTHBuildPacketSrcDstPortsTest02",
UTHBuildPacketSrcDstPortsTest02);
UtRegisterTest("UTHBuildPacketOfFlowsTest01", UTHBuildPacketOfFlowsTest01);
#endif /* UNITTESTS */
}