1diff --git a/configure.in b/configure.in 2index 86f32b8..910b1ec 100644 3--- a/configure.in 4+++ b/configure.in 5@@ -98,6 +98,13 @@ AC_ARG_WITH(libpcap, 6 AC_SUBST(PCAP_CFLAGS) 7 AC_SUBST(PCAPLIB) 8 9+AC_ARG_ENABLE(tcpreasm, 10+[ --enable-tcpreasm enable tcp reassembly support], 11+, 12+[ enable_tcpreasm="no"] 13+) 14+test "${enable_tcpreasm}" = "yes" && CFLAGS="${CFLAGS} -DENABLE_TCPREASM" 15+ 16 dnl Checks for libglib2.8 17 AC_ARG_ENABLE(libglib, 18 [ --disable-libglib use glib2 for multiprocessing support], 19diff --git a/src/libnids.c b/src/libnids.c 20index 42b5f96..4d0273e 100644 21--- a/src/libnids.c 22+++ b/src/libnids.c 23@@ -47,6 +47,9 @@ static struct proc_node *ip_procs; 24 static struct proc_node *udp_procs; 25 26 struct proc_node *tcp_procs; 27+#ifdef ENABLE_TCPREASM 28+struct proc_node *tcp_resume_procs; 29+#endif 30 static int linktype; 31 static pcap_t *desc = NULL; 32 33@@ -109,6 +112,10 @@ struct nids_prm nids_params = { 34 20000, /* queue_limit */ 35 0, /* tcp_workarounds */ 36 NULL /* pcap_desc */ 37+#ifdef ENABLE_TCPREASM 38+ , 39+ 1 /* tcp_resume_wscale */ 40+#endif 41 }; 42 43 static int nids_ip_filter(struct ip *x, int len) 44@@ -470,6 +477,9 @@ static void init_procs() 45 ip_procs->item = gen_ip_proc; 46 ip_procs->next = 0; 47 tcp_procs = 0; 48+#ifdef ENABLE_TCPREASM 49+ tcp_resume_procs = 0; 50+#endif 51 udp_procs = 0; 52 } 53 54diff --git a/src/nids.h b/src/nids.h 55index c44f131..b703c9d 100644 56--- a/src/nids.h 57+++ b/src/nids.h 58@@ -48,10 +48,19 @@ enum 59 # define NIDS_RESET 4 60 # define NIDS_TIMED_OUT 5 61 # define NIDS_EXITING 6 /* nids is exiting; last chance to get data */ 62+#ifdef ENABLE_TCPREASM 63+# define NIDS_RESUME 7 64+#endif 65 66 # define NIDS_DO_CHKSUM 0 67 # define NIDS_DONT_CHKSUM 1 68 69+#ifdef ENABLE_TCPREASM 70+# define NIDS_TCP_RESUME_NONE 0 71+# define NIDS_TCP_RESUME_CLIENT 1 72+# define NIDS_TCP_RESUME_SERVER 2 73+#endif 74+ 75 struct tuple4 76 { 77 u_short source; 78@@ -63,6 +72,10 @@ struct tuple4 79 struct half_stream 80 { 81 char state; 82+#ifdef ENABLE_TCPREASM 83+ char resume_second_half; 84+#endif 85+ 86 char collect; 87 char collect_urg; 88 89@@ -131,6 +144,9 @@ struct nids_prm 90 int queue_limit; 91 int tcp_workarounds; 92 pcap_t *pcap_desc; 93+#ifdef ENABLE_TCPREASM 94+ int tcp_resume_wscale; 95+#endif 96 }; 97 98 struct tcp_timeout 99@@ -148,6 +164,10 @@ void nids_register_ip (void (*)); 100 void nids_unregister_ip (void (*)); 101 void nids_register_tcp (void (*)); 102 void nids_unregister_tcp (void (*x)); 103+#ifdef ENABLE_TCPREASM 104+void nids_register_tcp_resume (void (*)); 105+void nids_unregister_tcp_resume (void (*x)); 106+#endif 107 void nids_register_udp (void (*)); 108 void nids_unregister_udp (void (*)); 109 void nids_killtcp (struct tcp_stream *); 110diff --git a/src/tcp.c b/src/tcp.c 111index f0ee031..08360b6 100644 112--- a/src/tcp.c 113+++ b/src/tcp.c 114@@ -48,6 +48,9 @@ enum { 115 #define EXP_SEQ (snd->first_data_seq + rcv->count + rcv->urg_count) 116 117 extern struct proc_node *tcp_procs; 118+#ifdef ENABLE_TCPREASM 119+extern struct proc_node *tcp_resume_procs; 120+#endif 121 122 static struct tcp_stream **tcp_stream_table; 123 static struct tcp_stream *streams_pool; 124@@ -248,7 +251,103 @@ static int get_wscale(struct tcphdr * this_tcphdr, unsigned int * ws) 125 return ret; 126 } 127 128- 129+#ifdef ENABLE_TCPREASM 130+static struct tcp_stream * 131+initiate_tcp_resume(struct tcphdr * this_tcphdr, struct ip * this_iphdr, int direction) 132+{ 133+ struct tcp_stream *tolink; 134+ struct tcp_stream *a_tcp; 135+ int hash_index; 136+ struct tuple4 addr; 137+ struct half_stream *half; 138+ struct half_stream *other_half; 139+ 140+ switch (direction) 141+ { 142+ case NIDS_TCP_RESUME_CLIENT: 143+ addr.source = ntohs(this_tcphdr->th_sport); 144+ addr.dest = ntohs(this_tcphdr->th_dport); 145+ addr.saddr = this_iphdr->ip_src.s_addr; 146+ addr.daddr = this_iphdr->ip_dst.s_addr; 147+ break; 148+ case NIDS_TCP_RESUME_SERVER: 149+ addr.source = ntohs(this_tcphdr->th_dport); 150+ addr.dest = ntohs(this_tcphdr->th_sport); 151+ addr.saddr = this_iphdr->ip_dst.s_addr; 152+ addr.daddr = this_iphdr->ip_src.s_addr; 153+ break; 154+ default: 155+ return NULL; 156+ } 157+ hash_index = mk_hash_index(addr); 158+ 159+ if (tcp_num > max_stream) { 160+ struct lurker_node *i; 161+ 162+ tcp_oldest->nids_state = NIDS_TIMED_OUT; 163+ for (i = tcp_oldest->listeners; i; i = i->next) 164+ (i->item) (tcp_oldest, &i->data); 165+ nids_free_tcp_stream(tcp_oldest); 166+ nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr); 167+ } 168+ a_tcp = free_streams; 169+ if (!a_tcp) { 170+ fprintf(stderr, "gdb me ...\n"); 171+ pause(); 172+ } 173+ free_streams = a_tcp->next_free; 174+ 175+ tcp_num++; 176+ tolink = tcp_stream_table[hash_index]; 177+ memset(a_tcp, 0, sizeof(struct tcp_stream)); 178+ a_tcp->hash_index = hash_index; 179+ a_tcp->addr = addr; 180+ if (direction == NIDS_TCP_RESUME_CLIENT) 181+ { 182+ half = &a_tcp->client; 183+ other_half = &a_tcp->server; 184+ } 185+ else 186+ { 187+ half = &a_tcp->server; 188+ other_half = &a_tcp->client; 189+ } 190+ half->state = TCP_ESTABLISHED; 191+ half->seq = ntohl(this_tcphdr->th_seq) + 1; 192+ half->first_data_seq = half->seq - 1; 193+ half->window = ntohs(this_tcphdr->th_win); 194+ half->ts_on = 0; 195+ half->wscale = nids_params.tcp_resume_wscale; 196+ if (this_tcphdr->th_flags & TH_ACK) 197+ half->ack_seq = ntohl(this_tcphdr->th_ack); 198+ 199+#ifdef ENABLE_TCPREASM_DEBUG 200+ DEBUG_REASSEMBLY("new connection: seq = %u, ack_seq = %u\n", 201+ half->seq, half->ack_seq); 202+#endif 203+ 204+ other_half->ack_seq = half->seq; 205+ other_half->state = TCP_ESTABLISHED; 206+ other_half->resume_second_half = 1; 207+ other_half->ts_on = 0; 208+ other_half->window = half->window; 209+ other_half->wscale = nids_params.tcp_resume_wscale; 210+ a_tcp->next_node = tolink; 211+ a_tcp->prev_node = 0; 212+ if (tolink) 213+ tolink->prev_node = a_tcp; 214+ tcp_stream_table[hash_index] = a_tcp; 215+ a_tcp->next_time = tcp_latest; 216+ a_tcp->prev_time = 0; 217+ if (!tcp_oldest) 218+ tcp_oldest = a_tcp; 219+ if (tcp_latest) 220+ tcp_latest->prev_time = a_tcp; 221+ tcp_latest = a_tcp; 222+ 223+ return a_tcp; 224+} 225+#endif 226 227 228 static void 229@@ -712,6 +811,9 @@ process_tcp(u_char * data, int skblen) 230 unsigned int tmp_ts; 231 struct tcp_stream *a_tcp; 232 struct half_stream *snd, *rcv; 233+#ifdef ENABLE_TCPREASM 234+ int resumed_tcp = 0; 235+#endif 236 237 ugly_iphdr = this_iphdr; 238 iplen = ntohs(this_iphdr->ip_len); 239@@ -743,6 +845,22 @@ process_tcp(u_char * data, int skblen) 240 this_tcphdr); 241 return; 242 } 243+ 244+#ifdef ENABLE_TCPREASM_DEBUG 245+ DEBUG_REASSEMBLY("process_tcp starting: packet is %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", 246+ (ntohl(this_iphdr->ip_src.s_addr) >> 24) & 0xff, 247+ (ntohl(this_iphdr->ip_src.s_addr) >> 16) & 0xff, 248+ (ntohl(this_iphdr->ip_src.s_addr) >> 8) & 0xff, 249+ (ntohl(this_iphdr->ip_src.s_addr)) & 0xff, 250+ ntohs(this_tcphdr->th_sport), 251+ (ntohl(this_iphdr->ip_dst.s_addr) >> 24) & 0xff, 252+ (ntohl(this_iphdr->ip_dst.s_addr) >> 16) & 0xff, 253+ (ntohl(this_iphdr->ip_dst.s_addr) >> 8) & 0xff, 254+ (ntohl(this_iphdr->ip_dst.s_addr)) & 0xff, 255+ ntohs(this_tcphdr->th_dport) 256+ ); 257+#endif 258+ 259 #if 0 260 check_flags(this_iphdr, this_tcphdr); 261 //ECN 262@@ -750,10 +868,64 @@ process_tcp(u_char * data, int skblen) 263 if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) { 264 if ((this_tcphdr->th_flags & TH_SYN) && 265 !(this_tcphdr->th_flags & TH_ACK) && 266- !(this_tcphdr->th_flags & TH_RST)) 267+ !(this_tcphdr->th_flags & TH_RST)) { 268 add_new_tcp(this_tcphdr, this_iphdr); 269+ return; 270+ } 271+ 272+#ifdef ENABLE_TCPREASM 273+#ifdef ENABLE_TCPREASM_DEBUG 274+ DEBUG_REASSEMBLY("packet is not in stream context: SYN %u, RST %u, ACK %u\n", 275+ this_tcphdr->th_flags & TH_SYN, 276+ this_tcphdr->th_flags & TH_RST, 277+ this_tcphdr->th_flags & TH_ACK 278+ ); 279+#endif 280+ 281+ /* does this look like a stream we should try to resume? 282+ * the conditions for it are: 283+ * - No SYN (that's the whole point) 284+ * - No RST or FIN (no point in doing that) 285+ * - we have a resume callback to identify direction 286+ */ 287+ if ((this_tcphdr->th_flags & TH_SYN) != 0 || 288+ (this_tcphdr->th_flags & TH_RST) != 0 || 289+ (this_tcphdr->th_flags & TH_FIN) != 0) { 290+ return; 291+ } 292+ else { 293+ struct proc_node *i; 294+ for (i = tcp_resume_procs; i; i = i->next) { 295+ int resume; 296+ 297+#ifdef ENABLE_TCPREASM_DEBUG 298+ DEBUG_REASSEMBLY("trying to resume stream\n"); 299+#endif 300+ 301+ i->item(this_tcphdr, this_iphdr, &resume); 302+ from_client = (resume == NIDS_TCP_RESUME_CLIENT); 303+ a_tcp = initiate_tcp_resume(this_tcphdr, this_iphdr, resume); 304+ 305+#ifdef ENABLE_TCPREASM_DEBUG 306+ DEBUG_REASSEMBLY("a_tcp = %p, from_client = %u, resume = %u\n", 307+ a_tcp, from_client, resume); 308+#endif 309+ 310+ if (a_tcp) { 311+ resumed_tcp = 1; 312+ break; 313+ } 314+ } 315+ } 316+ 317+ if (!resumed_tcp) 318+ return; 319+ } 320+#else 321 return; 322 } 323+#endif 324+ 325 if (from_client) { 326 snd = &a_tcp->client; 327 rcv = &a_tcp->server; 328@@ -762,6 +934,38 @@ process_tcp(u_char * data, int skblen) 329 rcv = &a_tcp->client; 330 snd = &a_tcp->server; 331 } 332+ 333+#ifdef ENABLE_TCPREASM 334+#ifdef ENABLE_TCPREASM_DEBUG 335+ DEBUG_REASSEMBLY("processing packet: seq = %u, ack = %u, snd->seq = %u, rcv->ack_seq = %u\n", 336+ ntohl(this_tcphdr->th_seq), 337+ ntohl(this_tcphdr->th_ack), 338+ snd->seq, rcv->ack_seq); 339+#endif 340+ 341+ /* are we the 2nd half of the resume? */ 342+ if (snd->resume_second_half) { 343+ snd->seq = ntohl(this_tcphdr->th_seq) + 1; 344+ snd->first_data_seq = snd->seq - 1; 345+ snd->window = ntohs(this_tcphdr->th_win); 346+ snd->resume_second_half = 0; 347+ snd->ack_seq = rcv->seq; 348+ 349+#ifdef ENABLE_TCPREASM_DEBUG 350+ DEBUG_REASSEMBLY("second half resumed, seq = %u, first = %u, ack = %u\n", 351+ snd->seq, snd->first_data_seq, snd->ack_seq); 352+#endif 353+ 354+ } 355+ 356+ if (resumed_tcp) { 357+ snd->state = TCP_ESTABLISHED; 358+ a_tcp->nids_state = NIDS_RESUME; 359+ goto do_lurkers; 360+ } 361+#endif 362+ 363+ /* normal SYN+ACK processing */ 364 if ((this_tcphdr->th_flags & TH_SYN)) { 365 if (from_client || a_tcp->client.state != TCP_SYN_SENT || 366 a_tcp->server.state != TCP_CLOSE || !(this_tcphdr->th_flags & TH_ACK)) 367@@ -797,8 +1001,15 @@ process_tcp(u_char * data, int skblen) 368 ( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) || 369 before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq) 370 ) 371- ) 372+ ) { 373+#ifdef ENABLE_TCPREASM_DEBUG 374+ DEBUG_REASSEMBLY("packet is ignored: " 375+ "datalen=%u, seq=%u, rcv->ack_seq=%u, rcv->window=%u, rcv->wscale=%u\n", 376+ datalen, ntohl(this_tcphdr->th_seq), 377+ rcv->ack_seq, rcv->window, rcv->wscale); 378+#endif 379 return; 380+ } 381 382 if ((this_tcphdr->th_flags & TH_RST)) { 383 if (a_tcp->nids_state == NIDS_DATA) { 384@@ -830,13 +1041,24 @@ process_tcp(u_char * data, int skblen) 385 386 a_tcp->server.state = TCP_ESTABLISHED; 387 a_tcp->nids_state = NIDS_JUST_EST; 388+#ifdef ENABLE_TCPREASM 389+do_lurkers: 390+#ifdef ENABLE_TCPREASM_DEBUG 391+ DEBUG_REASSEMBLY("notifying lurkers of new stream\n"); 392+#endif 393+#endif 394+ 395 for (i = tcp_procs; i; i = i->next) { 396 char whatto = 0; 397 char cc = a_tcp->client.collect; 398 char sc = a_tcp->server.collect; 399 char ccu = a_tcp->client.collect_urg; 400 char scu = a_tcp->server.collect_urg; 401- 402+ 403+#ifdef ENABLE_TCPREASM_DEBUG 404+ DEBUG_REASSEMBLY(" %p, nids_state = %u\n", i, a_tcp->nids_state); 405+#endif 406+ 407 (i->item) (a_tcp, &data); 408 if (cc < a_tcp->client.collect) 409 whatto |= COLLECT_cc; 410@@ -866,6 +1088,10 @@ process_tcp(u_char * data, int skblen) 411 } 412 } 413 if (!a_tcp->listeners) { 414+#ifdef ENABLE_TCPREASM_DEBUG 415+ DEBUG_REASSEMBLY("no listeners, killing stream\n"); 416+#endif 417+ 418 nids_free_tcp_stream(a_tcp); 419 return; 420 } 421@@ -890,9 +1116,16 @@ process_tcp(u_char * data, int skblen) 422 } 423 } 424 if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0) 425+ { 426+#ifdef ENABLE_TCPREASM_DEBUG 427+ DEBUG_REASSEMBLY("calling tcp_queue, datalen = %u, data = %.*s...\n", 428+ datalen, datalen > 10 ? 10 : datalen, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off); 429+#endif 430+ 431 tcp_queue(a_tcp, this_tcphdr, snd, rcv, 432 (char *) (this_tcphdr) + 4 * this_tcphdr->th_off, 433 datalen, skblen); 434+ } 435 snd->window = ntohs(this_tcphdr->th_win); 436 if (rcv->rmem_alloc > 65535) 437 prune_queue(rcv, this_tcphdr); 438@@ -919,6 +1152,20 @@ nids_unregister_tcp(void (*x)) 439 unregister_callback(&tcp_procs, x); 440 } 441 442+#ifdef ENABLE_TCPREASM 443+void 444+nids_register_tcp_resume(void (*x)) 445+{ 446+ register_callback(&tcp_resume_procs, x); 447+} 448+ 449+void 450+nids_unregister_tcp_resume(void (*x)) 451+{ 452+ unregister_callback(&tcp_resume_procs, x); 453+} 454+#endif 455+ 456 int 457 tcp_init(int size) 458 { 459