summaryrefslogtreecommitdiffstats
path: root/src/flow-util.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/flow-util.c300
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);
+}