diff options
Diffstat (limited to '')
-rw-r--r-- | src/flow-util.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/flow-util.c b/src/flow-util.c new file mode 100644 index 0000000..dc6a710 --- /dev/null +++ b/src/flow-util.c @@ -0,0 +1,300 @@ +/* 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()) { + DEBUG_VALIDATE_BUG_ON(FlowGetStorageById(f, MacSetGetFlowStorageID()) != NULL); + MacSet *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); +} |