void process_dest_unreach(struct tcptable *table, char *packet, char *ifname, int *nomem) { struct iphdr *ip; struct tcphdr *tcp; struct tcptableent *tcpentry; ip = (struct iphdr *) (packet + 8); if (ip->protocol != IPPROTO_TCP) return; tcp = (struct tcphdr *) (packet + 8 + (ip->ihl * 4)); /* * We really won't be making use of nomem here. Timeout checking * won't be performed either, so we just pass NULL as the pointer * to the configuration structure. in_table() will recognize this * and set its internal timeout variable to 0. */ tcpentry = in_table(table, ip->saddr, ip->daddr, ntohs(tcp->source), ntohs(tcp->dest), ifname, 0, NULL, nomem, NULL); if (tcpentry != NULL) { tcpentry->stat = tcpentry->oth_connection->stat = FLAG_RST; addtoclosedlist(table, tcpentry, nomem); } }
void process_dest_unreach(struct tcptable *table, char *packet, char *ifname) { struct iphdr *ip; struct ip6_hdr *ip6; struct tcphdr *tcp; struct tcptableent *tcpentry; ip = (struct iphdr *) (packet + 8); /* * Timeout checking won't be performed either, so we just pass 0 * as timeout variable. */ if (ip->version == 6) { ip6 = (struct ip6_hdr *) (packet + 8); if (ip6->ip6_nxt != IPPROTO_TCP) return; tcp = (struct tcphdr *) (packet + 48); struct sockaddr_storage saddr, daddr; sockaddr_make_ipv6(&saddr, &ip6->ip6_src); sockaddr_set_port(&saddr, ntohs(tcp->source)); sockaddr_make_ipv6(&daddr, &ip6->ip6_dst); sockaddr_set_port(&daddr, ntohs(tcp->dest)); tcpentry = in_table(table, &saddr, &daddr, ifname, 0, NULL, 0); } else { if (ip->protocol != IPPROTO_TCP) return; tcp = (struct tcphdr *) (packet + 8 + (ip->ihl * 4)); struct sockaddr_storage saddr, daddr; sockaddr_make_ipv4(&saddr, ip->saddr); sockaddr_set_port(&saddr, ntohs(tcp->source)); sockaddr_make_ipv4(&daddr, ip->daddr); sockaddr_set_port(&daddr, ntohs(tcp->dest)); tcpentry = in_table(table, &saddr, &daddr, ifname, 0, NULL, 0); } if (tcpentry != NULL) { tcpentry->stat = tcpentry->oth_connection->stat = FLAG_RST; addtoclosedlist(table, tcpentry); } }
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); }
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; } }
struct tcptableent *in_table(struct tcptable *table, unsigned long saddr, unsigned long daddr, unsigned int sport, unsigned int dport, char *ifname, int logging, FILE * logfile, int *nomem, struct OPTIONS *opts) { struct tcp_hashentry *hashptr; unsigned int hp; // int hastimeouts = 0; time_t now; time_t timeout; if (opts != NULL) timeout = opts->timeout; else timeout = 0; if (table->head == NULL) { return 0; } /* * Determine hash table index for this set of addresses and ports */ hp = tcp_hash(saddr, sport, daddr, dport, ifname); hashptr = table->hash_table[hp]; while (hashptr != NULL) { if ((hashptr->tcpnode->saddr.s_addr == saddr) && (hashptr->tcpnode->daddr.s_addr == daddr) && (hashptr->tcpnode->sport == sport) && (hashptr->tcpnode->dport == dport) && (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, nomem); //if (!(*nomem)) // hastimeouts = 1; if (logging) write_timeout_log(logging, logfile, hashptr->tcpnode, opts); } 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; } }