/* For terms of usage/redistribution/modification see the LICENSE file */ /* For authors and contributors see the AUTHORS file */ /*** tcptable.c - table manipulation routines for the IP monitor ***/ #include "iptraf-ng-compat.h" #include "tui/winops.h" #include "options.h" #include "tcptable.h" #include "deskman.h" #include "attrs.h" #include "log.h" #include "revname.h" #include "rvnamed.h" #include "servname.h" #include "hostmon.h" #include "sockaddr.h" #define MSGSTRING_MAX 320 unsigned int bmaxy = 0; unsigned int imaxy = 0; static void setlabels(WINDOW *win, int mode) { wmove(win, 0, 42 * COLS / 80); whline(win, ACS_HLINE, 23 * COLS / 80); if (mode == 0) { wmove(win, 0, 47 * COLS / 80); wprintw(win, " Packets "); wmove(win, 0, 59 * COLS / 80); wprintw(win, " Bytes "); } else if (mode == 1) { mvwprintw(win, 0, 47 * COLS / 80, " Source MAC Addr "); } else if (mode == 2) { wmove(win, 0, 45 * COLS / 80); wprintw(win, " Pkt Size "); wmove(win, 0, 56 * COLS / 80); wprintw(win, " Win Size "); } } /* * The hash function for the TCP hash table */ static unsigned int tcp_hash(struct sockaddr_storage *saddr, struct sockaddr_storage *daddr, char *ifname) { size_t i; unsigned int ifsum = 0; for (i = 0; i <= strlen(ifname) - 1; i++) ifsum += ifname[i]; switch (saddr->ss_family) { case AF_INET: ifsum += 4 * ((struct sockaddr_in *)saddr)->sin_addr.s_addr; ifsum += 3 * ((struct sockaddr_in *)saddr)->sin_port; break; case AF_INET6: { unsigned int ip6sum = 0; for (i = 0; i < 4; i++) ip6sum ^= ((struct sockaddr_in6 *)saddr)->sin6_addr.s6_addr32[i]; ifsum += 4 * ip6sum; ifsum += 3 * ((struct sockaddr_in6 *)saddr)->sin6_port; break; } default: die("%s(): saddr: unknown address family", __FUNCTION__); } switch (daddr->ss_family) { case AF_INET: ifsum += 2 * ((struct sockaddr_in *)daddr)->sin_addr.s_addr; ifsum += ((struct sockaddr_in *)daddr)->sin_port; break; case AF_INET6: { unsigned int ip6sum = 0; for (i = 0; i < 4; i++) ip6sum ^= ((struct sockaddr_in6 *)daddr)->sin6_addr.s6_addr32[i]; ifsum += 2 * ip6sum; ifsum += ((struct sockaddr_in6 *)daddr)->sin6_port; break; } default: die("%s(): daddr: unknown address family", __FUNCTION__); } return (ifsum % ENTRIES_IN_HASH_TABLE); } static void print_tcp_num_entries(struct tcptable *table) { mvwprintw(table->borderwin, table->bmaxy - 1, 1, " TCP: %6u entries ", table->count); } void init_tcp_table(struct tcptable *table) { int i; table->bmaxy = LINES * 0.6; /* 60% of total screen */ table->imaxy = table->bmaxy - 2; table->borderwin = newwin(table->bmaxy, COLS, 1, 0); table->borderpanel = new_panel(table->borderwin); wattrset(table->borderwin, BOXATTR); tx_box(table->borderwin, ACS_VLINE, ACS_HLINE); wmove(table->borderwin, 0, 1); wprintw(table->borderwin, " TCP Connections (Source Host:Port) "); setlabels(table->borderwin, 0); /* initially use mode 0 */ wmove(table->borderwin, 0, 65 * COLS / 80); wprintw(table->borderwin, " Flag "); wmove(table->borderwin, 0, 70 * COLS / 80); wprintw(table->borderwin, " Iface "); update_panels(); doupdate(); table->ifnamew = COLS - (70 * COLS / 80) - 3; if (table->ifnamew < 7) table->ifnamew = 7; if (table->ifnamew > IFNAMSIZ) table->ifnamew = IFNAMSIZ; table->head = table->tail = NULL; table->firstvisible = table->lastvisible = NULL; table->tcpscreen = newwin(table->imaxy, COLS - 2, 2, 1); table->tcppanel = new_panel(table->tcpscreen); table->closedentries = table->closedtail = NULL; wattrset(table->tcpscreen, BOXATTR); tx_colorwin(table->tcpscreen); table->lastpos = 0; table->count = 0; wtimeout(table->tcpscreen, -1); tx_stdwinset(table->tcpscreen); print_tcp_num_entries(table); /* * Initialize hash table to nulls */ for (i = 0; i <= ENTRIES_IN_HASH_TABLE - 1; i++) { table->hash_table[i] = NULL; table->hash_tails[i] = NULL; } table->barptr = NULL; table->baridx = 0; } /* * Add a TCP entry to the hash table. */ static void add_tcp_hash_entry(struct tcptable *table, struct tcptableent *entry) { unsigned int hp; /* hash position in table */ struct tcp_hashentry *ptmp; hp = tcp_hash(&entry->saddr, &entry->daddr, entry->ifname); ptmp = xmallocz(sizeof(struct tcp_hashentry)); /* * Add backpointer from screen node to hash node for deletion later * (Actually point to its predecessor coz of the header cell). */ entry->hash_node = ptmp; /* * Update hash node and add it to list. */ ptmp->tcpnode = entry; ptmp->hp = hp; if (table->hash_table[hp] == NULL) { ptmp->prev_entry = NULL; table->hash_table[hp] = ptmp; ptmp->index = 1; } if (table->hash_tails[hp] != NULL) { table->hash_tails[hp]->next_entry = ptmp; ptmp->prev_entry = table->hash_tails[hp]; ptmp->index = ptmp->prev_entry->index + 1; } table->hash_tails[hp] = ptmp; ptmp->next_entry = NULL; } /* * Delete a hash table node */ static void del_tcp_hash_node(struct tcptable *table, struct tcptableent *entry) { struct tcp_hashentry *ptmp; ptmp = entry->hash_node; /* ptmp now points to the target */ /* * If the targeted node is the last entry, adjust the corresponding tail * pointer to the preceeding node; */ if (ptmp->next_entry == NULL) table->hash_tails[ptmp->hp] = ptmp->prev_entry; if (ptmp->prev_entry != NULL) ptmp->prev_entry->next_entry = ptmp->next_entry; else table->hash_table[ptmp->hp] = ptmp->next_entry; if (ptmp->next_entry != NULL) ptmp->next_entry->prev_entry = ptmp->prev_entry; free(ptmp); } /* * Add a new entry to the TCP screen table */ struct tcptableent *addentry(struct tcptable *table, struct sockaddr_storage *saddr, struct sockaddr_storage *daddr, int protocol, char *ifname, int *rev_lookup, int rvnfd) { struct tcptableent *new_entry; struct closedlist *ctemp; /* * Allocate and attach a new node if no closed entries found */ if (table->closedentries == NULL) { new_entry = xmalloc(sizeof(struct tcptableent)); new_entry->oth_connection = xmalloc(sizeof(struct tcptableent)); new_entry->oth_connection->oth_connection = new_entry; if (table->head == NULL) { new_entry->prev_entry = NULL; table->head = new_entry; table->firstvisible = new_entry; } if (table->tail != NULL) { table->tail->next_entry = new_entry; new_entry->prev_entry = table->tail; } table->lastpos++; new_entry->index = table->lastpos; table->lastpos++; new_entry->oth_connection->index = table->lastpos; table->tail = new_entry->oth_connection; new_entry->next_entry = new_entry->oth_connection; new_entry->next_entry->prev_entry = new_entry; new_entry->next_entry->next_entry = NULL; if (new_entry->oth_connection->index <= table->firstvisible->index + (table->imaxy - 1)) table->lastvisible = new_entry->oth_connection; else if (new_entry->index <= table->firstvisible->index + (table->imaxy - 1)) table->lastvisible = new_entry; new_entry->reused = new_entry->oth_connection->reused = 0; table->count++; rate_alloc(&new_entry->rate, 5); rate_alloc(&new_entry->oth_connection->rate, 5); print_tcp_num_entries(table); } else { /* * If we reach this point, we're allocating off the list of closed * entries. In this case, we take the top entry, let the new_entry * variable point to whatever the top is pointing to. The new_entry's * oth_connection also points to the reused entry's oth_connection */ new_entry = table->closedentries->closedentry; new_entry->oth_connection = table->closedentries->pair; ctemp = table->closedentries; table->closedentries = table->closedentries->next_entry; free(ctemp); /* * Mark the closed list's tail as NULL if we use the last entry * in the list to prevent a dangling reference. */ if (table->closedentries == NULL) table->closedtail = NULL; new_entry->reused = new_entry->oth_connection->reused = 1; /* * Delete the old hash entries for this reallocated node; */ del_tcp_hash_node(table, new_entry); del_tcp_hash_node(table, new_entry->oth_connection); } /* * Fill in address fields with raw IP addresses */ sockaddr_copy(&new_entry->saddr, saddr); sockaddr_copy(&new_entry->oth_connection->daddr, saddr); sockaddr_copy(&new_entry->daddr, daddr); sockaddr_copy(&new_entry->oth_connection->saddr, daddr); new_entry->protocol = protocol; /* * Initialize count fields */ new_entry->pcount = new_entry->bcount = 0; new_entry->win = new_entry->psize = 0; new_entry->timedout = new_entry->oth_connection->timedout = 0; new_entry->oth_connection->pcount = new_entry->oth_connection->bcount = 0; new_entry->oth_connection->win = new_entry->oth_connection->psize = 0; /* * Store interface name */ strcpy(new_entry->ifname, ifname); strcpy(new_entry->oth_connection->ifname, ifname); /* * Zero out MAC address fields */ memset(new_entry->smacaddr, 0, sizeof(new_entry->smacaddr)); memset(new_entry->oth_connection->smacaddr, 0, sizeof(new_entry->oth_connection->smacaddr)); new_entry->stat = new_entry->oth_connection->stat = 0; new_entry->s_fstat = revname(rev_lookup, &new_entry->saddr, new_entry->s_fqdn, sizeof(new_entry->s_fqdn), rvnfd); new_entry->d_fstat = revname(rev_lookup, &new_entry->daddr, new_entry->d_fqdn, sizeof(new_entry->d_fqdn), rvnfd); /* set port service names (where applicable) */ servlook(sockaddr_get_port(saddr), IPPROTO_TCP, new_entry->s_sname, 10); servlook(sockaddr_get_port(daddr), IPPROTO_TCP, new_entry->d_sname, 10); strcpy(new_entry->oth_connection->s_sname, new_entry->d_sname); strcpy(new_entry->oth_connection->d_sname, new_entry->s_sname); strcpy(new_entry->oth_connection->d_fqdn, new_entry->s_fqdn); strcpy(new_entry->oth_connection->s_fqdn, new_entry->d_fqdn); new_entry->oth_connection->s_fstat = new_entry->d_fstat; new_entry->oth_connection->d_fstat = new_entry->s_fstat; if (new_entry->index < new_entry->oth_connection->index) { new_entry->half_bracket = ACS_ULCORNER; new_entry->oth_connection->half_bracket = ACS_LLCORNER; } else { new_entry->half_bracket = ACS_LLCORNER; new_entry->oth_connection->half_bracket = ACS_ULCORNER; } new_entry->inclosed = new_entry->oth_connection->inclosed = 0; new_entry->finack = new_entry->oth_connection->finack = 0; new_entry->finsent = new_entry->oth_connection->finsent = 0; new_entry->partial = new_entry->oth_connection->partial = 0; new_entry->spanbr = new_entry->oth_connection->spanbr = 0; new_entry->conn_starttime = new_entry->oth_connection->conn_starttime = time(NULL); rate_init(&new_entry->rate); rate_init(&new_entry->oth_connection->rate); /* * Mark flow rate start time and byte counter for flow computation * if the highlight bar is on either flow of the new connection. */ if (table->barptr == new_entry) { new_entry->starttime = time(NULL); new_entry->spanbr = 0; } else if (table->barptr == new_entry->oth_connection) { new_entry->oth_connection->starttime = time(NULL); new_entry->oth_connection->spanbr = 0; } /* * Add entries to hash table */ add_tcp_hash_entry(table, new_entry); add_tcp_hash_entry(table, new_entry->oth_connection); return new_entry; } void addtoclosedlist(struct tcptable *table, struct tcptableent *entry) { struct closedlist *ctemp; ctemp = xmalloc(sizeof(struct closedlist)); /* * Point to closed entries */ ctemp->closedentry = entry; ctemp->pair = entry->oth_connection; entry->inclosed = entry->oth_connection->inclosed = 1; /* * Add node to closed entry list. */ if (table->closedtail != NULL) table->closedtail->next_entry = ctemp; table->closedtail = ctemp; table->closedtail->next_entry = NULL; if (table->closedentries == NULL) table->closedentries = ctemp; } static char *tcplog_flowrate_msg(struct tcptableent *entry, char *buf, size_t bufsize) { time_t interval = time(NULL) - entry->conn_starttime; if (interval < 1) interval = 1; char rbuf[64]; rate_print(entry->bcount / interval, rbuf, sizeof(rbuf)); snprintf(buf, bufsize - 1, "avg flow rate %s", rbuf); buf[bufsize - 1] = '\0'; return buf; } void write_timeout_log(int logging, FILE *logfile, struct tcptableent *tcpnode) { char msgstring[MSGSTRING_MAX]; if (logging) { char flowrate1[64]; char flowrate2[64]; snprintf(msgstring, MSGSTRING_MAX, "TCP; Connection %s:%s to %s:%s timed out, %lu packets, %lu bytes, %s; opposite direction %lu packets, %lu bytes, %s", tcpnode->s_fqdn, tcpnode->s_sname, tcpnode->d_fqdn, tcpnode->d_sname, tcpnode->pcount, tcpnode->bcount, tcplog_flowrate_msg(tcpnode, flowrate1, sizeof(flowrate1)), tcpnode->oth_connection->pcount, tcpnode->oth_connection->bcount, tcplog_flowrate_msg(tcpnode->oth_connection, flowrate2, sizeof(flowrate2))); writelog(logging, logfile, msgstring); } } struct tcptableent *in_table(struct tcptable *table, struct sockaddr_storage *saddr, struct sockaddr_storage *daddr, char *ifname, int logging, FILE *logfile, time_t timeout) { struct tcp_hashentry *hashptr; unsigned int hp; time_t now; if (table->head == NULL) { return 0; } /* * Determine hash table index for this set of addresses and ports */ hp = tcp_hash(saddr, daddr, ifname); hashptr = table->hash_table[hp]; while (hashptr != NULL) { if (sockaddr_is_equal(&hashptr->tcpnode->saddr, saddr) && sockaddr_is_equal(&hashptr->tcpnode->daddr, daddr) && (strcmp(hashptr->tcpnode->ifname, ifname) == 0)) break; now = time(NULL); /* * Add the timed out entries to the closed list in case we didn't * find any closed ones. */ if ((timeout > 0) && ((now - hashptr->tcpnode->lastupdate) / 60 > timeout) && (!(hashptr->tcpnode->inclosed))) { hashptr->tcpnode->timedout = 1; hashptr->tcpnode->oth_connection->timedout = 1; addtoclosedlist(table, hashptr->tcpnode); if (logging) write_timeout_log(logging, logfile, hashptr->tcpnode); } hashptr = hashptr->next_entry; } if (hashptr != NULL) { /* needed to avoid SIGSEGV */ if ((((hashptr->tcpnode->finsent == 2) && (hashptr->tcpnode->oth_connection->finsent == 2))) || (((hashptr->tcpnode->stat & FLAG_RST) || (hashptr->tcpnode->oth_connection-> stat & FLAG_RST)))) { return NULL; } else { return hashptr->tcpnode; } } else { return NULL; } } /* * Update the TCP status record should an applicable packet arrive. */ void updateentry(struct tcptable *table, struct tcptableent *tableentry, struct tcphdr *transpacket, char *packet, int linkproto, unsigned long packetlength, unsigned int bcount, unsigned int fragofs, int logging, int *revlook, int rvnfd, FILE *logfile) { char msgstring[MSGSTRING_MAX]; char newmacaddr[18]; if (tableentry->s_fstat != RESOLVED) { tableentry->s_fstat = revname(revlook, &tableentry->saddr, tableentry->s_fqdn, sizeof(tableentry->s_fqdn), rvnfd); strcpy(tableentry->oth_connection->d_fqdn, tableentry->s_fqdn); tableentry->oth_connection->d_fstat = tableentry->s_fstat; } if (tableentry->d_fstat != RESOLVED) { tableentry->d_fstat = revname(revlook, &tableentry->daddr, tableentry->d_fqdn, sizeof(tableentry->d_fqdn), rvnfd); strcpy(tableentry->oth_connection->s_fqdn, tableentry->d_fqdn); tableentry->oth_connection->s_fstat = tableentry->d_fstat; } tableentry->pcount++; tableentry->bcount += bcount; tableentry->psize = packetlength; tableentry->spanbr += bcount; if (options.mac) { memset(newmacaddr, 0, sizeof(newmacaddr)); /* change updateentry to take struct pkt to remove this */ if (linkproto == ARPHRD_ETHER) { convmacaddr((char *) (((struct ethhdr *) packet)-> h_source), newmacaddr); } else if (linkproto == ARPHRD_FDDI) { convmacaddr((char *) (((struct fddihdr *) packet)-> saddr), newmacaddr); } if (tableentry->smacaddr[0] != '\0') { if (strcmp(tableentry->smacaddr, newmacaddr) != 0) { snprintf(msgstring, MSGSTRING_MAX, "TCP; %s; from %s:%s to %s:%s: new source MAC address %s (previously %s)", tableentry->ifname, tableentry->s_fqdn, tableentry->s_sname, tableentry->d_fqdn, tableentry->d_sname, newmacaddr, tableentry->smacaddr); writelog(logging, logfile, msgstring); strcpy(tableentry->smacaddr, newmacaddr); } } else strcpy(tableentry->smacaddr, newmacaddr); } /* * If this is not the first TCP fragment, skip interpretation of the * TCP header. */ if ((ntohs(fragofs) & 0x1fff) != 0) { tableentry->lastupdate = tableentry->oth_connection->lastupdate = time(NULL); return; } /* * At this point, we have a TCP header, and we proceed to process it. */ if (tableentry->pcount == 1) { if ((transpacket->syn) || (transpacket->rst)) tableentry->partial = 0; else tableentry->partial = 1; } tableentry->win = ntohs(transpacket->window); tableentry->stat = 0; if (transpacket->syn) tableentry->stat |= FLAG_SYN; if (transpacket->ack) { tableentry->stat |= FLAG_ACK; /* * The following sequences are used when the ACK is in response to * a FIN (see comments for FIN below). If the opposite direction * already has its indicator set to 1 (FIN sent, not ACKed), and * the incoming ACK has the same sequence number as the previously * stored FIN's ack number (i.e. the ACK in response to the opposite * flow's FIN), the opposite direction's state is set to 2 (FIN sent * and ACKed). */ if ((tableentry->oth_connection->finsent == 1) && (ntohl(transpacket->seq) == tableentry->oth_connection->finack)) { tableentry->oth_connection->finsent = 2; if (logging) { writetcplog(logging, logfile, tableentry, tableentry->psize, "FIN acknowleged"); } } } /* * The closing sequence is similar, but not identical to the TCP close * sequence described in the RFC. This sequence is primarily cosmetic. * * When a FIN is sent in a direction, a state indicator is set to 1, * to indicate a FIN sent, but not ACKed yet. For comparison later, * the acknowlegement number is also saved in the entry. See comments * in ACK above. */ if (transpacket->fin) { /* * First, we check if the opposite direction has no counts, in which * case we simply mark the entire connection available for reuse. * This is in case packets from a machine pass an interface, but * on the return, completely bypasses any interface on our machine. * * Q: Could such a situation really happen in practice? I managed to * do it but under *really* ridiculous circumstances. * * A: (as of version 2.5.0, June 2001): Yes this DOES happen in * practice. Unidirectional satellite feeds can send data straight * to a remote network using you as your upstream. */ if (tableentry->oth_connection->pcount == 0) addtoclosedlist(table, tableentry); else { /* * That aside, mark the direction as being done, and make it * ready for a complete close upon receipt of an ACK. We save * the acknowlegement number for identification of the proper * ACK packet when it arrives in the other direction. */ tableentry->finsent = 1; tableentry->finack = ntohl(transpacket->ack_seq); } if (logging) { char flowrate[64]; sprintf(msgstring, "FIN sent; %lu packets, %lu bytes, %s", tableentry->pcount, tableentry->bcount, tcplog_flowrate_msg(tableentry, flowrate, sizeof(flowrate))); writetcplog(logging, logfile, tableentry, tableentry->psize, msgstring); } } if (transpacket->rst) { tableentry->stat |= FLAG_RST; if (!(tableentry->inclosed)) addtoclosedlist(table, tableentry); if (logging) { char flowrate1[64]; char flowrate2[64]; snprintf(msgstring, MSGSTRING_MAX, "Connection reset; %lu packets, %lu bytes, %s; opposite direction %lu packets, %lu bytes; %s", tableentry->pcount, tableentry->bcount, tcplog_flowrate_msg(tableentry, flowrate1, sizeof(flowrate1)), tableentry->oth_connection->pcount, tableentry->oth_connection->bcount, tcplog_flowrate_msg(tableentry->oth_connection, flowrate2, sizeof(flowrate2))); writetcplog(logging, logfile, tableentry, tableentry->psize, msgstring); } } if (transpacket->psh) tableentry->stat |= FLAG_PSH; if (transpacket->urg) tableentry->stat |= FLAG_URG; tableentry->lastupdate = tableentry->oth_connection->lastupdate = time(NULL); /* * Shall we add this entry to the closed entry list? If both * directions have their state indicators set to 2, or one direction * is set to 2, and the other 1, that's it. */ if ((!tableentry->inclosed) && (((tableentry->finsent == 2) && ((tableentry->oth_connection->finsent == 1) || (tableentry->oth_connection->finsent == 2))) || ((tableentry->oth_connection->finsent == 2) && ((tableentry->finsent == 1) || (tableentry->finsent == 2))))) addtoclosedlist(table, tableentry); } /* * Clears out the resolved IP addresses from the window. This prevents * overlapping port numbers (in cases where the resolved DNS name is shorter * than its IP address), that may cause the illusion of large ports. Plus, * such output, while may be interpreted by people with a little know-how, * is just plain wrong. * * Returns immediately if the entry is not visible in the window. */ void clearaddr(struct tcptable *table, struct tcptableent *tableentry, unsigned int screen_idx) { unsigned int target_row; if ((tableentry->index < screen_idx) || (tableentry->index > screen_idx + (table->imaxy - 1))) return; target_row = (tableentry->index) - screen_idx; wmove(table->tcpscreen, target_row, 1); wprintw(table->tcpscreen, "%44c", ' '); } /* * Display a TCP connection line. Returns immediately if the entry is * not visible in the window. */ void printentry(struct tcptable *table, struct tcptableent *tableentry, unsigned int screen_idx, int mode) { char stat[7] = ""; unsigned int target_row; char sp_buf[MSGSTRING_MAX]; int normalattr; int highattr; /* * Set appropriate attributes for this entry */ if (table->barptr == tableentry) { normalattr = BARSTDATTR; highattr = BARHIGHATTR; } else { normalattr = STDATTR; highattr = HIGHATTR; } if ((tableentry->index < screen_idx) || (tableentry->index > screen_idx + (table->imaxy - 1))) return; target_row = (tableentry->index) - screen_idx; /* clear the data if it's a reused entry */ wattrset(table->tcpscreen, PTRATTR); wmove(table->tcpscreen, target_row, 2); if (tableentry->reused) { scrollok(table->tcpscreen, 0); sprintf(sp_buf, "%%%dc", COLS - 4); wprintw(table->tcpscreen, sp_buf, ' '); scrollok(table->tcpscreen, 1); tableentry->reused = 0; wmove(table->tcpscreen, target_row, 1); } /* print half of connection indicator bracket */ wmove(table->tcpscreen, target_row, 0); waddch(table->tcpscreen, tableentry->half_bracket); /* proceed with the actual entry */ wattrset(table->tcpscreen, normalattr); sprintf(sp_buf, "%%%dc", COLS - 5); mvwprintw(table->tcpscreen, target_row, 2, sp_buf, ' '); sprintf(sp_buf, "%%.%ds:%%.%ds", 32 * COLS / 80, 10); wmove(table->tcpscreen, target_row, 1); wprintw(table->tcpscreen, sp_buf, tableentry->s_fqdn, tableentry->s_sname); wattrset(table->tcpscreen, highattr); /* * Print packet and byte counts or window size and packet size, depending * on the value of mode. */ switch (mode) { case 0: wmove(table->tcpscreen, target_row, 47 * COLS / 80 - 2); if (tableentry->partial) wprintw(table->tcpscreen, ">"); else wprintw(table->tcpscreen, "="); wprintw(table->tcpscreen, "%8u ", tableentry->pcount); wmove(table->tcpscreen, target_row, 59 * COLS / 80 - 4); wprintw(table->tcpscreen, "%9u ", tableentry->bcount); break; case 1: wmove(table->tcpscreen, target_row, 50 * COLS / 80); if (tableentry->smacaddr[0] == '\0') wprintw(table->tcpscreen, " N/A "); else wprintw(table->tcpscreen, "%s", tableentry->smacaddr); break; case 2: wmove(table->tcpscreen, target_row, 45 * COLS / 80 + 3); wprintw(table->tcpscreen, "%5u ", tableentry->psize); wmove(table->tcpscreen, target_row, 56 * COLS / 80 - 1); wprintw(table->tcpscreen, "%9u ", tableentry->win); } wattrset(table->tcpscreen, normalattr); if (tableentry->finsent == 1) strcpy(stat, "DONE"); else if (tableentry->finsent == 2) strcpy(stat, "CLOS"); else if (tableentry->stat & FLAG_RST) strcpy(stat, "RSET"); else { strcat(stat, (tableentry->stat & FLAG_SYN) ? "S" : "-"); strcat(stat, (tableentry->stat & FLAG_PSH) ? "P" : "-"); strcat(stat, (tableentry->stat & FLAG_ACK) ? "A" : "-"); strcat(stat, (tableentry->stat & FLAG_URG) ? "U" : "-"); } wmove(table->tcpscreen, target_row, 65 * COLS / 80); wprintw(table->tcpscreen, "%4.4s", stat); wmove(table->tcpscreen, target_row, 70 * COLS / 80); wprintw(table->tcpscreen, "%-*.*s", table->ifnamew, table->ifnamew, tableentry->ifname); } /* * Redraw the TCP window */ void refreshtcpwin(struct tcptable *table, unsigned int idx, int mode) { struct tcptableent *ptmp; setlabels(table->borderwin, mode); wattrset(table->tcpscreen, STDATTR); tx_colorwin(table->tcpscreen); ptmp = table->firstvisible; while ((ptmp != NULL) && (ptmp->prev_entry != table->lastvisible)) { printentry(table, ptmp, idx, mode); ptmp = ptmp->next_entry; } wmove(table->borderwin, table->bmaxy - 1, 1); print_tcp_num_entries(table); update_panels(); doupdate(); } static void destroy_closed_entries(struct tcptable *table) { struct closedlist *closedtemp; struct closedlist *closedtemp_next; if (table->closedentries != NULL) { closedtemp = table->closedentries; closedtemp_next = table->closedentries->next_entry; while (closedtemp != NULL) { free(closedtemp); closedtemp = closedtemp_next; if (closedtemp_next != NULL) closedtemp_next = closedtemp_next->next_entry; } table->closedentries = NULL; table->closedtail = NULL; } } /* * Kill the entire TCP table */ void destroytcptable(struct tcptable *table) { struct tcptableent *ctemp; struct tcptableent *c_next_entry; struct tcp_hashentry *hashtemp; struct tcp_hashentry *hashtemp_next; unsigned int i; /* * Destroy main TCP table */ if (table->head != NULL) { ctemp = table->head; c_next_entry = table->head->next_entry; while (ctemp != NULL) { rate_destroy(&ctemp->rate); free(ctemp); ctemp = c_next_entry; if (c_next_entry != NULL) c_next_entry = c_next_entry->next_entry; } } /* * Destroy list of closed entries */ destroy_closed_entries(table); /* * Destroy hash table */ for (i = 0; i <= ENTRIES_IN_HASH_TABLE - 1; i++) { if (table->hash_table[i] != NULL) { hashtemp = table->hash_table[i]; hashtemp_next = table->hash_table[i]->next_entry; while (hashtemp != NULL) { free(hashtemp); hashtemp = hashtemp_next; if (hashtemp_next != NULL) hashtemp_next = hashtemp_next->next_entry; } } } } /* * Kill an entry from the TCP table */ static void destroy_tcp_entry(struct tcptable *table, struct tcptableent *ptmp) { if (ptmp->prev_entry != NULL) ptmp->prev_entry->next_entry = ptmp->next_entry; else table->head = ptmp->next_entry; if (ptmp->next_entry != NULL) ptmp->next_entry->prev_entry = ptmp->prev_entry; else table->tail = ptmp->prev_entry; rate_destroy(&ptmp->rate); free(ptmp); if (table->head == NULL) { table->firstvisible = NULL; table->lastvisible = NULL; } } /* * Kill all closed entries from the table, and clear the list of closed * entries. */ void flushclosedentries(struct tcptable *table, unsigned long *screen_idx, int logging, FILE *logfile) { struct tcptableent *ptmp = table->head; struct tcptableent *ctmp = NULL; unsigned long idx = 1; time_t now; time_t lastupdated = 0; while (ptmp != NULL) { now = time(NULL); lastupdated = (now - ptmp->lastupdate) / 60; if ((ptmp->inclosed) || (lastupdated > options.timeout)) { ctmp = ptmp; /* * Mark and flush timed out TCP entries. */ if (lastupdated > options.timeout) { if ((!(ptmp->timedout)) && (!(ptmp->inclosed))) { write_timeout_log(logging, logfile, ptmp); ptmp->timedout = ptmp->oth_connection->timedout = 1; } } /* * Advance to next entry and destroy target entry. */ ptmp = ptmp->next_entry; /* * If the targeted entry is highlighted, and the next entry is * not NULL (we're still in the list) we move the bar pointer to * the next entry otherwise we move it to the previous entry. */ if (ptmp != NULL) { if (table->barptr == ctmp) { table->barptr = ptmp; } } else { if (table->barptr == ctmp) { table->barptr = table->barptr->prev_entry; } } /* * Do the dirty deed */ del_tcp_hash_node(table, ctmp); destroy_tcp_entry(table, ctmp); /* * Adjust screen index if the deleted entry was "above" * the screen. */ if (idx < *screen_idx) (*screen_idx)--; } else { /* * Set the first visible pointer once the index matches * the screen index. */ if (idx == *screen_idx) table->firstvisible = ptmp; /* * Keep setting the last visible pointer until the scan * index "leaves" the screen */ if (idx <= (*screen_idx) + (table->imaxy - 1)) table->lastvisible = ptmp; ptmp->index = idx; idx++; ptmp = ptmp->next_entry; } } table->lastpos = idx - 1; table->count = table->lastpos / 2; destroy_closed_entries(table); /* * Shift entries down if the deletion causes the last entry to * occupy anywhere other than the last line of the TCP display * window. */ if (table->head != NULL) { /* * Point screen index to the last table entry if the tail entry is * "above" the screen index. Set the firstvisible pointer to that * as well. */ if (table->tail->index < *screen_idx) { *screen_idx = table->tail->index; table->firstvisible = table->tail; } /* * Move the screen index and firstvisible entry up until the tail * hits the bottom of the window (tail is at screen index plus * screen length minus 1) or the firstvisible pointer hits the * head of the table. The highlight bar should "go along" with * the shifting. */ while ((table->tail->index < *screen_idx + table->imaxy - 1) && (table->firstvisible->prev_entry != NULL)) { table->firstvisible = table->firstvisible->prev_entry; (*screen_idx)--; } /* * Set the bar position index once everything's done. */ table->baridx = table->barptr->index - *screen_idx + 1; } } void writetcplog(int logging, FILE *fd, struct tcptableent *entry, unsigned int pktlen, char *message) { char msgbuf[MSGSTRING_MAX]; if (logging) { if (options.mac) { snprintf(msgbuf, MSGSTRING_MAX, "TCP; %s; %u bytes; from %s:%s to %s:%s (source MAC addr %s); %s", entry->ifname, pktlen, entry->s_fqdn, entry->s_sname, entry->d_fqdn, entry->d_sname, entry->smacaddr, message); } else { snprintf(msgbuf, MSGSTRING_MAX, "TCP; %s; %u bytes; from %s:%s to %s:%s; %s", entry->ifname, pktlen, entry->s_fqdn, entry->s_sname, entry->d_fqdn, entry->d_sname, message); } writelog(logging, fd, msgbuf); } } void write_tcp_unclosed(int logging, FILE *fd, struct tcptable *table) { char msgbuf[MSGSTRING_MAX]; struct tcptableent *entry = table->head; while (entry != NULL) { if ((entry->finsent == 0) && ((entry->stat & FLAG_RST) == 0) && (!(entry->inclosed))) { sprintf(msgbuf, "TCP; %s; active; from %s:%s to %s:%s; %lu packets, %lu bytes", entry->ifname, entry->s_fqdn, entry->s_sname, entry->d_fqdn, entry->d_sname, entry->pcount, entry->bcount); writelog(logging, fd, msgbuf); } entry = entry->next_entry; } }