/* * 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 ArgusTcp #define ArgusTcp #endif #include #include #include #include static struct ArgusTCPModelerObject *ArgusThisTCPsrc, *ArgusThisTCPdst; /* These tcp optinos do not have the size octet */ #define ZEROLENOPT(o) ((o) == TCPOPT_EOL || (o) == TCPOPT_NOP) #define TH_ECN 0x40 #define TH_CWR 0x80 void ArgusParseTCPOptions(struct tcphdr *, int, u_int *); void ArgusParseTCPOptions(struct tcphdr *tcp, int len, u_int *options) { register const u_char *cp; register int i, opt, alen, datalen; if ((tcp != NULL)) { cp = (const u_char *)tcp + sizeof(*tcp); while (len > 0) { STRUCTCHECK(*cp); opt = *cp++; if (ZEROLENOPT(opt)) alen = 1; else { STRUCTCHECK(*cp); alen = *cp++; /* total including type, len */ if (alen < 2 || alen > len) goto bad; --len; /* account for length byte */ } --len; /* account for type byte */ datalen = 0; switch (opt) { case TCPOPT_MAXSEG: *options |= ARGUS_TCP_MAXSEG; datalen = 2; LENCHECK(datalen); break; case TCPOPT_EOL: break; case TCPOPT_NOP: break; case TCPOPT_WSCALE: *options |= ARGUS_TCP_WSCALE; datalen = 1; LENCHECK(datalen); break; case TCPOPT_SACKOK: *options |= ARGUS_TCP_SACKOK; break; case TCPOPT_SACK: *options |= ARGUS_TCP_SACK; datalen = alen - 2; for (i = 0; i < datalen; i += 4) { LENCHECK(i + 4); } break; case TCPOPT_ECHO: *options |= ARGUS_TCP_ECHO; datalen = 4; LENCHECK(datalen); break; case TCPOPT_ECHOREPLY: *options |= ARGUS_TCP_ECHOREPLY; datalen = 4; LENCHECK(datalen); break; case TCPOPT_TIMESTAMP: *options |= ARGUS_TCP_TIMESTAMP; datalen = 8; LENCHECK(4); LENCHECK(datalen); break; case TCPOPT_CC: *options |= ARGUS_TCP_CC; datalen = 4; LENCHECK(datalen); break; case TCPOPT_CCNEW: *options |= ARGUS_TCP_CCNEW; datalen = 4; LENCHECK(datalen); break; case TCPOPT_CCECHO: *options |= ARGUS_TCP_CCECHO; datalen = 4; LENCHECK(datalen); break; default: datalen = alen - 2; for (i = 0; i < datalen; ++i) LENCHECK(i); break; } cp += datalen; len -= datalen; ++datalen; /* option octet */ if (!ZEROLENOPT(opt)) ++datalen; /* size octet */ if (opt == TCPOPT_EOL) break; } } bad: trunc: return; } #include #include void ArgusUpdateTCPState (struct ArgusFlowStruct *flowstr, unsigned char *state) { struct tcphdr *tcp = (struct tcphdr *) ArgusThisUpHdr; struct ArgusTCPExtensionBuffer *tcpExt = NULL; if (flowstr) { if (tcp && STRUCTCAPTURED(*tcp)) { int tcplen = ArgusThisLength; int tcphlen = tcp->th_off * 4; int tcpdatalen = tcplen - tcphlen; unsigned char flags = tcp->th_flags; ArgusSnapLength -= tcphlen; ArgusThisLength = tcpdatalen; ArgusThisUpHdr += tcphlen; ArgusUpdateAppState(flowstr, state); #ifdef _LITTLE_ENDIAN tcp->th_dport = ntohs(tcp->th_dport); tcp->th_sport = ntohs(tcp->th_sport); tcp->th_win = ntohs(tcp->th_win); tcp->th_seq = ntohl(tcp->th_seq); tcp->th_ack = ntohl(tcp->th_ack); #endif if (*state == ARGUS_START) { if (flowstr->NetworkDSRBuffer) ArgusFree(flowstr->NetworkDSRBuffer); if ((flowstr->NetworkDSRBuffer = (struct ArgusTCPObject *) ArgusCalloc (1, sizeof (struct ArgusTCPExtensionBuffer))) == NULL) { ArgusLog (LOG_ERR, "ArgusUpdateTCPState: ArgusCalloc failed %s\n", strerror(errno)); } else tcpExt = flowstr->NetworkDSRBuffer; if ((tcphlen -= sizeof(*tcp)) > 0) ArgusParseTCPOptions (tcp, tcphlen, &tcpExt->options); if (flags & TH_RST) { tcpExt->status |= ARGUS_RESET; tcpExt->src.status |= ARGUS_RESET; tcpExt->state = TCPS_LISTEN; tcpExt->src.count += 1; tcpExt->src.bytes += ArgusThisLength; tcpExt->src.flag = tcp->th_flags; tcpExt->src.seq_base = tcp->th_seq - 1; tcpExt->src.ackbytes = tcp->th_seq - 1; tcpExt->src.lasttime = ArgusGlobalTime; tcpExt->src.seq = tcp->th_seq + ArgusThisLength; } else { switch (flags & (TH_SYN|TH_ACK|TH_FIN|TH_PUSH|TH_URG)) { case (TH_SYN): tcpExt->status |= ARGUS_SAW_SYN; tcpExt->state = TCPS_SYN_SENT; tcpExt->src.count += 1; tcpExt->src.bytes += ArgusThisLength; tcpExt->src.seq_base = tcp->th_seq; tcpExt->src.ackbytes = tcp->th_seq; tcpExt->src.seq = tcp->th_seq; tcpExt->src.flag = tcp->th_flags; tcpExt->src.lasttime = ArgusGlobalTime; if ((flags & (TH_ECN|TH_CWR)) == (TH_ECN|TH_CWR)) tcpExt->options |= ARGUS_TCP_SRC_ECN; break; case (TH_SYN|TH_ACK): tcpExt->status |= ARGUS_SAW_SYN_SENT; tcpExt->state = TCPS_SYN_RECEIVED; flowstr->state.rev = flowstr->state.rev ? 0 : 1; bcopy ((char *)&flowstr->state.src, (char *)&flowstr->state.dst, sizeof (flowstr->state.src)); bzero ((char *)&flowstr->state.src, sizeof (flowstr->state.src)); tcpExt->dst.count += 1; tcpExt->dst.bytes += ArgusThisLength; tcpExt->dst.seq_base = tcp->th_seq; tcpExt->dst.ackbytes = tcp->th_seq; tcpExt->dst.seq = tcp->th_seq; tcpExt->dst.flag = tcp->th_flags; tcpExt->dst.lasttime = ArgusGlobalTime; tcpExt->src.ack = tcp->th_ack - 1; tcpExt->src.ackbytes = tcp->th_ack - 1; tcpExt->src.seq_base = tcp->th_ack - 1; tcpExt->src.seq = tcp->th_ack - 1; if ((tcp->th_flags & (TH_ECN|TH_CWR)) == TH_ECN) tcpExt->options |= ARGUS_TCP_DST_ECN; break; case (TH_ACK): case (TH_PUSH|TH_ACK): case (TH_URG|TH_ACK): case (TH_PUSH|TH_URG|TH_ACK): tcpExt->dst.ack = tcp->th_ack - 1; tcpExt->dst.seq_base = tcp->th_ack - 1; tcpExt->dst.ackbytes = tcp->th_ack - 1; case (TH_PUSH): case (TH_URG): case (TH_PUSH|TH_URG): tcpExt->status |= ARGUS_CON_ESTABLISHED; tcpExt->state = TCPS_ESTABLISHED; tcpExt->src.count += 1; tcpExt->src.bytes += ArgusThisLength; tcpExt->src.flag = tcp->th_flags; tcpExt->src.seq_base = tcp->th_seq - 1; tcpExt->src.ackbytes = tcp->th_seq - 1; tcpExt->src.lasttime = ArgusGlobalTime; tcpExt->src.seq = tcp->th_seq + ArgusThisLength; break; case (TH_FIN): case (TH_FIN|TH_ACK): tcpExt->status |= ARGUS_FIN; tcpExt->state = TCPS_FIN_WAIT_1; tcpExt->src.count += 1; tcpExt->src.bytes += ArgusThisLength; tcpExt->src.flag = tcp->th_flags; tcpExt->src.seq_base = tcp->th_seq - 1; tcpExt->src.ackbytes = tcp->th_seq - 1; tcpExt->src.lasttime = ArgusGlobalTime; tcpExt->src.seq = tcp->th_seq + ArgusThisLength; break; default: tcpExt->status |= ARGUS_CON_ESTABLISHED; tcpExt->state = TCPS_CLOSING; tcpExt->src.count += 1; tcpExt->src.bytes += ArgusThisLength; tcpExt->src.flag = tcp->th_flags; tcpExt->src.seq_base = tcp->th_seq - 1; tcpExt->src.ackbytes = tcp->th_seq - 1; tcpExt->src.lasttime = ArgusGlobalTime; tcpExt->src.seq = ArgusThisLength; break; } } } else { if ((tcpExt = (struct ArgusTCPExtensionBuffer *) flowstr->NetworkDSRBuffer) != NULL) { struct ArgusFlowStats *ArgusThisStats; if ((tcphlen -= sizeof(*tcp)) > 0) ArgusParseTCPOptions (tcp, tcphlen, &tcpExt->options); if (flowstr->state.rev == ArgusThisDir) { ArgusThisStats = &flowstr->state.src; ArgusThisTCPsrc = &tcpExt->src; ArgusThisTCPdst = &tcpExt->dst; } else { ArgusThisStats = &flowstr->state.dst; ArgusThisTCPsrc = &tcpExt->dst; ArgusThisTCPdst = &tcpExt->src; } ArgusThisTCPsrc->count++; ArgusThisTCPsrc->flag |= tcp->th_flags; if (flags & TH_ECN) if (flags & TH_ACK) { tcpExt->status |= ARGUS_ECN_CONGESTED; ArgusThisTCPdst->state = ARGUS_ECN_CONGESTED; } ArgusUpdateTCPSequence(flowstr, tcp); switch (ArgusUpdateTCPStateMachine(flowstr, tcp)) { case TCPS_LISTEN: if (flags == TH_SYN) { ArgusThisStats->count--; ArgusThisStats->bytes -= ArgusThisLength; ArgusThisTCPsrc->count--; ArgusThisTCPsrc->bytes -= ArgusThisLength; ArgusThisUpHdr -= tcphlen; ArgusThisLength = tcplen; ArgusSnapLength += tcphlen; ArgusSendFlowRecord (flowstr, ARGUS_STOP); ArgusInitializeTCP (flowstr); return; } else { ArgusTallyTime (flowstr, *state); ArgusSendFlowRecord (flowstr, ARGUS_STOP); ArgusRemoveHashEntry(flowstr->htblhdr); flowstr->ArgusTimeout = 0; flowstr->htblhdr = NULL; } break; case TCPS_CLOSED: case TCPS_TIME_WAIT: if (!(tcpExt->status & ARGUS_RESET)) tcpExt->status |= ARGUS_NORMAL_CLOSE; flowstr->ArgusTimeout = 10; break; } ArgusThisTCPsrc->lasttime = ArgusGlobalTime; ArgusTallyTime (flowstr, *state); } else { *state = ARGUS_START; ArgusUpdateTCPState (flowstr, state); } } } } } void ArgusInitializeTCP (struct ArgusFlowStruct *flowstr) { unsigned char rev = flowstr->state.rev, dir = flowstr->state.dir; flowstr->ArgusTransactionNum = ArgusTransactionNum++; bzero ((char *)&flowstr->state, sizeof(flowstr->state)); flowstr->state.rev = rev; flowstr->state.dir = dir; flowstr->state.src.active.min = 0x7FFFFFFF; flowstr->state.dst.active.min = 0x7FFFFFFF; flowstr->qhdr.lasttime.tv_sec = 0; flowstr->qhdr.lasttime.tv_usec = 0; flowstr->qhdr.logtime.tv_sec = 0; flowstr->qhdr.logtime.tv_usec = 0; if (flowstr->NetworkDSRBuffer) { ArgusFree(flowstr->NetworkDSRBuffer); flowstr->NetworkDSRBuffer = NULL; } ArgusUpdateFlow (flowstr, ARGUS_START); } int ArgusUpdateTCPStateMachine (struct ArgusFlowStruct *flowstr, struct tcphdr *tcp) { unsigned char flags = tcp->th_flags; struct ArgusTCPExtensionBuffer *tcpExt = (struct ArgusTCPExtensionBuffer *) flowstr->NetworkDSRBuffer; unsigned int state = tcpExt->state; int len = ArgusThisLength; if (flags & TH_RST) { tcpExt->status |= ARGUS_RESET; ArgusThisTCPsrc->status |= ARGUS_RESET; if (state == TCPS_SYN_SENT) { if ((ArgusThisTCPdst->seq == ArgusThisTCPdst->ack)) state = TCPS_LISTEN; } else if ((tcp->th_seq >= ArgusThisTCPsrc->ack) && (tcp->th_seq < (ArgusThisTCPsrc->ack + ArgusThisTCPsrc->win))) state = TCPS_CLOSED; } else { switch (state) { case TCPS_LISTEN: case TCPS_SYN_SENT: if (flags == TH_SYN) { ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->retrans++; } else if (flags == (TH_SYN|TH_ACK)) { if (ArgusThisTCPsrc->status & ARGUS_SAW_SYN) { state = TCPS_LISTEN; tcpExt->status |= ARGUS_SAW_SYN_SENT; ArgusThisTCPsrc->status |= ARGUS_SAW_SYN_SENT; } else { state = TCPS_SYN_RECEIVED; tcpExt->status |= ARGUS_SAW_SYN_SENT; ArgusThisTCPsrc->status |= ARGUS_SAW_SYN_SENT; if (tcpExt->synAckuSecs == 0) flowstr->state.startime = ArgusThisTCPdst->lasttime; if ((ArgusThisTCPdst->seq == ArgusThisTCPdst->ack)) { tcpExt->synAckuSecs = ArgusAbsTimeDiff (&ArgusGlobalTime, &ArgusThisTCPdst->lasttime); } } } else if (flags & TH_FIN) { state = TCPS_FIN_WAIT_1; tcpExt->status |= ARGUS_FIN; ArgusThisTCPsrc->status |= ARGUS_FIN; } else if (flags & TH_ACK) { state = TCPS_ESTABLISHED; tcpExt->status |= ARGUS_CON_ESTABLISHED; ArgusThisTCPsrc->status |= ARGUS_CON_ESTABLISHED; flowstr->ArgusTimeout = ARGUS_IPTIMEOUT; } break; case TCPS_SYN_RECEIVED: if (flags & TH_FIN) { state = TCPS_FIN_WAIT_1; tcpExt->status |= ARGUS_FIN; ArgusThisTCPsrc->status |= ARGUS_FIN; } else if (!(flags & TH_SYN)) { if (flags & TH_ACK) { state = TCPS_ESTABLISHED; tcpExt->status |= ARGUS_CON_ESTABLISHED; flowstr->ArgusTimeout = ARGUS_IPTIMEOUT; ArgusThisTCPsrc->status |= ARGUS_CON_ESTABLISHED; if ((ArgusThisTCPsrc->seq == ArgusThisTCPsrc->ack)) { tcpExt->ackDatauSecs = ArgusAbsTimeDiff (&ArgusGlobalTime, &ArgusThisTCPdst->lasttime); } } } else { ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->retrans++; } break; case TCPS_ESTABLISHED: if (flags & TH_FIN) { state = TCPS_FIN_WAIT_1; tcpExt->status |= ARGUS_FIN; ArgusThisTCPsrc->status |= ARGUS_FIN; } else { if (flags & TH_SYN) { if (flags & TH_ACK) { tcpExt->status |= ARGUS_SAW_SYN_SENT; tcpExt->status |= ARGUS_CON_ESTABLISHED; } ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->retrans++; } if ((tcpExt->src.count > 2) || (tcpExt->dst.count > 2) || ((tcpExt->src.count + tcpExt->dst.count) > 2)) flowstr->ArgusTimeout = ARGUS_IPTIMEOUT; } break; case TCPS_CLOSE_WAIT: case TCPS_FIN_WAIT_1: if ((flags & TH_SYN) && !(flags & TH_ACK)) { state = TCPS_LISTEN; } else case TCPS_LAST_ACK: case TCPS_FIN_WAIT_2: if (flags & TH_FIN) { if (!(flags & TH_ACK)) { if (ArgusThisTCPdst->status & ARGUS_FIN_ACK) { ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->retrans++; } } else { tcpExt->status |= ARGUS_FIN; ArgusThisTCPsrc->status |= ARGUS_FIN; } } if ((flags & TH_ACK) && !(len)) { if (ArgusThisTCPdst->status & ARGUS_FIN) { if (ArgusThisTCPdst->seq == ArgusThisTCPdst->ack) { state = TCPS_FIN_WAIT_2; tcpExt->status |= ARGUS_FIN_ACK; ArgusThisTCPdst->status |= ARGUS_FIN_ACK; } } } break; case TCPS_CLOSING: case TCPS_TIME_WAIT: if ((flags & TH_SYN) && !(flags & TH_ACK)) state = TCPS_LISTEN; else if (flags & TH_ACK) if ((ArgusThisTCPsrc->seq == ArgusThisTCPsrc->ack) && (ArgusThisTCPdst->seq == ArgusThisTCPdst->ack)) state = TCPS_CLOSED; break; case TCPS_CLOSED: if ((flags & TH_SYN) && !(flags & TH_ACK)) state = TCPS_LISTEN; break; } } if (state != TCPS_LISTEN) tcpExt->state = state; return (state); } int ArgusUpdateTCPSequence (struct ArgusFlowStruct *flowstr, struct tcphdr *tcp) { unsigned char flags = tcp->th_flags; int len = ArgusThisLength; int retn = 1, win; unsigned int maxseq = 0; unsigned int seq = tcp->th_seq; unsigned int newseq = seq + len; ArgusInProtocol = 1; if (!(tcp->th_win) && !(flags & (TH_FIN|TH_RST))) { ArgusThisTCPsrc->status |= ARGUS_WINDOW_SHUT; ArgusInProtocol = 0; } if (len && (ArgusThisTCPdst->win != 0)) { ArgusThisTCPsrc->bytes += len; if (ArgusThisTCPsrc->winbytes == 0) ArgusInProtocol = 0; ArgusThisTCPsrc->winbytes += len; } else ArgusInProtocol = 0; if ((newseq < seq) || (flags == TH_SYN)) { /* we rolled over or started over */ ArgusThisTCPsrc->seq_base = newseq; ArgusThisTCPsrc->ackbytes = newseq; ArgusThisTCPsrc->seq = newseq; } else { if (!ArgusThisTCPsrc->seq_base) { ArgusThisTCPsrc->seq_base = seq; ArgusThisTCPsrc->ackbytes = seq; ArgusThisTCPsrc->seq = newseq; } else { if (len && (ArgusThisTCPdst->win != 0)) { if (tcp->th_seq < ArgusThisTCPsrc->ack) { if ((ArgusThisTCPsrc->ack - tcp->th_seq) < ArgusThisTCPsrc->win) { ArgusThisTCPsrc->retrans++; ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->winbytes -= len; ArgusInProtocol = 0; } } else { maxseq = (newseq > ArgusThisTCPsrc->seq) ? newseq : ArgusThisTCPsrc->seq; if (ArgusThisTCPsrc->win) { if (ArgusThisTCPsrc->winbytes > ((maxseq - 1) - ArgusThisTCPsrc->ack)) { ArgusThisTCPsrc->retrans++; ArgusThisTCPsrc->status |= ARGUS_PKTS_RETRANS; ArgusThisTCPsrc->winbytes -= len; ArgusInProtocol = 0; } } ArgusThisTCPsrc->seq = maxseq; } } else { if ((flags == TH_ACK) && (ArgusThisTCPdst->ack == (tcp->th_ack - 1))) { if (ArgusThisTCPsrc->win == tcp->th_win) { /* ArgusThisTCPdst->retrans++; ArgusThisTCPdst->status |= ARGUS_PKTS_RETRANS; */ } ArgusInProtocol = 0; } } } } if (tcp->th_ack && (flags & TH_ACK)) { if (ArgusThisTCPdst->seq > ArgusThisTCPdst->ack) ArgusThisTCPdst->winbytes = (ArgusThisTCPdst->seq - 1) - ArgusThisTCPdst->ack; if (!(ArgusThisTCPdst->ack == (tcp->th_ack - 1))) { if (!(ArgusThisTCPdst->ack) || (ArgusThisTCPdst->seq == tcp->th_ack)) { ArgusThisTCPdst->winbytes = 0; if (!(ArgusThisTCPdst->ack == (tcp->th_ack - 1))) if (ArgusThisTCPdst->seq == tcp->th_ack) ArgusThisTCPdst->winnum++; } else { if (ArgusThisTCPdst->ack) { win = (tcp->th_ack - 1) - ArgusThisTCPdst->ack; win = (ArgusThisTCPdst->winbytes < win) ? ArgusThisTCPdst->winbytes : win; ArgusThisTCPdst->winbytes -= win; ArgusThisTCPdst->winnum++; } } ArgusThisTCPdst->ack = tcp->th_ack - 1; } } ArgusThisTCPsrc->win = tcp->th_win; return (retn); } #include void ArgusTCPFlowRecord (struct ArgusFlowStruct *flow, struct ArgusRecord *argus, unsigned char state) { int length = 0; struct ArgusTCPExtensionBuffer *tcpExt = (struct ArgusTCPExtensionBuffer *) flow->NetworkDSRBuffer; struct ArgusTCPObject tcpbuf, *tcp = &tcpbuf; bzero ((char *) tcp, sizeof(*tcp)); if (tcpExt) { tcp->type = ARGUS_TCP_DSR; tcp->length = sizeof(struct ArgusTCPObject); tcp->state = tcpExt->status; tcp->state &= ~ARGUS_RESET; if (tcpExt->src.status & ARGUS_RESET) tcp->state |= ARGUS_SRC_RESET; if (tcpExt->dst.status & ARGUS_RESET) tcp->state |= ARGUS_DST_RESET; tcp->state &= ~ARGUS_PKTS_RETRANS; if (tcpExt->src.status & ARGUS_PKTS_RETRANS) tcp->state |= ARGUS_SRC_PKTS_RETRANS; if (tcpExt->dst.status & ARGUS_PKTS_RETRANS) tcp->state |= ARGUS_DST_PKTS_RETRANS; tcp->state &= ~ARGUS_WINDOW_SHUT; if (tcpExt->src.status & ARGUS_WINDOW_SHUT) tcp->state |= ARGUS_SRC_WINDOW_SHUT; if (tcpExt->dst.status & ARGUS_WINDOW_SHUT) tcp->state |= ARGUS_DST_WINDOW_SHUT; tcp->synAckuSecs = tcpExt->synAckuSecs; tcp->ackDatauSecs = tcpExt->ackDatauSecs; tcp->options = tcpExt->options; tcp->src.seqbase = tcpExt->src.seq_base; tcp->dst.seqbase = tcpExt->dst.seq_base; if (tcpExt->src.ack && tcpExt->src.ackbytes) { if (tcpExt->src.ack != tcpExt->src.ackbytes) { if (tcpExt->src.ack > tcpExt->src.ackbytes) tcp->src.ackbytes = (tcpExt->src.ack - 1) - tcpExt->src.ackbytes; } } if (tcpExt->dst.ack && tcpExt->dst.ackbytes) { if (tcpExt->dst.ack > tcpExt->dst.ackbytes) { if (tcpExt->dst.ack > tcpExt->dst.ackbytes) tcp->dst.ackbytes = (tcpExt->dst.ack - 1) - tcpExt->dst.ackbytes; } } tcp->src.rpkts = tcpExt->src.retrans; tcp->dst.rpkts = tcpExt->dst.retrans; if ((tcp->src.bytes = tcpExt->src.bytes) < 0) tcp->src.bytes = 0; if ((tcp->dst.bytes = tcpExt->dst.bytes) < 0) tcp->dst.bytes = 0; tcp->src.win = tcpExt->src.win; tcp->dst.win = tcpExt->dst.win; tcp->src.flags = tcpExt->src.flag; tcp->dst.flags = tcpExt->dst.flag; if ((tcpExt->src.ackbytes = tcpExt->src.ack) < 0) tcpExt->src.ackbytes = 0; if (tcpExt->src.ackbytes == (tcp->src.bytes - 1)) tcpExt->src.ackbytes++; if ((tcpExt->dst.ackbytes = tcpExt->dst.ack) < 0) tcpExt->dst.ackbytes = 0; if (tcpExt->dst.ackbytes == (tcp->dst.bytes - 1)) tcpExt->dst.ackbytes++; if (tcp && ((length = argus->ahdr.length) > 0)) { bcopy ((char *)tcp, &((char *)argus)[length], sizeof(*tcp)); argus->ahdr.length += sizeof(*tcp); } tcpExt->src.count = 0; tcpExt->dst.count = 0; tcpExt->src.bytes = 0; tcpExt->dst.bytes = 0; tcpExt->src.retrans = 0; tcpExt->dst.retrans = 0; tcpExt->src.flag = 0; tcpExt->dst.flag = 0; tcpExt->status &= ~(ARGUS_RESET|ARGUS_PKTS_RETRANS|ARGUS_WINDOW_SHUT|ARGUS_ECN_CONGESTED); tcpExt->src.status &= ~(ARGUS_RESET|ARGUS_PKTS_RETRANS|ARGUS_WINDOW_SHUT|ARGUS_ECN_CONGESTED); tcpExt->dst.status &= ~(ARGUS_RESET|ARGUS_PKTS_RETRANS|ARGUS_WINDOW_SHUT|ARGUS_ECN_CONGESTED); } }