mirror of https://github.com/OISF/suricata
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.
305 lines
8.2 KiB
C
305 lines
8.2 KiB
C
/* Copyright (C) 2007-2013 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 Victor Julien <victor@inliniac.net>
|
|
*
|
|
* Flow utility functions
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "threads.h"
|
|
|
|
#include "flow.h"
|
|
#include "flow-private.h"
|
|
#include "flow-util.h"
|
|
#include "flow-var.h"
|
|
#include "app-layer.h"
|
|
|
|
#include "util-var.h"
|
|
#include "util-debug.h"
|
|
#include "util-macset.h"
|
|
#include "flow-storage.h"
|
|
|
|
#include "detect.h"
|
|
#include "detect-engine-state.h"
|
|
|
|
#include "decode-icmpv4.h"
|
|
|
|
#include "util-validate.h"
|
|
|
|
/** \brief allocate a flow
|
|
*
|
|
* We check against the memuse counter. If it passes that check we increment
|
|
* the counter first, then we try to alloc.
|
|
*
|
|
* \retval f the flow or NULL on out of memory
|
|
*/
|
|
Flow *FlowAlloc(void)
|
|
{
|
|
Flow *f;
|
|
size_t size = sizeof(Flow) + FlowStorageSize();
|
|
|
|
if (!(FLOW_CHECK_MEMCAP(size))) {
|
|
return NULL;
|
|
}
|
|
|
|
(void) SC_ATOMIC_ADD(flow_memuse, size);
|
|
|
|
f = SCMalloc(size);
|
|
if (unlikely(f == NULL)) {
|
|
(void)SC_ATOMIC_SUB(flow_memuse, size);
|
|
return NULL;
|
|
}
|
|
memset(f, 0, size);
|
|
|
|
/* coverity[missing_lock] */
|
|
FLOW_INITIALIZE(f);
|
|
return f;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief cleanup & free the memory of a flow
|
|
*
|
|
* \param f flow to clear & destroy
|
|
*/
|
|
void FlowFree(Flow *f)
|
|
{
|
|
FLOW_DESTROY(f);
|
|
SCFree(f);
|
|
|
|
size_t size = sizeof(Flow) + FlowStorageSize();
|
|
(void) SC_ATOMIC_SUB(flow_memuse, size);
|
|
}
|
|
|
|
/**
|
|
* \brief Function to map the protocol to the defined FLOW_PROTO_* enumeration.
|
|
*
|
|
* \param proto protocol which is needed to be mapped
|
|
*/
|
|
|
|
uint8_t FlowGetProtoMapping(uint8_t proto)
|
|
{
|
|
switch (proto) {
|
|
case IPPROTO_TCP:
|
|
return FLOW_PROTO_TCP;
|
|
case IPPROTO_UDP:
|
|
return FLOW_PROTO_UDP;
|
|
case IPPROTO_ICMP:
|
|
return FLOW_PROTO_ICMP;
|
|
default:
|
|
return FLOW_PROTO_DEFAULT;
|
|
}
|
|
}
|
|
|
|
uint8_t FlowGetReverseProtoMapping(uint8_t rproto)
|
|
{
|
|
switch (rproto) {
|
|
case FLOW_PROTO_TCP:
|
|
return IPPROTO_TCP;
|
|
case FLOW_PROTO_UDP:
|
|
return IPPROTO_UDP;
|
|
case FLOW_PROTO_ICMP:
|
|
return IPPROTO_ICMP;
|
|
default:
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static inline void FlowSetICMPv4CounterPart(Flow *f)
|
|
{
|
|
int ctype = ICMPv4GetCounterpart(f->icmp_s.type);
|
|
if (ctype == -1)
|
|
return;
|
|
|
|
f->icmp_d.type = (uint8_t)ctype;
|
|
}
|
|
|
|
static inline void FlowSetICMPv6CounterPart(Flow *f)
|
|
{
|
|
int ctype = ICMPv6GetCounterpart(f->icmp_s.type);
|
|
if (ctype == -1)
|
|
return;
|
|
|
|
f->icmp_d.type = (uint8_t)ctype;
|
|
}
|
|
|
|
/* initialize the flow from the first packet
|
|
* we see from it. */
|
|
void FlowInit(Flow *f, const Packet *p)
|
|
{
|
|
SCEnter();
|
|
SCLogDebug("flow %p", f);
|
|
|
|
f->proto = p->proto;
|
|
f->recursion_level = p->recursion_level;
|
|
memcpy(&f->vlan_id[0], &p->vlan_id[0], sizeof(f->vlan_id));
|
|
f->vlan_idx = p->vlan_idx;
|
|
f->livedev = p->livedev;
|
|
|
|
if (PKT_IS_IPV4(p)) {
|
|
FLOW_SET_IPV4_SRC_ADDR_FROM_PACKET(p, &f->src);
|
|
FLOW_SET_IPV4_DST_ADDR_FROM_PACKET(p, &f->dst);
|
|
f->min_ttl_toserver = f->max_ttl_toserver = IPV4_GET_IPTTL((p));
|
|
f->flags |= FLOW_IPV4;
|
|
} else if (PKT_IS_IPV6(p)) {
|
|
FLOW_SET_IPV6_SRC_ADDR_FROM_PACKET(p, &f->src);
|
|
FLOW_SET_IPV6_DST_ADDR_FROM_PACKET(p, &f->dst);
|
|
f->min_ttl_toserver = f->max_ttl_toserver = IPV6_GET_HLIM((p));
|
|
f->flags |= FLOW_IPV6;
|
|
} else {
|
|
SCLogDebug("neither IPv4 or IPv6, weird");
|
|
DEBUG_VALIDATE_BUG_ON(1);
|
|
}
|
|
|
|
if (p->tcph != NULL) { /* XXX MACRO */
|
|
SET_TCP_SRC_PORT(p,&f->sp);
|
|
SET_TCP_DST_PORT(p,&f->dp);
|
|
} else if (p->udph != NULL) { /* XXX MACRO */
|
|
SET_UDP_SRC_PORT(p,&f->sp);
|
|
SET_UDP_DST_PORT(p,&f->dp);
|
|
} else if (p->icmpv4h != NULL) {
|
|
f->icmp_s.type = p->icmp_s.type;
|
|
f->icmp_s.code = p->icmp_s.code;
|
|
FlowSetICMPv4CounterPart(f);
|
|
} else if (p->icmpv6h != NULL) {
|
|
f->icmp_s.type = p->icmp_s.type;
|
|
f->icmp_s.code = p->icmp_s.code;
|
|
FlowSetICMPv6CounterPart(f);
|
|
} else if (p->sctph != NULL) { /* XXX MACRO */
|
|
SET_SCTP_SRC_PORT(p,&f->sp);
|
|
SET_SCTP_DST_PORT(p,&f->dp);
|
|
} else if (p->esph != NULL) {
|
|
f->esp.spi = ESP_GET_SPI(p);
|
|
} else {
|
|
/* nothing to do for this IP proto. */
|
|
SCLogDebug("no special setup for IP proto %u", p->proto);
|
|
}
|
|
f->startts = p->ts;
|
|
|
|
f->protomap = FlowGetProtoMapping(f->proto);
|
|
f->timeout_policy = FlowGetTimeoutPolicy(f);
|
|
const uint32_t timeout_at = (uint32_t)SCTIME_SECS(f->startts) + f->timeout_policy;
|
|
f->timeout_at = timeout_at;
|
|
|
|
if (MacSetFlowStorageEnabled()) {
|
|
MacSet *ms = FlowGetStorageById(f, MacSetGetFlowStorageID());
|
|
if (ms != NULL) {
|
|
MacSetReset(ms);
|
|
} else {
|
|
ms = MacSetInit(10);
|
|
FlowSetStorageById(f, MacSetGetFlowStorageID(), ms);
|
|
}
|
|
}
|
|
|
|
SCReturn;
|
|
}
|
|
|
|
FlowStorageId g_bypass_info_id = { .id = -1 };
|
|
|
|
FlowStorageId GetFlowBypassInfoID(void)
|
|
{
|
|
return g_bypass_info_id;
|
|
}
|
|
|
|
static void FlowBypassFree(void *x)
|
|
{
|
|
FlowBypassInfo *fb = (FlowBypassInfo *) x;
|
|
|
|
if (fb == NULL)
|
|
return;
|
|
|
|
if (fb->bypass_data && fb->BypassFree) {
|
|
fb->BypassFree(fb->bypass_data);
|
|
}
|
|
SCFree(fb);
|
|
}
|
|
|
|
void RegisterFlowBypassInfo(void)
|
|
{
|
|
g_bypass_info_id = FlowStorageRegister("bypass_counters", sizeof(void *),
|
|
NULL, FlowBypassFree);
|
|
}
|
|
|
|
void FlowEndCountersRegister(ThreadVars *t, FlowEndCounters *fec)
|
|
{
|
|
for (int i = 0; i < FLOW_STATE_SIZE; i++) {
|
|
const char *name = NULL;
|
|
if (i == FLOW_STATE_NEW) {
|
|
name = "flow.end.state.new";
|
|
} else if (i == FLOW_STATE_ESTABLISHED) {
|
|
name = "flow.end.state.established";
|
|
} else if (i == FLOW_STATE_CLOSED) {
|
|
name = "flow.end.state.closed";
|
|
} else if (i == FLOW_STATE_LOCAL_BYPASSED) {
|
|
name = "flow.end.state.local_bypassed";
|
|
#ifdef CAPTURE_OFFLOAD
|
|
} else if (i == FLOW_STATE_CAPTURE_BYPASSED) {
|
|
name = "flow.end.state.capture_bypassed";
|
|
#endif
|
|
}
|
|
if (name) {
|
|
fec->flow_state[i] = StatsRegisterCounter(name, t);
|
|
}
|
|
}
|
|
|
|
for (enum TcpState i = TCP_NONE; i <= TCP_CLOSED; i++) {
|
|
const char *name = NULL;
|
|
switch (i) {
|
|
case TCP_NONE:
|
|
name = "flow.end.tcp_state.none";
|
|
break;
|
|
case TCP_SYN_SENT:
|
|
name = "flow.end.tcp_state.syn_sent";
|
|
break;
|
|
case TCP_SYN_RECV:
|
|
name = "flow.end.tcp_state.syn_recv";
|
|
break;
|
|
case TCP_ESTABLISHED:
|
|
name = "flow.end.tcp_state.established";
|
|
break;
|
|
case TCP_FIN_WAIT1:
|
|
name = "flow.end.tcp_state.fin_wait1";
|
|
break;
|
|
case TCP_FIN_WAIT2:
|
|
name = "flow.end.tcp_state.fin_wait2";
|
|
break;
|
|
case TCP_TIME_WAIT:
|
|
name = "flow.end.tcp_state.time_wait";
|
|
break;
|
|
case TCP_LAST_ACK:
|
|
name = "flow.end.tcp_state.last_ack";
|
|
break;
|
|
case TCP_CLOSE_WAIT:
|
|
name = "flow.end.tcp_state.close_wait";
|
|
break;
|
|
case TCP_CLOSING:
|
|
name = "flow.end.tcp_state.closing";
|
|
break;
|
|
case TCP_CLOSED:
|
|
name = "flow.end.tcp_state.closed";
|
|
break;
|
|
}
|
|
|
|
fec->flow_tcp_state[i] = StatsRegisterCounter(name, t);
|
|
}
|
|
fec->flow_tcp_liberal = StatsRegisterCounter("flow.end.tcp_liberal", t);
|
|
}
|