/* * Copyright (c) 2000-2004 QoSient, LLC * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * 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 * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef ArgusFrag #define ArgusFrag #endif #include #include #include #include int ArgusCreateFRAGFlow (struct ip *ip) { int retn = 0, i, len; unsigned short hash = 0, *ptr = (unsigned short *) ArgusThisFlow; if (STRUCTCAPTURED(*ip)) { ArgusThisFlow->ip_flow.ip_src = ip->ip_src.s_addr; ArgusThisFlow->ip_flow.ip_dst = ip->ip_dst.s_addr; ArgusThisFlow->ip_flow.ip_p = ip->ip_p; ArgusThisFlow->ip_flow.tp_p = ARGUS_FRAG_FLOWTAG; ArgusThisFlow->ip_flow.sport = 0xFFFF; ArgusThisFlow->ip_flow.dport = 0xFFFF; ArgusThisFlow->ip_flow.ip_id = ip->ip_id; for (i = 0, len = (sizeof(*ArgusThisFlow)) / sizeof(unsigned short); i < len; i++) hash += *ptr++; ArgusThisDir = 0; ArgusThisHash = hash; retn = 1; } #ifdef ARGUSDEBUG ArgusDebug (4, "ArgusCreateFragFlow() returning %d\n", retn); #endif return (retn); } struct ArgusFlowStruct * ArgusNewFragFlow () { struct ArgusFlowStruct *retn = NULL; struct ArgusFragExtensionBuffer *frag = NULL; int len; if ((retn = (struct ArgusFlowStruct *) ArgusCalloc (1, sizeof (struct ArgusFlowStruct))) != NULL) { ArgusTotalFrags++; retn->ArgusTimeout = ARGUS_IPFRAGTIMEOUT; retn->state.src.idle.min = 0x7FFFFFFF; retn->state.dst.idle.min = 0x7FFFFFFF; retn->state.src.active.min = 0x7FFFFFFF; retn->state.dst.active.min = 0x7FFFFFFF; retn->state.startime = ArgusGlobalTime; retn->state.lasttime = ArgusGlobalTime; retn->qhdr.lasttime = ArgusGlobalTime; retn->qhdr.logtime = ArgusGlobalTime; retn->state.src.status |= ARGUS_FRAGMENTS; retn->ArgusFlowType = ArgusThisNetworkFlowType; bcopy((char *)ArgusThisFlow, (char *)&retn->flow, sizeof(*ArgusThisFlow)); if (ArgusThisPacketLLCEncaps) retn->ArgusFlowType |= ARGUS_SNAPENCAPS; if ((retn->htblhdr = ArgusAddHashEntry (retn)) != NULL) ArgusAddToQueue(ArgusFlowQueue, retn); else ArgusLog (LOG_ERR, "ArgusAddHashEntry failed %s\n", strerror(errno)); if ((frag = (struct ArgusFragExtensionBuffer *) ArgusCalloc (1, sizeof(*frag))) != NULL) { retn->FragDSRBuffer = frag; bcopy((char *)ArgusThisFlow, (char *)&frag->flow, sizeof(*ArgusThisFlow)); if ((len = getArgusUserDataLen()) > 0) { if ((frag->user = ArgusCalloc (1, sizeof(struct ArgusUserDataObject))) != NULL) { if ((frag->user->array = ArgusCalloc (1, len)) != NULL) { frag->user->size = len; } else ArgusLog (LOG_ERR, "ArgusNewFragFlow: ArgusCalloc failed %s\n", strerror(errno)); } else ArgusLog (LOG_ERR, "ArgusNewFragFlow: ArgusCalloc failed %s\n", strerror(errno)); } } else ArgusLog (LOG_ERR, "ArgusNewFragFlow: ArgusCalloc failed %s\n", strerror(errno)); } else ArgusLog (LOG_ERR, "ArgusCalloc failed %s\n", strerror(errno)); #ifdef ARGUSDEBUG ArgusDebug (4, "ArgusNewFragFlow() returning 0x%x\n", retn); #endif return (retn); } void ArgusUpdateFRAGState (struct ArgusFlowStruct *flowstr, unsigned char *state) { struct ArgusFragExtensionBuffer *fragBuf = NULL; struct ArgusFragOffsetStruct *fragOffset = NULL, *thisFragOffset = NULL; int offset = (ArgusThisIpHdr->ip_off & 0x1fff) << 3; int length = ArgusThisLength, newbytes = 1, len = 0, count = 0; int end = offset + length; char *ptr; ArgusTallyTime (flowstr, *state); if ((fragBuf = (struct ArgusFragExtensionBuffer *) flowstr->FragDSRBuffer) != NULL) { if (((ArgusThisIpHdr->ip_off & 0x1fff) == 0) && (ArgusThisIpHdr->ip_off & IP_MF)) fragBuf->startbytes += ArgusThisBytes; fragBuf->totbytes += ArgusThisLength; if ((ArgusThisIpHdr->ip_p == IPPROTO_TCP) && (offset < 2)) fragBuf->frag.status |= ARGUS_TCPFRAGOFFSETERROR; if ((fragOffset = fragBuf->offsets) != NULL) { while (1) { if ((offset == fragOffset->start) && (end == fragOffset->end)) { fragBuf->frag.status |= ARGUS_SRC_PKTS_RETRANS; offset = 0; end = 0; } if ((offset > fragOffset->start) && (offset < fragOffset->end)) { fragBuf->frag.status |= ARGUS_FRAGOVERLAP; if (end > fragOffset->end) { offset = fragOffset->end + 1; } else { newbytes = 0; break; } } if ((end > fragOffset->start) && (end < fragOffset->end)) { fragBuf->frag.status |= ARGUS_FRAGOVERLAP; end = fragOffset->start - 1; } if (fragOffset->nxt == NULL) break; fragOffset = fragOffset->nxt; } } if ((offset != end) && ((offset > 0) && (offset < end))) { if (newbytes) { fragBuf->bytes += end - offset; if ((thisFragOffset = (struct ArgusFragOffsetStruct *) ArgusCalloc(1, sizeof(*thisFragOffset))) != NULL) { thisFragOffset->start = offset; thisFragOffset->end = end; if (fragOffset && (fragOffset->nxt == NULL)) fragOffset->nxt = thisFragOffset; else { if ((fragOffset = fragBuf->offsets) != NULL) { while (fragOffset->nxt != NULL) fragOffset = fragOffset->nxt; fragOffset->nxt = thisFragOffset; } else fragBuf->offsets = thisFragOffset; } } else ArgusLog (LOG_ERR, "ArgusUpdateFragState: ArgusCalloc %s", strerror(errno)); } } if ((offset >= 0) && (fragBuf->user != NULL)) { if (fragBuf->user->array != NULL) { if (offset < fragBuf->user->size) { ptr = ((char *)ArgusThisIpHdr) + (ArgusThisIpHdr->ip_hl << 2); len = fragBuf->user->size - offset; if ((len = ((ArgusThisLength < len) ? ArgusThisLength : len)) > 0) { if ((count = (offset + len)) < fragBuf->user->size) { bcopy (ptr, &fragBuf->user->array[offset], len); if (fragBuf->user->count < count) fragBuf->user->count = count; } } } } } if (ArgusThisLength > fragBuf->frag.maxfraglen) fragBuf->frag.maxfraglen = ArgusThisLength; if (!(ArgusThisIpHdr->ip_off & IP_MF)) fragBuf->frag.totlen = ((ArgusThisIpHdr->ip_off & 0x1fff) << 3) + (ArgusThisIpHdr->ip_len - (ArgusThisIpHdr->ip_hl << 2)); if (fragBuf->frag.totlen) { if (fragBuf->frag.totlen == fragBuf->bytes) if (!(ArgusUpdateParentFlow (flowstr))) ArgusSendFlowRecord(flowstr, ARGUS_STOP); } } else ArgusLog (LOG_ERR, "ArgusUpdateFRAGState (0x%x, %d) no extension buffer\n", flowstr, *state); } int ArgusUpdateParentFlow (struct ArgusFlowStruct *frag) { int retn = 0; struct ArgusFragExtensionBuffer *fragBuf; struct ArgusUserDataObject *ArgusThisUser, *ArgusFragUser; struct ArgusUserObject *user; if (frag->state.src.count || frag->state.dst.count) { if ((fragBuf = (struct ArgusFragExtensionBuffer *) frag->FragDSRBuffer) != NULL) { struct ArgusFlowStruct *parent = fragBuf->flowstr; if ((parent != NULL) && (parent->htblhdr != NULL)) { struct ArgusHashTableHeader *htblhdr = NULL, *target, *head; if ((target = ArgusHashTable.array[parent->htblhdr->hash % ArgusHashTable.size]) != NULL) { head = target; do { if (!(bcmp ((char *) &parent->flow, (char *) &target->flow, sizeof(*ArgusThisFlow)))) { htblhdr = target; break; } else target = target->nxt; } while (target != head); } if (htblhdr) { struct ArgusFlowState *pstate = NULL; struct ArgusFlowStats *pstat = NULL; struct ArgusIPFlow *pFlow = NULL, *fFlow = NULL; pstate = &parent->state; pFlow = (struct ArgusIPFlow *) &parent->flow; fFlow = (struct ArgusIPFlow *) &frag->flow; if (((pFlow->ip_src == fFlow->ip_src) && !(pstate->rev)) || ((pFlow->ip_src != fFlow->ip_src) && pstate->rev)) { pstat = &pstate->src; } else pstat = &pstate->dst; if (parent->state.ofragcnt) parent->state.ofragcnt--; if (parent->state.startime.tv_sec == 0) parent->state.startime = ArgusGlobalTime; parent->qhdr.lasttime = ArgusGlobalTime; parent->state.lasttime = ArgusGlobalTime; pstat->lasttime = ArgusGlobalTime; pstat->count += frag->state.src.count - 1; pstat->bytes += frag->state.src.bytes - fragBuf->startbytes; pstat->status |= ARGUS_FRAGMENTS; if (fragBuf->frag.status & ARGUS_FRAGOVERLAP) pstat->status |= ARGUS_FRAGOVERLAP; if (pstat->active.min > frag->state.src.active.min) pstat->active.min = frag->state.src.active.min; if (pstat->active.max < frag->state.src.active.max) pstat->active.max = frag->state.src.active.max; pstat->active.sum += frag->state.src.active.sum; pstat->active.n += frag->state.src.active.n; frag->state.src.count = 0; frag->state.dst.count = 0; frag->state.src.bytes = 0; frag->state.dst.bytes = 0; pstate->status |= ARGUS_FRAGMENTS; if (fragBuf->frag.status & ARGUS_FRAGOVERLAP) pstate->status |= ARGUS_FRAGOVERLAP; if ((user = (struct ArgusUserObject *) parent->UserDSRBuffer) != NULL) { if (parent->state.rev == ArgusThisDir) { ArgusThisUser = &user->src; } else ArgusThisUser = &user->dst; if ((ArgusFragUser = fragBuf->user) != NULL) { if ((ArgusThisUser->count >= 0) && (ArgusThisUser->count < ArgusThisUser->size)) { char *ptr = (char *)&ArgusThisUser->array[ArgusThisUser->count]; int thislen = ArgusThisUser->size - ArgusThisUser->count; if (thislen > ArgusFragUser->count) thislen = ArgusFragUser->count; bcopy (ArgusFragUser->array, ptr, thislen); ArgusThisUser->count += thislen; } } } if ((ArgusFragUser = fragBuf->user) != NULL) { ArgusFree(ArgusFragUser->array); ArgusFragUser->array = NULL; ArgusFree(ArgusFragUser); fragBuf->user = NULL; } if (pstate->status & ARGUS_SEND_FRAG_COMPLETE) { pstate->status &= ~(ARGUS_SEND_FRAG_COMPLETE); ArgusSendFlowRecord (parent, ARGUS_STATUS); } retn = 1; } else { #ifdef ARGUSDEBUG ArgusDebug (2, "ArgusUpdateParentFlow(0x%x) did not find parent 0x%x\n", frag, parent); #endif } } } } #ifdef ARGUSDEBUG if (retn) ArgusDebug (7, "ArgusUpdateParentFlow(0x%x) returning 0x%x\n", frag, retn); else ArgusDebug (5, "ArgusUpdateParentFlow(0x%x) returning 0x%x\n", frag, retn); #endif return (retn); }